"""Service for managing contributor identities across integrations."""

import logging
from typing import Any

from django.db import transaction

from accounts.models import Workspace
from members.models import Identity, Member

logger = logging.getLogger(__name__)


# LinkedIn URN patterns
LINKEDIN_PERSON_URN_PREFIX = "urn:li:person:"


class ContributorIdentityService:
    """Service for creating and linking contributor identities across providers."""

    def __init__(self, workspace: Workspace):
        self.workspace = workspace
        # Cache of github_id -> Member for efficient lookup
        self._github_member_cache: dict[int, Member] = {}
        # Cache of linkedin_urn -> Member for efficient lookup
        self._linkedin_member_cache: dict[str, Member] = {}
        self._load_existing_identities()

    def _load_existing_identities(self) -> None:
        """Load existing GitHub and LinkedIn identities for this workspace into cache."""
        identities = Identity.objects.filter(
            provider__in=["github", "linkedin"],
            member__workspace=self.workspace,
        ).select_related("member")

        for identity in identities:
            if identity.provider == "github":
                github_id = int(identity.external_id)
                self._github_member_cache[github_id] = identity.member
            elif identity.provider == "linkedin":
                self._linkedin_member_cache[identity.external_id] = identity.member

    def get_or_create_member_from_github_user(self, github_user: dict[str, Any]) -> Member:
        """Get or create a Member from GitHub user data.

        Args:
            github_user: Dictionary with GitHub user data containing at minimum:
                - id: GitHub user ID
                - login: GitHub username
                - avatar_url: (optional) Avatar URL

        Returns:
            The Member object for this GitHub user
        """
        github_id = github_user["id"]
        github_login = github_user["login"]
        avatar_url = github_user.get("avatar_url", "")

        # Check cache first
        if github_id in self._github_member_cache:
            member = self._github_member_cache[github_id]
            # Update avatar if changed
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member

        # Check database for existing identity
        try:
            identity = Identity.objects.select_related("member").get(
                provider="github",
                external_id=str(github_id),
                member__workspace=self.workspace,
            )
            member = identity.member
            self._github_member_cache[github_id] = member
            # Update avatar if changed
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member
        except Identity.DoesNotExist:
            pass

        # Check if identity exists in any workspace (unique constraint is global)
        existing_identity = (
            Identity.objects.filter(
                provider="github",
                external_id=str(github_id),
            )
            .select_related("member")
            .first()
        )

        if existing_identity:
            # Identity exists - use its member
            # Note: This means a GitHub contributor has ONE member globally
            # TODO: Consider per-workspace members if needed (requires schema change)
            member = existing_identity.member
            self._github_member_cache[github_id] = member
            # Update avatar if changed
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member

        # Create new member and identity atomically
        with transaction.atomic():
            member = Member.objects.create(
                workspace=self.workspace,
                display_name=github_login,
                avatar_url=avatar_url,
            )

            Identity.objects.create(
                member=member,
                provider="github",
                external_id=str(github_id),
                handle=github_login,
            )

            self._github_member_cache[github_id] = member
            logger.info(
                f"Created new member {member.display_name} (GitHub: {github_login}) "
                f"in workspace {self.workspace.name}"
            )

            return member

    def link_thread_author(self, thread, github_author: dict[str, Any]) -> None:
        """Link a thread to its GitHub author.

        Updates the thread's metadata to include author member ID.

        Args:
            thread: The Thread object to update
            github_author: GitHub user data for the author
        """
        if not github_author or "id" not in github_author:
            return

        member = self.get_or_create_member_from_github_user(github_author)

        # Update thread metadata with author member ID
        if thread.metadata is None:
            thread.metadata = {}

        if "github" not in thread.metadata:
            thread.metadata["github"] = {}

        thread.metadata["github"]["author_member_id"] = str(member.pk)
        thread.save(update_fields=["metadata", "updated_at"])

    def link_message_author(self, message, github_author: dict[str, Any]) -> None:
        """Link a message to its GitHub author.

        Sets the message's author field to the Member.

        Args:
            message: The Message object to update
            github_author: GitHub user data for the author
        """
        if not github_author or "id" not in github_author:
            return

        member = self.get_or_create_member_from_github_user(github_author)
        message.author = member
        message.save(update_fields=["author", "updated_at"])

    # =========================================================================
    # LinkedIn Identity Methods
    # =========================================================================

    def get_or_create_member_from_linkedin_person(
        self,
        *,
        person_urn: str,
        display_name: str,
        avatar_url: str | None = None,
        profile_url: str | None = None,
        headline: str | None = None,
    ) -> Member:
        """Get or create a Member from LinkedIn person data.

        Args:
            person_urn: LinkedIn person URN (urn:li:person:ABC123).
            display_name: Person's display name.
            avatar_url: Optional avatar URL.
            profile_url: Optional LinkedIn profile URL.
            headline: Optional LinkedIn headline.

        Returns:
            The Member object for this LinkedIn person.
        """
        # Check cache first
        if person_urn in self._linkedin_member_cache:
            member = self._linkedin_member_cache[person_urn]
            # Update avatar if changed
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member

        # Check database for existing identity in this workspace
        try:
            identity = Identity.objects.select_related("member").get(
                provider="linkedin",
                external_id=person_urn,
                member__workspace=self.workspace,
            )
            member = identity.member
            self._linkedin_member_cache[person_urn] = member
            # Update avatar if changed
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member
        except Identity.DoesNotExist:
            pass

        # Check if identity exists in any workspace (unique constraint is global)
        existing_identity = (
            Identity.objects.filter(
                provider="linkedin",
                external_id=person_urn,
            )
            .select_related("member")
            .first()
        )

        if existing_identity:
            # Identity exists - use its member
            member = existing_identity.member
            self._linkedin_member_cache[person_urn] = member
            if avatar_url and member.avatar_url != avatar_url:
                member.avatar_url = avatar_url
                member.save(update_fields=["avatar_url", "updated_at"])
            return member

        # Create new member and identity atomically
        with transaction.atomic():
            member = Member.objects.create(
                workspace=self.workspace,
                display_name=display_name,
                avatar_url=avatar_url or "",
            )

            # Build handle from profile URL or URN
            handle = person_urn
            if profile_url:
                # Extract vanity name from profile URL if available
                if "/in/" in profile_url:
                    handle = profile_url.split("/in/")[-1].rstrip("/")

            Identity.objects.create(
                member=member,
                provider="linkedin",
                external_id=person_urn,
                handle=handle,
            )

            self._linkedin_member_cache[person_urn] = member
            logger.info(
                f"Created new member {member.display_name} (LinkedIn: {person_urn}) "
                f"in workspace {self.workspace.name}"
            )

            return member

    def link_thread_linkedin_author(
        self,
        thread,
        *,
        person_urn: str,
        display_name: str,
        avatar_url: str | None = None,
        profile_url: str | None = None,
    ) -> None:
        """Link a thread to its LinkedIn author.

        Args:
            thread: The Thread object to update.
            person_urn: LinkedIn person URN.
            display_name: Author's display name.
            avatar_url: Optional avatar URL.
            profile_url: Optional profile URL.
        """
        if not person_urn:
            return

        member = self.get_or_create_member_from_linkedin_person(
            person_urn=person_urn,
            display_name=display_name,
            avatar_url=avatar_url,
            profile_url=profile_url,
        )

        # Update thread metadata with author member ID
        if thread.metadata is None:
            thread.metadata = {}

        if "linkedin" not in thread.metadata:
            thread.metadata["linkedin"] = {}

        thread.metadata["linkedin"]["author_member_id"] = str(member.pk)
        thread.save(update_fields=["metadata", "updated_at"])
