"""Custom fields routes — merge plugin definitions with stored values."""

from __future__ import annotations

import uuid
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Body
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.db import get_db
from app.deps import get_current_user, require_permission
from app.models.contact import Contact
from app.plugins.registry import get_registry

router = APIRouter(prefix="/api/v1/contacts", tags=["custom-fields"])


class CustomFieldUpdateRequest(BaseModel):
    """Request body for updating custom field values."""

    values: dict[str, Any] = {}


def _collect_custom_field_definitions(entity: str = "contact") -> list[dict[str, Any]]:
    """Collect all custom field definitions from active plugin manifests."""
    definitions: list[dict[str, Any]] = []
    seen_names: set[str] = set()
    registry = get_registry()
    for plugin in registry._plugins.values():
        manifest = plugin.manifest
        for cf in manifest.custom_fields:
            if cf.entity != entity:
                continue
            if cf.name in seen_names:
                continue
            seen_names.add(cf.name)
            definitions.append(
                {
                    "name": cf.name,
                    "label": cf.label,
                    "label_key": cf.label_key,
                    "field_type": cf.field_type,
                    "options": cf.options,
                    "default_value": cf.default_value,
                    "required": cf.required,
                    "entity": cf.entity,
                    "plugin": manifest.name,
                }
            )
    return definitions


def _merge_definitions_with_values(
    definitions: list[dict[str, Any]], stored: dict[str, Any] | None
) -> list[dict[str, Any]]:
    """Merge field definitions with stored values, applying defaults."""
    stored = stored or {}
    result: list[dict[str, Any]] = []
    for d in definitions:
        name = d["name"]
        value = stored.get(name, d.get("default_value"))
        entry = {**d, "value": value}
        result.append(entry)
    return result


@router.get("/{contact_id}/custom-fields", dependencies=[Depends(require_permission("contacts:read"))])
async def get_custom_fields(
    contact_id: str,
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """Get all custom fields for a contact (merged definitions + stored values)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        cid = uuid.UUID(contact_id)
    except (ValueError, TypeError):
        raise HTTPException(400, detail={"detail": "Invalid contact_id", "code": "invalid_id"}) from None

    result = await db.execute(
        select(Contact).where(Contact.id == cid, Contact.tenant_id == tenant_id)
    )
    contact = result.scalar_one_or_none()
    if contact is None:
        raise HTTPException(404, detail={"detail": "Contact not found", "code": "not_found"})

    definitions = _collect_custom_field_definitions("contact")
    merged = _merge_definitions_with_values(definitions, contact.custom)
    return {"fields": merged}


@router.patch("/{contact_id}/custom-fields", dependencies=[Depends(require_permission("contacts:write"))])
async def update_custom_fields(
    contact_id: str,
    body: CustomFieldUpdateRequest,
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """Update custom field values for a contact (stored in contacts.custom JSONB)."""
    tenant_id = uuid.UUID(current_user["tenant_id"])
    try:
        cid = uuid.UUID(contact_id)
    except (ValueError, TypeError):
        raise HTTPException(400, detail={"detail": "Invalid contact_id", "code": "invalid_id"}) from None

    result = await db.execute(
        select(Contact).where(Contact.id == cid, Contact.tenant_id == tenant_id)
    )
    contact = result.scalar_one_or_none()
    if contact is None:
        raise HTTPException(404, detail={"detail": "Contact not found", "code": "not_found"})

    # Validate against definitions
    definitions = _collect_custom_field_definitions("contact")
    def_map = {d["name"]: d for d in definitions}

    current_custom = dict(contact.custom or {})
    for name, value in body.values.items():
        if name not in def_map:
            raise HTTPException(
                400,
                detail={"detail": f"Unknown custom field: {name}", "code": "unknown_field"},
            )
        field_def = def_map[name]
        # Validate required
        if field_def["required"] and (value is None or value == ""):
            raise HTTPException(
                400,
                detail={"detail": f"Field '{name}' is required", "code": "required_field"},
            )
        # Validate select/multiselect options
        if field_def["field_type"] == "select" and value is not None:
            if value not in field_def["options"]:
                raise HTTPException(
                    400,
                    detail={"detail": f"Invalid option for field '{name}'", "code": "invalid_option"},
                )
        if field_def["field_type"] == "multiselect" and value is not None:
            if not isinstance(value, list):
                raise HTTPException(
                    400,
                    detail={"detail": f"Field '{name}' must be a list", "code": "invalid_type"},
                )
            for v in value:
                if v not in field_def["options"]:
                    raise HTTPException(
                        400,
                        detail={"detail": f"Invalid option '{v}' for field '{name}'", "code": "invalid_option"},
                    )
        current_custom[name] = value

    contact.custom = current_custom
    await db.flush()
    merged = _merge_definitions_with_values(definitions, contact.custom)
    return {"fields": merged}
