"""GraphQL types, queries, and mutations for members app."""

import strawberry
from strawberry import ID
from strawberry.types import Info

from accounts.models import Workspace, WorkspaceMembership
from core.graphql.enums import IdentityProvider

from .models import Identity, Member


@strawberry.type
class IdentityType:
    """GraphQL type for Identity model."""

    id: ID
    provider: IdentityProvider
    external_id: str
    handle: str
    created_at: str
    updated_at: str
    member_id: ID

    @classmethod
    def from_model(cls, identity: Identity) -> "IdentityType":
        return cls(
            id=ID(str(identity.pk)),
            provider=IdentityProvider(identity.provider),
            external_id=identity.external_id,
            handle=identity.handle,
            created_at=identity.created_at.isoformat() if identity.created_at else "",
            updated_at=identity.updated_at.isoformat() if identity.updated_at else "",
            member_id=ID(str(identity.member_id)),
        )


@strawberry.type
class MemberType:
    """GraphQL type for Member model."""

    id: ID
    display_name: str
    email: str
    created_at: str
    updated_at: str
    workspace_id: ID

    @strawberry.field
    def identities(self, info: Info) -> list[IdentityType]:
        """Get all identities for this member."""
        member = Member.objects.get(pk=self.id)
        return [IdentityType.from_model(i) for i in member.identities.all()]

    @classmethod
    def from_model(cls, member: Member) -> "MemberType":
        return cls(
            id=ID(str(member.pk)),
            display_name=member.display_name,
            email=member.email or "",
            created_at=member.created_at.isoformat() if member.created_at else "",
            updated_at=member.updated_at.isoformat() if member.updated_at else "",
            workspace_id=ID(str(member.workspace_id)),
        )


# Input types for mutations
@strawberry.input
class CreateMemberInput:
    workspace_id: ID
    display_name: str
    email: str | None = None


@strawberry.input
class UpdateMemberInput:
    display_name: str | None = None
    email: str | None = None


@strawberry.input
class CreateIdentityInput:
    member_id: ID
    provider: IdentityProvider
    external_id: str
    handle: str


def check_workspace_access(user, workspace_id: ID, roles: list[str] | None = None) -> Workspace:
    """Check if user has access to workspace and return the workspace."""
    if not user or not user.is_authenticated:
        raise Exception("Authentication required") from None

    try:
        workspace = Workspace.objects.get(pk=workspace_id)
    except Workspace.DoesNotExist:
        raise Exception("Workspace not found") from None

    membership_filter = {"user": user, "workspace": workspace}
    if roles:
        membership_filter["role__in"] = roles

    if not WorkspaceMembership.objects.filter(**membership_filter).exists():
        raise Exception("Permission denied") from None

    return workspace


@strawberry.type
class MembersQuery:
    """Queries for members app."""

    @strawberry.field
    def members(self, info: Info, workspace_id: ID) -> list[MemberType]:
        """Get all members in a workspace."""
        user = info.context.user
        if not user or not user.is_authenticated:
            return []

        # Check user has access to workspace
        has_access = WorkspaceMembership.objects.filter(user=user, workspace_id=workspace_id).exists()
        if not has_access:
            return []

        members = Member.objects.filter(workspace_id=workspace_id)
        return [MemberType.from_model(m) for m in members]

    @strawberry.field
    def member(self, info: Info, id: ID) -> MemberType | None:
        """Get a specific member by ID."""
        user = info.context.user
        if not user or not user.is_authenticated:
            return None

        try:
            member = Member.objects.get(pk=id)
        except Member.DoesNotExist:
            return None

        # Check user has access to member's workspace
        has_access = WorkspaceMembership.objects.filter(user=user, workspace=member.workspace).exists()
        if not has_access:
            return None

        return MemberType.from_model(member)

    @strawberry.field
    def identities(self, info: Info, member_id: ID) -> list[IdentityType]:
        """Get all identities for a member."""
        user = info.context.user
        if not user or not user.is_authenticated:
            return []

        try:
            member = Member.objects.get(pk=member_id)
        except Member.DoesNotExist:
            return []

        # Check user has access to member's workspace
        has_access = WorkspaceMembership.objects.filter(user=user, workspace=member.workspace).exists()
        if not has_access:
            return []

        return [IdentityType.from_model(i) for i in member.identities.all()]


@strawberry.type
class MembersMutation:
    """Mutations for members app."""

    @strawberry.mutation
    def create_member(self, info: Info, input: CreateMemberInput) -> MemberType:
        """Create a new member."""
        workspace = check_workspace_access(info.context.user, input.workspace_id, ["owner", "admin", "member"])

        member = Member.objects.create(
            workspace=workspace,
            display_name=input.display_name,
            email=input.email or "",
        )
        return MemberType.from_model(member)

    @strawberry.mutation
    def update_member(self, info: Info, id: ID, input: UpdateMemberInput) -> MemberType:
        """Update a member."""
        user = info.context.user
        if not user or not user.is_authenticated:
            raise Exception("Authentication required") from None

        try:
            member = Member.objects.get(pk=id)
        except Member.DoesNotExist:
            raise Exception("Member not found") from None

        check_workspace_access(user, ID(str(member.workspace_id)), ["owner", "admin", "member"])

        if input.display_name is not None:
            member.display_name = input.display_name
        if input.email is not None:
            member.email = input.email

        member.save()
        return MemberType.from_model(member)

    @strawberry.mutation
    def delete_member(self, info: Info, id: ID) -> bool:
        """Delete a member."""
        user = info.context.user
        if not user or not user.is_authenticated:
            raise Exception("Authentication required") from None

        try:
            member = Member.objects.get(pk=id)
        except Member.DoesNotExist:
            raise Exception("Member not found") from None

        check_workspace_access(user, ID(str(member.workspace_id)), ["owner", "admin"])

        member.delete()
        return True

    @strawberry.mutation
    def create_identity(self, info: Info, input: CreateIdentityInput) -> IdentityType:
        """Create a new identity for a member."""
        user = info.context.user
        if not user or not user.is_authenticated:
            raise Exception("Authentication required") from None

        try:
            member = Member.objects.get(pk=input.member_id)
        except Member.DoesNotExist:
            raise Exception("Member not found") from None

        check_workspace_access(user, ID(str(member.workspace_id)), ["owner", "admin", "member"])

        if Identity.objects.filter(provider=input.provider.value, external_id=input.external_id).exists():
            raise Exception("Identity with this provider and external ID already exists") from None

        identity = Identity.objects.create(
            member=member,
            provider=input.provider.value,
            external_id=input.external_id,
            handle=input.handle,
        )
        return IdentityType.from_model(identity)

    @strawberry.mutation
    def delete_identity(self, info: Info, id: ID) -> bool:
        """Delete an identity."""
        user = info.context.user
        if not user or not user.is_authenticated:
            raise Exception("Authentication required") from None

        try:
            identity = Identity.objects.select_related("member").get(pk=id)
        except Identity.DoesNotExist:
            raise Exception("Identity not found") from None

        check_workspace_access(user, ID(str(identity.member.workspace_id)), ["owner", "admin"])

        identity.delete()
        return True
