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

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

from accounts.models import Workspace, WorkspaceMembership
from core.graphql.enums import ThreadStatus
from members.models import Member

from .models import Message, Tag, Thread


@strawberry.type
class TagType:
    """GraphQL type for Tag model."""

    id: ID
    name: str
    created_at: str
    updated_at: str
    workspace_id: ID

    @classmethod
    def from_model(cls, tag: Tag) -> "TagType":
        return cls(
            id=ID(str(tag.pk)),
            name=tag.name,
            created_at=tag.created_at.isoformat() if tag.created_at else "",
            updated_at=tag.updated_at.isoformat() if tag.updated_at else "",
            workspace_id=ID(str(tag.workspace_id)),
        )


@strawberry.type
class MessageType:
    """GraphQL type for Message model."""

    id: ID
    content: str
    external_id: str
    sent_at: str
    created_at: str
    updated_at: str
    thread_id: ID
    author_id: ID | None

    @classmethod
    def from_model(cls, message: Message) -> "MessageType":
        return cls(
            id=ID(str(message.pk)),
            content=message.content,
            external_id=message.external_id or "",
            sent_at=message.sent_at.isoformat() if message.sent_at else "",
            created_at=message.created_at.isoformat() if message.created_at else "",
            updated_at=message.updated_at.isoformat() if message.updated_at else "",
            thread_id=ID(str(message.thread_id)),
            author_id=ID(str(message.author_id)) if message.author_id else None,
        )


@strawberry.type
class ThreadType:
    """GraphQL type for Thread model."""

    id: ID
    title: str
    external_id: str
    status: ThreadStatus
    created_at: str
    updated_at: str
    workspace_id: ID
    source_id: ID

    @strawberry.field
    def messages(self, info: Info) -> list[MessageType]:
        """Get all messages in this thread."""
        thread = Thread.objects.get(pk=self.id)
        return [MessageType.from_model(m) for m in thread.messages.order_by("sent_at")]

    @strawberry.field
    def tags(self, info: Info) -> list[TagType]:
        """Get all tags for this thread."""
        thread = Thread.objects.get(pk=self.id)
        return [TagType.from_model(t) for t in thread.tags.all()]

    @classmethod
    def from_model(cls, thread: Thread) -> "ThreadType":
        return cls(
            id=ID(str(thread.pk)),
            title=thread.title,
            external_id=thread.external_id,
            status=ThreadStatus(thread.status),
            created_at=thread.created_at.isoformat() if thread.created_at else "",
            updated_at=thread.updated_at.isoformat() if thread.updated_at else "",
            workspace_id=ID(str(thread.workspace_id)),
            source_id=ID(str(thread.source_id)),
        )


# Input types for mutations
@strawberry.input
class CreateTagInput:
    workspace_id: ID
    name: str


@strawberry.input
class UpdateThreadInput:
    status: ThreadStatus | None = None
    title: str | None = None
    tag_ids: list[ID] | None = None


@strawberry.input
class CreateMessageInput:
    thread_id: ID
    content: str
    author_id: ID | None = None


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 MessagesQuery:
    """Queries for messages app."""

    @strawberry.field
    def threads(
        self,
        info: Info,
        workspace_id: ID,
        status: ThreadStatus | None = None,
        source_id: ID | None = None,
    ) -> list[ThreadType]:
        """Get all threads in a workspace."""
        user = info.context.user
        if not user or not user.is_authenticated:
            return []

        has_access = WorkspaceMembership.objects.filter(user=user, workspace_id=workspace_id).exists()
        if not has_access:
            return []

        threads = Thread.objects.filter(workspace_id=workspace_id)

        if status:
            threads = threads.filter(status=status.value)
        if source_id:
            threads = threads.filter(source_id=source_id)

        return [ThreadType.from_model(t) for t in threads.order_by("-created_at")]

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

        try:
            thread = Thread.objects.get(pk=id)
        except Thread.DoesNotExist:
            return None

        has_access = WorkspaceMembership.objects.filter(user=user, workspace=thread.workspace).exists()
        if not has_access:
            return None

        return ThreadType.from_model(thread)

    @strawberry.field
    def messages(self, info: Info, thread_id: ID) -> list[MessageType]:
        """Get all messages in a thread."""
        user = info.context.user
        if not user or not user.is_authenticated:
            return []

        try:
            thread = Thread.objects.get(pk=thread_id)
        except Thread.DoesNotExist:
            return []

        has_access = WorkspaceMembership.objects.filter(user=user, workspace=thread.workspace).exists()
        if not has_access:
            return []

        return [MessageType.from_model(m) for m in thread.messages.order_by("sent_at")]

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

        has_access = WorkspaceMembership.objects.filter(user=user, workspace_id=workspace_id).exists()
        if not has_access:
            return []

        tags = Tag.objects.filter(workspace_id=workspace_id)
        return [TagType.from_model(t) for t in tags]


@strawberry.type
class MessagesMutation:
    """Mutations for messages app."""

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

        if Tag.objects.filter(workspace=workspace, name=input.name).exists():
            raise Exception("Tag with this name already exists in this workspace") from None

        tag = Tag.objects.create(workspace=workspace, name=input.name)
        return TagType.from_model(tag)

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

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

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

        tag.delete()
        return True

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

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

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

        if input.status is not None:
            thread.status = input.status.value
        if input.title is not None:
            thread.title = input.title

        thread.save()

        if input.tag_ids is not None:
            thread.tags.clear()
            for tag_id in input.tag_ids:
                try:
                    tag = Tag.objects.get(pk=tag_id, workspace=thread.workspace)
                    thread.tags.add(tag)
                except Tag.DoesNotExist:
                    pass

        return ThreadType.from_model(thread)

    @strawberry.mutation
    def create_message(self, info: Info, input: CreateMessageInput) -> MessageType:
        """Create a new message in a thread."""
        user = info.context.user
        if not user or not user.is_authenticated:
            raise Exception("Authentication required") from None

        try:
            thread = Thread.objects.get(pk=input.thread_id)
        except Thread.DoesNotExist:
            raise Exception("Thread not found") from None

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

        from django.utils import timezone

        author = None
        if input.author_id:
            try:
                author = Member.objects.get(pk=input.author_id)
            except Member.DoesNotExist:
                pass

        message = Message.objects.create(
            thread=thread,
            content=input.content,
            author=author,
            sent_at=timezone.now(),
        )
        return MessageType.from_model(message)
