"""HTTP API for the module system.

* ``GET  /api/v1/modules/``            — registered manifests + per-tenant status
* ``GET  /api/v1/modules/<name>/``     — manifest detail
* ``POST /api/v1/modules/<name>/install``
* ``POST /api/v1/modules/<name>/enable``
* ``POST /api/v1/modules/<name>/disable``
* ``POST /api/v1/modules/<name>/upgrade``
* ``POST /api/v1/modules/<name>/rollback``
* ``GET  /api/v1/modules/<name>/flags/``
* ``POST /api/v1/modules/<name>/flags/<flag>/`` — body: ``{"enabled": true, "user_id": null}``
"""

from __future__ import annotations

from rest_framework import status
from rest_framework.exceptions import NotFound, ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView

from simorgh.apps.iam.permissions import HasPermission
from simorgh.apps.modules.models import TenantFeatureOverride, TenantModule
from simorgh.apps.modules.registry import (
    ModuleError,
    get_module,
    list_modules,
)
from simorgh.apps.modules.services import (
    disable_module,
    enable_module,
    grant_feature,
    install_module,
    is_feature_enabled,
    list_feature_status,
    purchase_feature,
    revoke_feature,
    rollback_module,
    set_feature_flag,
    toggle_feature,
    upgrade_module,
)
from simorgh.core.context import current_request_context


def _require_tenant():
    ctx = current_request_context()
    if ctx.tenant is None:
        raise ValidationError({"detail": "tenant required (set X-Tenant header)"})
    return ctx.tenant


def _module_row_payload(name: str, row: TenantModule | None) -> dict:
    if row is None:
        return {"installed": False}
    return {
        "installed": True,
        "version": row.version,
        "status": row.status,
        "previous_version": row.previous_version,
    }


class ModuleListView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.module.view"

    def get(self, _request: Request) -> Response:
        tenant = _require_tenant()
        rows = {m.name: m for m in TenantModule.objects.filter(tenant=tenant)}
        return Response(
            {
                "results": [
                    {
                        "manifest": m.serialize(),
                        "tenant_state": _module_row_payload(m.name, rows.get(m.name)),
                    }
                    for m in list_modules()
                ],
            },
        )


class ModuleDetailView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.module.view"

    def get(self, _request: Request, name: str) -> Response:
        tenant = _require_tenant()
        try:
            manifest = get_module(name)
        except ModuleError as exc:
            raise NotFound(str(exc)) from exc
        row = TenantModule.objects.filter(tenant=tenant, name=name).first()
        return Response(
            {
                "manifest": manifest.serialize(),
                "tenant_state": _module_row_payload(name, row),
            },
        )


class _LifecycleView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.module.manage"
    action: str = ""

    def post(self, request: Request, name: str) -> Response:
        tenant = _require_tenant()
        try:
            if self.action == "install":
                rows = install_module(
                    tenant,
                    name,
                    config=request.data.get("config") or {},
                )
                return Response(
                    {"installed": [r.name for r in rows]},
                    status=status.HTTP_201_CREATED,
                )
            handler = {
                "enable": enable_module,
                "disable": disable_module,
                "upgrade": upgrade_module,
                "rollback": rollback_module,
            }[self.action]
            row = handler(tenant, name)
            return Response(_module_row_payload(name, row))
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc


class ModuleInstallView(_LifecycleView):
    action = "install"


class ModuleEnableView(_LifecycleView):
    action = "enable"


class ModuleDisableView(_LifecycleView):
    action = "disable"


class ModuleUpgradeView(_LifecycleView):
    action = "upgrade"


class ModuleRollbackView(_LifecycleView):
    action = "rollback"


class FeatureFlagListView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature_flag.view"

    def get(self, _request: Request, name: str) -> Response:
        tenant = _require_tenant()
        try:
            manifest = get_module(name)
        except ModuleError as exc:
            raise NotFound(str(exc)) from exc
        results = []
        for spec in manifest.feature_flags:
            tenant_value = is_feature_enabled(tenant, name, spec.name)
            results.append(
                {
                    "name": spec.name,
                    "label_key": spec.label_key,
                    "default": spec.default,
                    "scope": spec.scope,
                    "tenant_enabled": tenant_value,
                },
            )
        return Response({"results": results})


class FeatureFlagSetView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature_flag.manage"

    def post(self, request: Request, name: str, flag: str) -> Response:
        tenant = _require_tenant()
        enabled = request.data.get("enabled")
        if enabled is None:
            raise ValidationError({"enabled": "required"})
        user_id = request.data.get("user_id")
        try:
            obj: TenantFeatureOverride = set_feature_flag(
                tenant,
                name,
                flag,
                enabled=bool(enabled),
                user_id=int(user_id) if user_id is not None else None,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response(
            {
                "id": str(obj.public_id),
                "module": name,
                "name": flag,
                "enabled": obj.enabled,
                "user_id": obj.user_id,
            },
            status=status.HTTP_200_OK,
        )


def _entitlement_payload(name: str, code: str, ent) -> dict:
    return {
        "id": str(ent.public_id),
        "module": name,
        "feature": code,
        "source": ent.source,
        "enabled": ent.enabled,
        "user_id": ent.user_id,
        "starts_at": ent.starts_at.isoformat() if ent.starts_at else None,
        "expires_at": ent.expires_at.isoformat() if ent.expires_at else None,
        "revoked_at": ent.revoked_at.isoformat() if ent.revoked_at else None,
        "metadata": ent.metadata,
    }


class FeatureListView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature.view"

    def get(self, request: Request, name: str) -> Response:
        tenant = _require_tenant()
        try:
            get_module(name)
        except ModuleError as exc:
            raise NotFound(str(exc)) from exc
        user_id = request.query_params.get("user_id")
        try:
            results = list_feature_status(
                tenant,
                name,
                user_id=int(user_id) if user_id else None,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response({"results": results})


class FeatureGrantView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature.grant"

    def post(self, request: Request, name: str, code: str) -> Response:
        tenant = _require_tenant()
        user_id = request.data.get("user_id")
        source = request.data.get("source") or "granted"
        expires_at = request.data.get("expires_at")
        metadata = request.data.get("metadata") or {}
        try:
            ent = grant_feature(
                tenant,
                name,
                code,
                source=source,
                user_id=int(user_id) if user_id is not None else None,
                expires_at=expires_at,
                metadata=metadata,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response(_entitlement_payload(name, code, ent), status=status.HTTP_200_OK)


class FeatureRevokeView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature.grant"

    def post(self, request: Request, name: str, code: str) -> Response:
        tenant = _require_tenant()
        user_id = request.data.get("user_id")
        try:
            ent = revoke_feature(
                tenant,
                name,
                code,
                user_id=int(user_id) if user_id is not None else None,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response(_entitlement_payload(name, code, ent), status=status.HTTP_200_OK)


class FeaturePurchaseView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature.purchase"

    def post(self, request: Request, name: str, code: str) -> Response:
        tenant = _require_tenant()
        user_id = request.data.get("user_id")
        expires_at = request.data.get("expires_at")
        metadata = request.data.get("metadata") or {}
        try:
            spec = get_module(name).feature(code)
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        # Default to the current user for user-scoped paid features.
        if user_id is None and spec.scope == "user":
            user_id = request.user.pk
        try:
            ent = purchase_feature(
                tenant,
                name,
                code,
                user_id=int(user_id) if user_id is not None else None,
                expires_at=expires_at,
                metadata=metadata,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response(
            _entitlement_payload(name, code, ent),
            status=status.HTTP_201_CREATED,
        )


class FeatureToggleView(APIView):
    permission_classes = (IsAuthenticated, HasPermission)
    required_permission = "modules.feature.toggle"

    def post(self, request: Request, name: str, code: str) -> Response:
        tenant = _require_tenant()
        enabled = request.data.get("enabled")
        if enabled is None:
            raise ValidationError({"enabled": "required"})
        user_id = request.data.get("user_id")
        try:
            ent = toggle_feature(
                tenant,
                name,
                code,
                enabled=bool(enabled),
                user_id=int(user_id) if user_id is not None else None,
            )
        except ModuleError as exc:
            raise ValidationError({"detail": str(exc)}) from exc
        return Response(_entitlement_payload(name, code, ent), status=status.HTTP_200_OK)
