"""Fiscal year/period API — Phase 5.1 + 5.3 + PATCH / set-current / generate."""
from __future__ import annotations

from uuid import UUID

from fastapi import APIRouter, Depends, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db, require_tenant
from app.core.security import get_current_user
from app.models.foundation import FiscalPeriod, FiscalYear
from app.repositories.foundation import FiscalPeriodRepository, FiscalYearRepository
from app.schemas.foundation import (
    FiscalPeriodCreate,
    FiscalPeriodRead,
    FiscalPeriodUpdate,
    FiscalYearCreate,
    FiscalYearRead,
    FiscalYearUpdate,
)
from app.services.fiscal_service import FiscalManagementService
from shared.exceptions import NotFoundError
from shared.security import CurrentUser

router = APIRouter()


def _apply_update(entity, data: dict) -> None:
    for key, value in data.items():
        setattr(entity, key, value)


@router.post("/years", response_model=FiscalYearRead, status_code=status.HTTP_201_CREATED)
async def create_fiscal_year(
    body: FiscalYearCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalYearRepository(db)
    if body.is_current:
        for y in await repo.list_by_tenant(tenant_id, limit=500):
            y.is_current = False
    entity = FiscalYear(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/years", response_model=list[FiscalYearRead])
async def list_fiscal_years(
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalYearRepository(db)
    return await repo.list_by_tenant(tenant_id, limit=100)


@router.patch("/years/{year_id}", response_model=FiscalYearRead)
async def update_fiscal_year(
    year_id: UUID,
    body: FiscalYearUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalYearRepository(db)
    entity = await repo.get(tenant_id, year_id)
    if entity is None:
        raise NotFoundError("سال مالی یافت نشد", error_code="year_not_found")
    data = body.model_dump(exclude_unset=True)
    if data.get("is_current") is True:
        for y in await repo.list_by_tenant(tenant_id, limit=500):
            y.is_current = y.id == year_id
        data.pop("is_current", None)
        entity.is_current = True
    _apply_update(entity, data)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/years/{year_id}/set-current", response_model=FiscalYearRead)
async def set_current_year(
    year_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    year = await svc.set_current_year(tenant_id, year_id)
    await db.commit()
    return year


@router.post("/years/{year_id}/close", response_model=FiscalYearRead)
async def close_fiscal_year(
    year_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    year = await svc.close_fiscal_year(tenant_id, year_id)
    await db.commit()
    return year


@router.post("/years/{year_id}/generate-periods", response_model=list[FiscalPeriodRead])
async def generate_periods(
    year_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    periods = await svc.generate_monthly_periods(tenant_id, year_id)
    await db.commit()
    return periods


@router.post("/periods", response_model=FiscalPeriodRead, status_code=status.HTTP_201_CREATED)
async def create_fiscal_period(
    body: FiscalPeriodCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalPeriodRepository(db)
    if body.is_current:
        for p in await repo.list_by_tenant(tenant_id, limit=500):
            p.is_current = False
    entity = FiscalPeriod(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/periods", response_model=list[FiscalPeriodRead])
async def list_fiscal_periods(
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalPeriodRepository(db)
    return await repo.list_by_tenant(tenant_id, limit=100)


@router.patch("/periods/{period_id}", response_model=FiscalPeriodRead)
async def update_fiscal_period(
    period_id: UUID,
    body: FiscalPeriodUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = FiscalPeriodRepository(db)
    entity = await repo.get(tenant_id, period_id)
    if entity is None:
        raise NotFoundError("دوره مالی یافت نشد", error_code="period_not_found")
    data = body.model_dump(exclude_unset=True)
    if data.get("is_current") is True:
        for p in await repo.list_by_tenant(tenant_id, limit=500):
            p.is_current = p.id == period_id
        data.pop("is_current", None)
        entity.is_current = True
    _apply_update(entity, data)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/periods/{period_id}/set-current", response_model=FiscalPeriodRead)
async def set_current_period(
    period_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    period = await svc.set_current_period(tenant_id, period_id)
    await db.commit()
    return period


@router.post("/periods/{period_id}/lock", response_model=FiscalPeriodRead)
async def lock_period(
    period_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    period = await svc.lock_period(tenant_id, period_id)
    await db.commit()
    return period


@router.post("/periods/{period_id}/close", response_model=FiscalPeriodRead)
async def close_period(
    period_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    period = await svc.close_period(tenant_id, period_id)
    await db.commit()
    return period


@router.post("/periods/{period_id}/reopen", response_model=FiscalPeriodRead)
async def reopen_period(
    period_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = FiscalManagementService(db)
    period = await svc.reopen_period(tenant_id, period_id)
    await db.commit()
    return period
