"""/api/npl/* — New Product Listing endpoints.

Access: every endpoint guarded by require_section('npl_module'), which the
permission matrix grants to Admin / Nabava / Veleprodaja only.

Routes:
  GET    /api/npl/health
  GET    /api/npl/enum-values           — admin-only (npl_admin_enum)
  GET    /api/npl/summary               — dashboard widget summary
  GET    /api/npl                       — list (filters: status, group, channel, q)
  POST   /api/npl                       — create
  GET    /api/npl/{id}                  — read (incl. nested child rows)
  PUT    /api/npl/{id}                  — update master fields
  DELETE /api/npl/{id}                  — delete (cascade)

  POST   /api/npl/{id}/allocation       — add / upsert allocation row
  DELETE /api/npl/allocation/{aid}      — delete allocation row
  POST   /api/npl/{id}/forecast         — add / upsert forecast row
  DELETE /api/npl/forecast/{fid}        — delete forecast row
  POST   /api/npl/{id}/substitution     — add / upsert substitution row
  DELETE /api/npl/substitution/{sid}    — delete substitution row

  POST   /api/npl/{id}/approve          — draft → approved
  POST   /api/npl/{id}/mark-ordered     — approved | fmb_triggered → ordered
  POST   /api/npl/{id}/mark-in-stock    — ordered → in_stock
  POST   /api/npl/{id}/activate         — in_stock → active (+ parent phase-out if hard)
  POST   /api/npl/{id}/phase-out        — any → phase_out
  POST   /api/npl/run-fmb-check         — daily sweep (cron / admin button)
"""
from __future__ import annotations

from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session

from backend.api.middleware.auth import (
    CurrentUser,
    get_current_user,
    require_section,
)
from backend.models.database import get_db
from backend.schemas.npl import (
    NplApprovePayload,
    NplDashboardSummary,
    NplEnumValues,
    NplFmbCheckResponse,
    NplForecastCreate,
    NplForecastRead,
    NplListResponse,
    NplProductCreate,
    NplProductRead,
    NplProductUpdate,
    NplStatusChangeResponse,
    NplStockAllocationCreate,
    NplStockAllocationRead,
    NplSubstitutionCreate,
    NplSubstitutionRead,
    NplSupplierOption,
    NplReportListResponse,
    NplReportTimeseriesResponse,
)
from backend.services.npl_service import NplError, NplService

router = APIRouter(prefix="/npl", tags=["npl"])

_REQ_MODULE = Depends(require_section("npl_module"))
_REQ_REPORT = Depends(require_section("npl_report"))
_REQ_ADMIN  = Depends(require_section("npl_admin_enum"))


def _svc(db: Session) -> NplService:
    return NplService(db)


def _wrap(fn):
    """Translate NplError to HTTP 400."""
    try:
        return fn()
    except NplError as e:
        raise HTTPException(status_code=400, detail=str(e))


# ----------------------------------------------------------------------
# Health / meta
# ----------------------------------------------------------------------
@router.get("/health")
def health() -> dict:
    return {"status": "ok"}


@router.get("/enum-values", response_model=NplEnumValues)
def enum_values(db: Session = Depends(get_db),
                _: CurrentUser = _REQ_ADMIN) -> NplEnumValues:
    return _svc(db).enum_values()


@router.get("/summary", response_model=NplDashboardSummary)
def summary(db: Session = Depends(get_db),
            _: CurrentUser = _REQ_MODULE) -> NplDashboardSummary:
    return _svc(db).dashboard_summary()


# --- NPL Report (actuals for NPD list) -------------------------------
@router.get("/report/list", response_model=NplReportListResponse)
def report_list(db: Session = Depends(get_db),
                 _: CurrentUser = _REQ_REPORT) -> NplReportListResponse:
    return _svc(db).report_list()


@router.get("/report/timeseries", response_model=NplReportTimeseriesResponse)
def report_timeseries(
    sku: str = Query(..., min_length=1),
    granularity: str = Query("week", pattern="^(week|month)$"),
    db: Session = Depends(get_db),
    _: CurrentUser = _REQ_REPORT,
) -> NplReportTimeseriesResponse:
    return _wrap(lambda: _svc(db).report_timeseries(sku, granularity))


@router.get("/suppliers", response_model=list[NplSupplierOption])
def supplier_options(
    q: Optional[str] = Query(None, description="Filter by name substring"),
    limit: int = Query(200, ge=1, le=1000),
    db: Session = Depends(get_db),
    _: CurrentUser = _REQ_MODULE,
) -> list[NplSupplierOption]:
    return _svc(db).supplier_options(search=q, limit=limit)


# ----------------------------------------------------------------------
# List + CRUD
# ----------------------------------------------------------------------
@router.get("", response_model=NplListResponse)
def list_npl(
    status:  list[str] = Query(default=[]),
    group:   list[str] = Query(default=[]),
    channel: Optional[str] = Query(None),
    q:       Optional[str] = Query(None, description="SKU or name search"),
    db: Session = Depends(get_db),
    _: CurrentUser = _REQ_MODULE,
) -> NplListResponse:
    return _wrap(lambda: _svc(db).list_products(
        status=status or None,
        group=group or None,
        channel=channel,
        search=q,
    ))


@router.post("", response_model=NplProductRead, status_code=201)
def create_npl(payload: NplProductCreate,
                db: Session = Depends(get_db),
                user: CurrentUser = _REQ_MODULE) -> NplProductRead:
    return _wrap(lambda: _svc(db).create(payload, user_id=user.user_id))


@router.get("/{npl_id}", response_model=NplProductRead)
def get_npl(npl_id: int, db: Session = Depends(get_db),
             _: CurrentUser = _REQ_MODULE) -> NplProductRead:
    return _wrap(lambda: _svc(db).get_product(npl_id))


@router.put("/{npl_id}", response_model=NplProductRead)
def update_npl(npl_id: int, payload: NplProductUpdate,
                db: Session = Depends(get_db),
                _: CurrentUser = _REQ_MODULE) -> NplProductRead:
    return _wrap(lambda: _svc(db).update(npl_id, payload))


@router.delete("/{npl_id}", status_code=204)
def delete_npl(npl_id: int, db: Session = Depends(get_db),
                _: CurrentUser = _REQ_MODULE) -> None:
    _wrap(lambda: _svc(db).delete(npl_id))


# ----------------------------------------------------------------------
# Child CRUD
# ----------------------------------------------------------------------
@router.post("/{npl_id}/allocation", response_model=NplStockAllocationRead,
              status_code=201)
def add_allocation(npl_id: int, payload: NplStockAllocationCreate,
                    db: Session = Depends(get_db),
                    _: CurrentUser = _REQ_MODULE) -> NplStockAllocationRead:
    return _wrap(lambda: _svc(db).add_allocation(npl_id, payload))


@router.delete("/allocation/{alloc_id}", status_code=204)
def delete_allocation(alloc_id: int, db: Session = Depends(get_db),
                       _: CurrentUser = _REQ_MODULE) -> None:
    _svc(db).delete_allocation(alloc_id)


@router.post("/{npl_id}/forecast", response_model=NplForecastRead,
              status_code=201)
def add_forecast(npl_id: int, payload: NplForecastCreate,
                  db: Session = Depends(get_db),
                  _: CurrentUser = _REQ_MODULE) -> NplForecastRead:
    return _wrap(lambda: _svc(db).add_forecast(npl_id, payload))


@router.delete("/forecast/{fc_id}", status_code=204)
def delete_forecast(fc_id: int, db: Session = Depends(get_db),
                     _: CurrentUser = _REQ_MODULE) -> None:
    _svc(db).delete_forecast(fc_id)


@router.post("/{npl_id}/substitution", response_model=NplSubstitutionRead,
              status_code=201)
def add_substitution(npl_id: int, payload: NplSubstitutionCreate,
                      db: Session = Depends(get_db),
                      _: CurrentUser = _REQ_MODULE) -> NplSubstitutionRead:
    return _wrap(lambda: _svc(db).add_substitution(npl_id, payload))


@router.delete("/substitution/{sub_id}", status_code=204)
def delete_substitution(sub_id: int, db: Session = Depends(get_db),
                         _: CurrentUser = _REQ_MODULE) -> None:
    _svc(db).delete_substitution(sub_id)


# ----------------------------------------------------------------------
# Lifecycle transitions
# ----------------------------------------------------------------------
@router.post("/{npl_id}/approve", response_model=NplStatusChangeResponse)
def approve(npl_id: int,
             payload: Optional[NplApprovePayload] = None,
             db: Session = Depends(get_db),
             user: CurrentUser = _REQ_MODULE) -> NplStatusChangeResponse:
    return _wrap(lambda: _svc(db).approve(npl_id, user_id=user.user_id))


@router.post("/{npl_id}/mark-ordered", response_model=NplStatusChangeResponse)
def mark_ordered(npl_id: int, db: Session = Depends(get_db),
                  _: CurrentUser = _REQ_MODULE) -> NplStatusChangeResponse:
    return _wrap(lambda: _svc(db).mark_ordered(npl_id))


@router.post("/{npl_id}/mark-in-stock", response_model=NplStatusChangeResponse)
def mark_in_stock(npl_id: int, db: Session = Depends(get_db),
                   _: CurrentUser = _REQ_MODULE) -> NplStatusChangeResponse:
    return _wrap(lambda: _svc(db).mark_in_stock(npl_id))


@router.post("/{npl_id}/activate", response_model=NplStatusChangeResponse)
def activate(npl_id: int, db: Session = Depends(get_db),
              _: CurrentUser = _REQ_MODULE) -> NplStatusChangeResponse:
    return _wrap(lambda: _svc(db).activate(npl_id))


@router.post("/{npl_id}/phase-out", response_model=NplStatusChangeResponse)
def phase_out(npl_id: int, db: Session = Depends(get_db),
               _: CurrentUser = _REQ_MODULE) -> NplStatusChangeResponse:
    return _wrap(lambda: _svc(db).phase_out(npl_id))


@router.post("/run-fmb-check", response_model=NplFmbCheckResponse)
def run_fmb_check(db: Session = Depends(get_db),
                   _: CurrentUser = _REQ_MODULE) -> NplFmbCheckResponse:
    return _svc(db).run_fmb_check()
