"""
Admin-managed dropdown lists. Each list is its own master table (see
app/models/master_list_models.py); master_list_registry names them.

GET /lookups/?category=AC_MAKE           → values of one list
GET /lookups/?category=LOOKUP_CATEGORY   → the lists themselves (from the registry)

Reading is open to any logged-in user because every screen needs its dropdowns;
changing values needs PRODUCTION_MASTERS permissions, the same as other masters.
"""
from typing import List, Optional, Any
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy import select, func
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.security import get_current_user, require_permission
from app.models.master_list_models import MasterListRegistry, MASTER_LIST_MODELS, META_KEYS

router = APIRouter()

CATEGORY_REGISTRY = 'LOOKUP_CATEGORY'


class LookupOut(BaseModel):
    lookup_id: UUID | str
    category: str
    code: str
    label: str
    sort_order: int
    meta: Optional[dict[str, Any]] = None
    is_active: bool


class LookupCreate(BaseModel):
    category: str = Field(..., min_length=1, max_length=60)
    code: Optional[str] = Field(None, max_length=120)
    label: str = Field(..., min_length=1, max_length=300)
    sort_order: Optional[int] = None
    meta: Optional[dict[str, Any]] = None


class LookupUpdate(BaseModel):
    label: Optional[str] = Field(None, min_length=1, max_length=300)
    sort_order: Optional[int] = None
    meta: Optional[dict[str, Any]] = None
    is_active: Optional[bool] = None


def _model_for(category: str):
    model = MASTER_LIST_MODELS.get(category.upper())
    if not model:
        raise HTTPException(400, f"Unknown list '{category}'")
    return model


def _out(category: str, row) -> LookupOut:
    meta = {key: getattr(row, col) for key, col in META_KEYS.get(category, {}).items()} or None
    return LookupOut(lookup_id=row.id, category=category, code=row.code, label=row.label,
                     sort_order=row.sort_order, meta=meta, is_active=row.is_active)


def _apply_meta(category: str, row, meta: Optional[dict]) -> None:
    for key, col in META_KEYS.get(category, {}).items():
        if meta and meta.get(key) is not None:
            setattr(row, col, int(meta[key]))


async def _find_row(db: AsyncSession, lookup_id: UUID):
    """Values are addressed by id alone, so search the list tables for it."""
    for category, model in MASTER_LIST_MODELS.items():
        row = await db.get(model, lookup_id)
        if row:
            return category, row
    raise HTTPException(404, 'Value not found')


@router.get('/', response_model=List[LookupOut])
async def list_lookups(
    category: str = Query(..., description='List code, or LOOKUP_CATEGORY for the lists themselves'),
    include_inactive: bool = False,
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    category = category.upper()
    if category == CATEGORY_REGISTRY:
        regs = (await db.execute(select(MasterListRegistry).order_by(MasterListRegistry.sort_order))).scalars().all()
        return [LookupOut(lookup_id=r.list_code, category=CATEGORY_REGISTRY, code=r.list_code, label=r.label,
                          sort_order=r.sort_order, meta={'description': r.description, 'table': r.table_name},
                          is_active=True) for r in regs]

    model = _model_for(category)
    stmt = select(model)
    if not include_inactive:
        stmt = stmt.where(model.is_active == True)  # noqa: E712
    rows = (await db.execute(stmt.order_by(model.sort_order, model.label))).scalars().all()
    return [_out(category, r) for r in rows]


@router.post('/', response_model=LookupOut, status_code=201)
async def create_lookup(
    payload: LookupCreate,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('PRODUCTION_MASTERS', 'create')),
):
    category = payload.category.strip().upper()
    model = _model_for(category)

    sort_order = payload.sort_order
    if sort_order is None:
        sort_order = ((await db.execute(select(func.max(model.sort_order)))).scalar() or 0) + 1

    row = model(code=(payload.code or payload.label).strip(), label=payload.label.strip(), sort_order=sort_order)
    _apply_meta(category, row, payload.meta)
    db.add(row)
    try:
        await db.commit()
    except IntegrityError:
        await db.rollback()
        raise HTTPException(409, f"'{row.code}' already exists in this list")
    await db.refresh(row)
    return _out(category, row)


@router.put('/{lookup_id}', response_model=LookupOut)
async def update_lookup(
    lookup_id: UUID,
    payload: LookupUpdate,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('PRODUCTION_MASTERS', 'edit')),
):
    category, row = await _find_row(db, lookup_id)
    data = payload.model_dump(exclude_unset=True)
    if 'label' in data:
        row.label = data['label'].strip()
    if 'sort_order' in data:
        row.sort_order = data['sort_order']
    if 'is_active' in data:
        row.is_active = data['is_active']
    if 'meta' in data:
        _apply_meta(category, row, data['meta'])
    await db.commit()
    await db.refresh(row)
    return _out(category, row)


@router.delete('/{lookup_id}', status_code=204)
async def deactivate_lookup(
    lookup_id: UUID,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('PRODUCTION_MASTERS', 'delete')),
):
    """Soft delete: records that already use this value keep showing it."""
    _, row = await _find_row(db, lookup_id)
    row.is_active = False
    await db.commit()
