from django import forms
from django.contrib import admin

from apps.core.permissions.roles import UserRole
from apps.repository.documents.models import (
    AccessRequest,
    Author,
    AuthorAlias,
    EmailIngestEvent,
    EmailIngestMailbox,
    Collection,
    CollectionGroupAccess,
    CollectionSlugAlias,
    Document,
    DocumentAuthor,
    DocumentComment,
    DocumentReviewTask,
    DocumentShare,
    DocumentVersion,
    IntegrationOutbox,
    Keyword,
    MetadataSchema,
    QARecord,
    Region,
    SavedSearch,
)


class DocumentVersionInline(admin.TabularInline):
    model = DocumentVersion
    extra = 0
    readonly_fields = ("file_hash", "uploaded_at")


class DocumentAuthorInline(admin.TabularInline):
    model = DocumentAuthor
    extra = 0
    autocomplete_fields = ("author",)


class QARecordInline(admin.TabularInline):
    model = QARecord
    extra = 0
    readonly_fields = ("assigned_at", "decided_at")


@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
    list_display = (
        "public_document_id",
        "title",
        "collection",
        "document_type",
        "status",
        "published_at",
    )
    list_filter = ("status", "document_type", "license_type", "collection")
    search_fields = ("title", "public_document_id", "doi", "grant_reference")
    readonly_fields = (
        "document_uid",
        "public_document_id",
        "status",
        "search_vector",
        "file_hash",
        "file_size_bytes",
        "extracted_text",
        "ai_metadata",
        "ai_summary",
        "submitted_at",
        "published_at",
        "withdrawn_at",
        "created_at",
        "updated_at",
    )
    autocomplete_fields = ("collection", "submitted_by")
    inlines = [DocumentAuthorInline, DocumentVersionInline, QARecordInline]


class CollectionAdminForm(forms.ModelForm):
    """FRREP-DCI014 — role-based upload notifications alongside the named-user
    ``upload_notification_recipients`` M2M."""

    notify_roles = forms.MultipleChoiceField(
        choices=UserRole.choices,
        required=False,
        widget=forms.SelectMultiple(attrs={"size": "10"}),
        help_text=(
            "Every active user holding one of these roles is notified when a "
            "document is ingested into this collection, in addition to the "
            "named recipients below."
        ),
    )

    class Meta:
        model = Collection
        fields = "__all__"


@admin.register(Collection)
class CollectionAdmin(admin.ModelAdmin):
    form = CollectionAdminForm
    list_display = ("name", "slug", "visibility", "qa_workflow_enabled", "qa_turnaround_days")
    list_filter = ("visibility", "qa_workflow_enabled")
    prepopulated_fields = {"slug": ("name",)}
    search_fields = ("name", "slug")
    autocomplete_fields = ("parent",)
    filter_horizontal = ("managed_by", "upload_notification_recipients")


@admin.register(Keyword)
class KeywordAdmin(admin.ModelAdmin):
    list_display = ("label", "slug", "kind", "status", "suggested_by", "reviewed_at")
    list_filter = ("status", "kind")
    search_fields = ("label", "slug", "agrovoc_uri")
    autocomplete_fields = ("suggested_by", "reviewed_by")


@admin.register(Region)
class RegionAdmin(admin.ModelAdmin):
    list_display = ("name", "slug", "display_order")
    search_fields = ("name", "slug")
    prepopulated_fields = {"slug": ("name",)}


class MetadataSchemaAdminForm(forms.ModelForm):
    """FRREP-MCL006 — validate the raw JSON textareas so a typo in
    ``auto_classification_rules`` (or the other JSON fields) surfaces as a
    clear admin-form error instead of failing silently the next time a
    document is ingested."""

    class Meta:
        model = MetadataSchema
        fields = "__all__"

    def clean_mandatory_fields(self):
        value = self.cleaned_data.get("mandatory_fields")
        if not value:
            return []
        if not isinstance(value, list) or not all(isinstance(v, str) for v in value):
            raise forms.ValidationError(
                "mandatory_fields must be a JSON list of field-code strings, "
                'e.g. ["title", "abstract", "license_type"].'
            )
        return value

    def clean_controlled_vocabularies(self):
        value = self.cleaned_data.get("controlled_vocabularies")
        if not value:
            return {}
        if not isinstance(value, dict):
            raise forms.ValidationError(
                "controlled_vocabularies must be a JSON object mapping field "
                'code → list of allowed values, e.g. {"language": ["en", "fr"]}.'
            )
        for field_code, allowed in value.items():
            if not isinstance(allowed, list):
                raise forms.ValidationError(
                    f"'{field_code}' must map to a JSON list of allowed values."
                )
        return value

    def clean_auto_classification_rules(self):
        value = self.cleaned_data.get("auto_classification_rules")
        if not value:
            return []
        if not isinstance(value, list):
            raise forms.ValidationError(
                "auto_classification_rules must be a JSON list of rule objects, "
                'e.g. [{"if_keyword": "climate", "then_collection_slug": "climate-research"}].'
            )
        allowed_keys = {"if_keyword", "then_collection_slug"}
        for idx, rule in enumerate(value, start=1):
            if not isinstance(rule, dict):
                raise forms.ValidationError(
                    f"Rule #{idx} must be a JSON object with 'if_keyword' and "
                    "'then_collection_slug' keys."
                )
            if not str(rule.get("if_keyword") or "").strip():
                raise forms.ValidationError(
                    f"Rule #{idx} is missing a non-empty 'if_keyword' string."
                )
            if not str(rule.get("then_collection_slug") or "").strip():
                raise forms.ValidationError(
                    f"Rule #{idx} is missing a non-empty 'then_collection_slug' string."
                )
            unknown = set(rule.keys()) - allowed_keys
            if unknown:
                raise forms.ValidationError(
                    f"Rule #{idx} has unknown key(s): {', '.join(sorted(unknown))}. "
                    "Allowed keys: if_keyword, then_collection_slug."
                )
        return value


@admin.register(MetadataSchema)
class MetadataSchemaAdmin(admin.ModelAdmin):
    form = MetadataSchemaAdminForm
    list_display = ("name",)
    search_fields = ("name",)


class AuthorAliasInline(admin.TabularInline):
    model = AuthorAlias
    extra = 0
    fields = ("full_name", "source", "note", "created_at")
    readonly_fields = ("created_at",)


@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ("last_name", "first_name", "orcid", "institution", "is_archived", "merged_into")
    list_filter = ("is_archived",)
    search_fields = ("first_name", "last_name", "orcid", "email", "aliases__full_name")
    autocomplete_fields = ("institution", "merged_into")
    inlines = [AuthorAliasInline]

    def get_queryset(self, request):
        # Admin needs to see archived rows too — bypass the default manager.
        return Author.all_objects.get_queryset()


@admin.register(AuthorAlias)
class AuthorAliasAdmin(admin.ModelAdmin):
    list_display = ("full_name", "author", "source", "created_at")
    list_filter = ("source",)
    search_fields = ("full_name", "author__first_name", "author__last_name")
    autocomplete_fields = ("author",)


@admin.register(QARecord)
class QARecordAdmin(admin.ModelAdmin):
    list_display = ("document", "reviewer", "decision", "assigned_at", "decided_at")
    list_filter = ("decision",)
    autocomplete_fields = ("document", "reviewer")


@admin.register(AccessRequest)
class AccessRequestAdmin(admin.ModelAdmin):
    list_display = ("document", "requester", "status", "created_at")
    list_filter = ("status",)
    autocomplete_fields = ("document", "requester", "decided_by")


@admin.register(DocumentShare)
class DocumentShareAdmin(admin.ModelAdmin):
    list_display = ("document", "collaborator", "permission_level", "granted_at")
    autocomplete_fields = ("document", "collaborator", "granted_by")


@admin.register(DocumentComment)
class DocumentCommentAdmin(admin.ModelAdmin):
    list_display = ("document", "author", "created_at", "is_resolved")
    search_fields = ("text", "author__email", "document__public_document_id")
    autocomplete_fields = ("document", "author", "parent")


@admin.register(DocumentReviewTask)
class DocumentReviewTaskAdmin(admin.ModelAdmin):
    list_display = ("document", "assignee", "status", "due_date")
    list_filter = ("status",)
    autocomplete_fields = ("document", "assignee", "created_by")


@admin.register(SavedSearch)
class SavedSearchAdmin(admin.ModelAdmin):
    list_display = ("user", "name", "alert_enabled", "created_at")
    autocomplete_fields = ("user",)


@admin.register(IntegrationOutbox)
class IntegrationOutboxAdmin(admin.ModelAdmin):
    list_display = ("target", "event_type", "status", "retry_count", "created_at", "sent_at")
    list_filter = ("target", "status")
    readonly_fields = ("payload", "error", "created_at", "sent_at")


@admin.register(CollectionGroupAccess)
class CollectionGroupAccessAdmin(admin.ModelAdmin):
    list_display = (
        "collection",
        "group",
        "permission_level",
        "granted_at",
        "expires_at",
    )
    list_filter = ("permission_level",)
    autocomplete_fields = ("collection", "group", "granted_by")
    readonly_fields = ("granted_at",)
    search_fields = ("collection__name", "group__name", "note")


@admin.register(CollectionSlugAlias)
class CollectionSlugAliasAdmin(admin.ModelAdmin):
    list_display = ("old_slug", "collection", "created_at")
    autocomplete_fields = ("collection",)
    readonly_fields = ("created_at",)
    search_fields = ("old_slug", "collection__name")


@admin.register(EmailIngestMailbox)
class EmailIngestMailboxAdmin(admin.ModelAdmin):
    list_display = ("name", "imap_host", "username", "default_collection",
                    "is_enabled", "last_polled_at")
    list_filter = ("is_enabled", "use_ssl")
    search_fields = ("name", "imap_host", "username")
    autocomplete_fields = ("default_collection", "submitter")
    readonly_fields = ("password_signed", "last_polled_at", "last_error",
                       "created_at", "updated_at")
    fieldsets = (
        (None, {"fields": ("name", "is_enabled", "default_collection", "submitter",
                            "default_license", "parsing_rules")}),
        ("IMAP connection", {"fields": ("imap_host", "imap_port", "use_ssl",
                                         "username", "password_signed")}),
        ("Folders", {"fields": ("inbox_folder", "processed_folder", "failed_folder")}),
        ("Telemetry", {"fields": ("last_polled_at", "last_error", "created_at", "updated_at")}),
    )


@admin.register(EmailIngestEvent)
class EmailIngestEventAdmin(admin.ModelAdmin):
    list_display = ("processed_at", "mailbox", "sender", "subject", "status", "document")
    list_filter = ("status", "mailbox")
    search_fields = ("message_uid", "sender", "subject")
    readonly_fields = ("mailbox", "message_uid", "subject", "sender", "processed_at",
                       "status", "error_message", "document")
    date_hierarchy = "processed_at"

    def has_add_permission(self, request):
        return False  # events are system-generated only
