import json
from pathlib import Path

from django.contrib import admin, messages
from django.utils.translation import gettext_lazy as _
from django_jsonform.widgets import JSONFormWidget

from servala.core.forms import ControlPlaneAdminForm, ServiceDefinitionAdminForm
from servala.core.models import (
    BillingEntity,
    CloudProvider,
    ComputePlan,
    ComputePlanAssignment,
    ControlPlane,
    ControlPlaneCRD,
    OdooObjectCache,
    Organization,
    OrganizationInvitation,
    OrganizationMembership,
    OrganizationOrigin,
    Service,
    ServiceCategory,
    ServiceDefinition,
    ServiceInstance,
    ServiceOffering,
    User,
)


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    list_display = (
        "email",
        "first_name",
        "last_name",
        "company",
        "is_staff",
        "is_superuser",
    )
    list_filter = ("is_staff", "is_superuser")
    search_fields = ("email", "first_name", "last_name", "company")
    ordering = ("email",)
    fieldsets = (
        (None, {"fields": ("email",)}),
        (_("Personal info"), {"fields": ("first_name", "last_name", "company")}),
        (_("Permissions"), {"fields": ("is_staff", "is_superuser")}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": ("email", "password1", "password2"),
            },
        ),
    )


class OrganizationMembershipInline(admin.TabularInline):
    model = OrganizationMembership
    extra = 1
    autocomplete_fields = ("user",)


@admin.register(Organization)
class OrganizationAdmin(admin.ModelAdmin):
    list_display = ("name", "namespace", "billing_entity", "origin")
    list_filter = ("origin",)
    search_fields = ("name", "namespace")
    autocomplete_fields = ("billing_entity", "origin")
    inlines = (OrganizationMembershipInline,)
    filter_horizontal = ("limit_osb_services",)

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = list(super().get_readonly_fields(request, obj) or [])
        readonly_fields.append("namespace")  # Always read-only

        if obj and obj.has_inherited_billing_entity:
            readonly_fields.append("billing_entity")

        return readonly_fields


@admin.register(BillingEntity)
class BillingEntityAdmin(admin.ModelAdmin):
    list_display = ("name", "erp_reference")
    search_fields = ("name", "erp_reference")


@admin.register(OrganizationOrigin)
class OrganizationOriginAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "billing_entity",
        "invoice_grouping",
        "default_odoo_sale_order_id",
        "hide_billing_address",
    )
    list_filter = ("hide_billing_address", "invoice_grouping")
    search_fields = ("name",)
    autocomplete_fields = ("billing_entity",)
    filter_horizontal = ("limit_cloudproviders",)
    fieldsets = (
        (
            None,
            {
                "fields": (
                    "name",
                    "description",
                )
            },
        ),
        (
            _("Billing"),
            {
                "fields": (
                    "billing_entity",
                    "default_odoo_sale_order_id",
                    "invoice_grouping",
                    "hide_billing_address",
                    "billing_message",
                )
            },
        ),
        (
            _("Restrictions"),
            {
                "fields": ("limit_cloudproviders",),
            },
        ),
    )


@admin.register(OrganizationMembership)
class OrganizationMembershipAdmin(admin.ModelAdmin):
    list_display = ("user", "organization", "role", "date_joined")
    list_filter = ("role", "date_joined", "organization")
    search_fields = ("user__email", "organization__name")
    autocomplete_fields = ("user", "organization")
    date_hierarchy = "date_joined"


@admin.register(OrganizationInvitation)
class OrganizationInvitationAdmin(admin.ModelAdmin):
    list_display = ("email", "organization", "role", "is_accepted", "created_at")
    list_filter = ("role", "created_at", "accepted_at", "organization")
    search_fields = ("email", "organization__name")
    autocomplete_fields = ("organization", "accepted_by")
    readonly_fields = (
        "secret",
        "accepted_by",
        "accepted_at",
        "created_at",
        "updated_at",
    )
    date_hierarchy = "created_at"
    actions = ["send_invitation_emails"]

    def is_accepted(self, obj):
        return obj.is_accepted

    is_accepted.boolean = True
    is_accepted.short_description = _("Accepted")

    def send_invitation_emails(self, request, queryset):
        pending_invitations = queryset.filter(accepted_by__isnull=True)
        sent_count = 0
        failed_count = 0

        for invitation in pending_invitations:
            try:
                invitation.send_invitation_email(request)
                sent_count += 1
            except Exception as e:
                failed_count += 1
                messages.error(
                    request,
                    _(f"Failed to send invitation to {invitation.email}: {str(e)}"),
                )

        if sent_count > 0:
            messages.success(
                request,
                _(f"Successfully sent {sent_count} invitation email(s)."),
            )

        if failed_count > 0:
            messages.warning(
                request, _(f"Failed to send {failed_count} invitation email(s).")
            )

    send_invitation_emails.short_description = _("Send invitation emails")


@admin.register(ServiceCategory)
class ServiceCategoryAdmin(admin.ModelAdmin):
    list_display = ("name", "parent")
    list_filter = ("parent",)
    search_fields = ("name", "description")
    autocomplete_fields = ("parent",)


@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
    list_display = ("name", "category")
    list_filter = ("category",)
    search_fields = ("name", "description")
    autocomplete_fields = ("category",)
    prepopulated_fields = {"slug": ["name"]}

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        external_links_schema = {
            "type": "array",
            "title": "External Links",
            "items": {
                "type": "object",
                "title": "Link",
                "properties": {
                    "url": {"type": "string", "format": "uri", "title": "URL"},
                    "title": {"type": "string", "title": "Title"},
                    "featured": {
                        "type": "boolean",
                        "title": "Featured",
                        "default": False,
                        "description": "Featured links will be shown on the service list page",
                    },
                },
                "required": ["url", "title"],
            },
        }
        form.base_fields["external_links"].widget = JSONFormWidget(
            schema=external_links_schema
        )
        return form


@admin.register(CloudProvider)
class CloudProviderAdmin(admin.ModelAdmin):
    list_display = ("name",)
    search_fields = ("name", "description")

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        external_links_schema = {
            "type": "array",
            "title": "External Links",
            "items": {
                "type": "object",
                "title": "Link",
                "properties": {
                    "url": {"type": "string", "format": "uri", "title": "URL"},
                    "title": {"type": "string", "title": "Title"},
                },
                "required": ["url", "title"],
            },
        }
        form.base_fields["external_links"].widget = JSONFormWidget(
            schema=external_links_schema
        )
        return form


@admin.register(ControlPlane)
class ControlPlaneAdmin(admin.ModelAdmin):
    form = ControlPlaneAdminForm
    list_display = ("name", "cloud_provider")
    list_filter = ("cloud_provider",)
    search_fields = ("name", "description")
    autocomplete_fields = ("cloud_provider",)
    actions = ["test_kubernetes_connection"]
    ordering = ("name",)

    fieldsets = (
        (
            None,
            {
                "fields": (
                    "name",
                    "description",
                    "cloud_provider",
                    "required_label",
                    "wildcard_dns",
                )
            },
        ),
        (
            _("API Credentials"),
            {
                "fields": ("certificate_authority_data", "server", "token"),
                "description": _(
                    "All three credential fields must be provided together or left empty."
                ),
            },
        ),
        (
            _("User Information"),
            {
                "fields": ("user_info",),
                "description": _(
                    "Key-value information displayed to users when selecting this control plane."
                ),
            },
        ),
        (
            _("Storage Plan"),
            {
                "fields": (
                    "storage_plan_odoo_product_id",
                    "storage_plan_odoo_unit_id",
                    "storage_plan_price_per_gib",
                ),
                "description": _(
                    "Storage plan configuration for this control plane (hardcoded per control plane)."
                ),
            },
        ),
    )

    def get_exclude(self, request, obj=None):
        # Exclude the original api_credentials field as we're using our custom fields
        return ["api_credentials"]

    def response_change(self, request, obj):
        result = super().response_change(request, obj)
        if "_save_and_test" in request.POST:
            success, message = obj.test_connection()
            if success:
                messages.success(request, message)
            else:
                messages.warning(request, message)
        return result

    def test_kubernetes_connection(self, request, queryset):
        """Admin action to test Kubernetes connection for selected control planes"""
        for control_plane in queryset:
            success, message = control_plane.test_connection()
            message = f"{control_plane.name}: {message}"

            if success:
                messages.success(request, message)
            else:
                messages.warning(request, message)

    test_kubernetes_connection.short_description = _("Test Kubernetes connection")


@admin.register(ServiceDefinition)
class ServiceDefinitionAdmin(admin.ModelAdmin):
    form = ServiceDefinitionAdminForm
    list_display = ("name", "service")
    list_filter = ("service",)
    search_fields = ("name", "description")
    autocomplete_fields = ("service",)

    fieldsets = (
        (
            None,
            {"fields": ("name", "description", "service")},
        ),
        (
            _("API Definition"),
            {
                "fields": ("api_group", "api_version", "api_kind"),
                "description": _("API definition for the Kubernetes Custom Resource"),
            },
        ),
        (
            _("Form Configuration"),
            {
                "fields": ("form_config", "hide_expert_mode"),
                "description": _(
                    "Optional custom form configuration. When provided, this will be used instead of auto-generating the form from the OpenAPI spec."
                ),
            },
        ),
    )

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        schema_path = Path(__file__).parent / "schemas" / "form_config_schema.json"
        with open(schema_path) as f:
            form_config_schema = json.load(f)

        if "form_config" in form.base_fields:
            form.base_fields["form_config"].widget = JSONFormWidget(
                schema=form_config_schema
            )
        return form

    def get_exclude(self, request, obj=None):
        # Exclude the original api_definition field as we're using our custom fields
        return ["api_definition"]


class ControlPlaneCRDInline(admin.TabularInline):
    model = ControlPlaneCRD
    extra = 1
    autocomplete_fields = ("control_plane", "service_definition")


@admin.register(ControlPlaneCRD)
class ControlPlaneCRDAdmin(admin.ModelAdmin):
    list_display = ("service_offering", "control_plane", "service_definition")
    list_filter = ("service_offering", "control_plane", "service_definition")
    search_fields = ("service_offering__service__name", "control_plane__name")
    autocomplete_fields = ("service_offering", "control_plane", "service_definition")


@admin.register(ServiceInstance)
class ServiceInstanceAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "organization",
        "context",
        "compute_plan_assignment",
        "created_by",
    )
    list_filter = ("organization", "context", "compute_plan_assignment")
    search_fields = (
        "name",
        "organization__name",
        "context__service_offering__service__name",
    )
    readonly_fields = ("name", "organization", "context")
    autocomplete_fields = ("organization", "context", "compute_plan_assignment")

    def get_readonly_fields(self, request, obj=None):
        if obj:  # If this is an edit (not a new instance)
            return self.readonly_fields
        return []

    fieldsets = (
        (
            None,
            {
                "fields": (
                    "name",
                    "organization",
                    "context",
                    "created_by",
                )
            },
        ),
        (
            _("Plan"),
            {"fields": ("compute_plan_assignment",)},
        ),
    )


@admin.register(ServiceOffering)
class ServiceOfferingAdmin(admin.ModelAdmin):
    list_display = ("id", "service", "provider")
    list_filter = ("service", "provider")
    search_fields = ("description",)
    autocomplete_fields = ("service", "provider")
    inlines = (ControlPlaneCRDInline,)

    def get_form(self, request, obj=None, **kwargs):
        form = super().get_form(request, obj, **kwargs)
        external_links_schema = {
            "type": "array",
            "title": "External Links",
            "items": {
                "type": "object",
                "title": "Link",
                "properties": {
                    "url": {"type": "string", "format": "uri", "title": "URL"},
                    "title": {"type": "string", "title": "Title"},
                },
                "required": ["url", "title"],
            },
        }
        form.base_fields["external_links"].widget = JSONFormWidget(
            schema=external_links_schema
        )
        return form


class ComputePlanAssignmentInline(admin.TabularInline):
    model = ComputePlanAssignment
    extra = 1
    autocomplete_fields = ("control_plane_crd",)
    fields = (
        "compute_plan",
        "control_plane_crd",
        "sla",
        "odoo_product_id",
        "odoo_unit_id",
        "price",
        "unit",
        "minimum_service_size",
        "sort_order",
        "is_active",
    )
    readonly_fields = ()


@admin.register(ComputePlan)
class ComputePlanAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "is_active",
        "memory_limits",
        "cpu_limits",
    )
    list_filter = ("is_active",)
    search_fields = ("name", "description")
    inlines = (ComputePlanAssignmentInline,)
    fieldsets = (
        (
            None,
            {
                "fields": (
                    "name",
                    "description",
                    "is_active",
                )
            },
        ),
        (
            _("Resources"),
            {
                "fields": (
                    "memory_requests",
                    "memory_limits",
                    "cpu_requests",
                    "cpu_limits",
                )
            },
        ),
    )


@admin.register(ComputePlanAssignment)
class ComputePlanAssignmentAdmin(admin.ModelAdmin):
    list_display = (
        "compute_plan",
        "control_plane_crd",
        "sla",
        "price",
        "unit",
        "sort_order",
        "is_active",
    )
    list_filter = ("is_active", "sla", "control_plane_crd")
    search_fields = (
        "compute_plan__name",
        "control_plane_crd__service_offering__service__name",
    )
    autocomplete_fields = ("compute_plan", "control_plane_crd")
    fieldsets = (
        (
            None,
            {
                "fields": (
                    "compute_plan",
                    "control_plane_crd",
                    "sla",
                    "is_active",
                    "sort_order",
                )
            },
        ),
        (
            _("Odoo Integration"),
            {
                "fields": (
                    "odoo_product_id",
                    "odoo_unit_id",
                )
            },
        ),
        (
            _("Pricing & Constraints"),
            {
                "fields": (
                    "price",
                    "unit",
                    "minimum_service_size",
                )
            },
        ),
    )


@admin.register(OdooObjectCache)
class OdooObjectCacheAdmin(admin.ModelAdmin):
    list_display = ("odoo_model", "odoo_id", "updated_at", "expires_at", "is_expired")
    list_filter = ("odoo_model", "updated_at", "expires_at")
    search_fields = ("odoo_model", "odoo_id")
    readonly_fields = ("created_at", "updated_at")
    actions = ["refresh_caches"]

    def is_expired(self, obj):
        return obj.is_expired()

    is_expired.boolean = True
    is_expired.short_description = _("Expired")

    def refresh_caches(self, request, queryset):
        """Admin action to refresh selected Odoo caches."""
        refreshed_count = 0
        for cache_obj in queryset:
            cache_obj.fetch_and_update()
            refreshed_count += 1
        messages.success(
            request,
            _(f"Successfully refreshed {refreshed_count} cache(s)."),
        )

    refresh_caches.short_description = _("Refresh caches")
