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

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

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

from .models import Source


@strawberry.type
class SourceType:
    """GraphQL type for Source model."""

    id: ID
    name: str
    kind: SourceKind
    external_id: str
    metadata: strawberry.scalars.JSON
    created_at: str
    updated_at: str
    workspace_id: ID

    @classmethod
    def from_model(cls, source: Source) -> "SourceType":
        return cls(
            id=ID(str(source.pk)),
            name=source.name,
            kind=SourceKind(source.kind),
            external_id=source.external_id,
            metadata=source.metadata or {},
            created_at=source.created_at.isoformat() if source.created_at else "",
            updated_at=source.updated_at.isoformat() if source.updated_at else "",
            workspace_id=ID(str(source.workspace_id)),
        )


# Input types for mutations
@strawberry.input
class CreateSourceInput:
    workspace_id: ID
    name: str
    kind: SourceKind
    external_id: str
    metadata: strawberry.scalars.JSON | None = None


@strawberry.input
class UpdateSourceInput:
    name: str | None = None
    metadata: strawberry.scalars.JSON | 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 SourcesQuery:
    """Queries for sources app."""

    @strawberry.field
    def sources(self, info: Info, workspace_id: ID) -> list[SourceType]:
        """Get all sources 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 []

        sources = Source.objects.filter(workspace_id=workspace_id)
        return [SourceType.from_model(s) for s in sources]

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

        try:
            source = Source.objects.get(pk=id)
        except Source.DoesNotExist:
            return None

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

        return SourceType.from_model(source)


@strawberry.type
class SourcesMutation:
    """Mutations for sources app."""

    @strawberry.mutation
    def create_source(self, info: Info, input: CreateSourceInput) -> SourceType:
        """Create a new source."""
        workspace = check_workspace_access(info.context.user, input.workspace_id, ["owner", "admin"])

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

        source = Source.objects.create(
            workspace=workspace,
            name=input.name,
            kind=input.kind.value,
            external_id=input.external_id,
            metadata=input.metadata or {},
        )
        return SourceType.from_model(source)

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

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

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

        if input.name is not None:
            source.name = input.name
        if input.metadata is not None:
            source.metadata = input.metadata

        source.save()
        return SourceType.from_model(source)

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

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

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

        source.delete()
        return True
