"""Foundation CRUD API — Phase 5.1 + PATCH/archive + COA templates."""
from __future__ import annotations

from uuid import UUID

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

from app.api.deps import get_db, get_pagination, require_tenant
from app.core.security import get_current_user
from app.models.foundation import Account, ChartOfAccounts, CostCenter, Currency, Project
from app.models.types import AccountStatus
from app.repositories.foundation import (
    AccountRepository,
    ChartOfAccountsRepository,
    CostCenterRepository,
    CurrencyRepository,
    ProjectRepository,
)
from app.schemas.foundation import (
    AccountCreate,
    AccountRead,
    AccountUpdate,
    ChartOfAccountsCreate,
    ChartOfAccountsRead,
    ChartOfAccountsUpdate,
    CoaTemplateImportRequest,
    CoaTemplateRead,
    CostCenterCreate,
    CostCenterRead,
    CostCenterUpdate,
    CurrencyCreate,
    CurrencyRead,
    CurrencyUpdate,
    ProjectCreate,
    ProjectRead,
    ProjectUpdate,
)
from app.services.coa_templates import COA_TEMPLATES, import_coa_template
from shared.exceptions import AppError, NotFoundError
from shared.pagination import PaginationParams
from shared.security import CurrentUser

router = APIRouter()


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


# ── Charts ──────────────────────────────────────────────────────────────────

@router.post("/charts", response_model=ChartOfAccountsRead, status_code=status.HTTP_201_CREATED)
async def create_chart(
    body: ChartOfAccountsCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ChartOfAccountsRepository(db)
    if body.is_default:
        for existing in await repo.list_by_tenant(tenant_id, limit=500):
            existing.is_default = False
    entity = ChartOfAccounts(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/charts", response_model=list[ChartOfAccountsRead])
async def list_charts(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ChartOfAccountsRepository(db)
    return await repo.list_by_tenant(tenant_id, offset=pagination.offset, limit=pagination.page_size)


@router.get("/charts/{chart_id}", response_model=ChartOfAccountsRead)
async def get_chart(
    chart_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    entity = await ChartOfAccountsRepository(db).get(tenant_id, chart_id)
    if entity is None:
        raise NotFoundError("دفتر حساب یافت نشد", error_code="chart_not_found")
    return entity


@router.patch("/charts/{chart_id}", response_model=ChartOfAccountsRead)
async def update_chart(
    chart_id: UUID,
    body: ChartOfAccountsUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ChartOfAccountsRepository(db)
    entity = await repo.get(tenant_id, chart_id)
    if entity is None:
        raise NotFoundError("دفتر حساب یافت نشد", error_code="chart_not_found")
    data = body.model_dump(exclude_unset=True)
    if data.get("is_default") is True:
        for existing in await repo.list_by_tenant(tenant_id, limit=500):
            if existing.id != chart_id:
                existing.is_default = False
    _apply_update(entity, data)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/charts/{chart_id}/archive", response_model=ChartOfAccountsRead)
async def archive_chart(
    chart_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ChartOfAccountsRepository(db)
    entity = await repo.get(tenant_id, chart_id)
    if entity is None:
        raise NotFoundError("دفتر حساب یافت نشد", error_code="chart_not_found")
    entity.is_active = False
    entity.is_default = False
    await db.commit()
    await db.refresh(entity)
    return entity


# ── Templates (static paths before /{account_id}) ────────────────────────────

@router.get("/templates", response_model=list[CoaTemplateRead])
async def list_coa_templates(_user: CurrentUser = Depends(get_current_user)):
    return list(COA_TEMPLATES.values())


@router.get("/templates/{template_id}", response_model=CoaTemplateRead)
async def get_coa_template(template_id: str, _user: CurrentUser = Depends(get_current_user)):
    tpl = COA_TEMPLATES.get(template_id)
    if tpl is None:
        raise NotFoundError("قالب یافت نشد", error_code="template_not_found")
    return tpl


@router.post(
    "/templates/{template_id}/import",
    response_model=list[AccountRead],
    status_code=status.HTTP_201_CREATED,
)
async def import_template(
    template_id: str,
    body: CoaTemplateImportRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    accounts = await import_coa_template(
        db,
        tenant_id=tenant_id,
        template_id=template_id,
        chart_id=body.chart_id,
        chart_name=body.chart_name,
        chart_code=body.chart_code,
    )
    await db.commit()
    return accounts


# ── Currencies ──────────────────────────────────────────────────────────────

@router.post("/currencies", response_model=CurrencyRead, status_code=status.HTTP_201_CREATED)
async def create_currency(
    body: CurrencyCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CurrencyRepository(db)
    if body.is_base:
        for existing in await repo.list_by_tenant(tenant_id, limit=500):
            existing.is_base = False
    entity = Currency(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/currencies", response_model=list[CurrencyRead])
async def list_currencies(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CurrencyRepository(db)
    return await repo.list_by_tenant(tenant_id, offset=pagination.offset, limit=pagination.page_size)


@router.patch("/currencies/{currency_id}", response_model=CurrencyRead)
async def update_currency(
    currency_id: UUID,
    body: CurrencyUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CurrencyRepository(db)
    entity = await repo.get(tenant_id, currency_id)
    if entity is None:
        raise NotFoundError("ارز یافت نشد", error_code="currency_not_found")
    data = body.model_dump(exclude_unset=True)
    if data.get("is_base") is True:
        for existing in await repo.list_by_tenant(tenant_id, limit=500):
            if existing.id != currency_id:
                existing.is_base = False
    _apply_update(entity, data)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/currencies/{currency_id}/set-base", response_model=CurrencyRead)
async def set_base_currency(
    currency_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CurrencyRepository(db)
    entity = await repo.get(tenant_id, currency_id)
    if entity is None:
        raise NotFoundError("ارز یافت نشد", error_code="currency_not_found")
    if not entity.is_active:
        raise AppError(
            "ارز غیرفعال نمی‌تواند پایه باشد",
            status_code=422,
            error_code="currency_inactive",
        )
    for existing in await repo.list_by_tenant(tenant_id, limit=500):
        existing.is_base = existing.id == currency_id
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/currencies/{currency_id}/archive", response_model=CurrencyRead)
async def archive_currency(
    currency_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CurrencyRepository(db)
    entity = await repo.get(tenant_id, currency_id)
    if entity is None:
        raise NotFoundError("ارز یافت نشد", error_code="currency_not_found")
    if entity.is_base:
        raise AppError(
            "ارز پایه قابل آرشیو نیست",
            status_code=422,
            error_code="base_currency_archive",
        )
    entity.is_active = False
    await db.commit()
    await db.refresh(entity)
    return entity


# ── Cost centers ────────────────────────────────────────────────────────────

@router.post("/cost-centers", response_model=CostCenterRead, status_code=status.HTTP_201_CREATED)
async def create_cost_center(
    body: CostCenterCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CostCenterRepository(db)
    entity = CostCenter(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/cost-centers", response_model=list[CostCenterRead])
async def list_cost_centers(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CostCenterRepository(db)
    return await repo.list_by_tenant(tenant_id, offset=pagination.offset, limit=pagination.page_size)


@router.patch("/cost-centers/{cost_center_id}", response_model=CostCenterRead)
async def update_cost_center(
    cost_center_id: UUID,
    body: CostCenterUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CostCenterRepository(db)
    entity = await repo.get(tenant_id, cost_center_id)
    if entity is None:
        raise NotFoundError("مرکز هزینه یافت نشد", error_code="cost_center_not_found")
    _apply_update(entity, body.model_dump(exclude_unset=True))
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/cost-centers/{cost_center_id}/archive", response_model=CostCenterRead)
async def archive_cost_center(
    cost_center_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CostCenterRepository(db)
    entity = await repo.get(tenant_id, cost_center_id)
    if entity is None:
        raise NotFoundError("مرکز هزینه یافت نشد", error_code="cost_center_not_found")
    entity.is_active = False
    await db.commit()
    await db.refresh(entity)
    return entity


# ── Projects ────────────────────────────────────────────────────────────────

@router.post("/projects", response_model=ProjectRead, status_code=status.HTTP_201_CREATED)
async def create_project(
    body: ProjectCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ProjectRepository(db)
    entity = Project(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/projects", response_model=list[ProjectRead])
async def list_projects(
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ProjectRepository(db)
    return await repo.list_by_tenant(tenant_id, offset=pagination.offset, limit=pagination.page_size)


@router.patch("/projects/{project_id}", response_model=ProjectRead)
async def update_project(
    project_id: UUID,
    body: ProjectUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ProjectRepository(db)
    entity = await repo.get(tenant_id, project_id)
    if entity is None:
        raise NotFoundError("پروژه یافت نشد", error_code="project_not_found")
    _apply_update(entity, body.model_dump(exclude_unset=True))
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/projects/{project_id}/archive", response_model=ProjectRead)
async def archive_project(
    project_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = ProjectRepository(db)
    entity = await repo.get(tenant_id, project_id)
    if entity is None:
        raise NotFoundError("پروژه یافت نشد", error_code="project_not_found")
    entity.is_active = False
    await db.commit()
    await db.refresh(entity)
    return entity


# ── Accounts (param routes last) ────────────────────────────────────────────

@router.post("", response_model=AccountRead, status_code=status.HTTP_201_CREATED)
async def create_account(
    body: AccountCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = AccountRepository(db)
    entity = Account(tenant_id=tenant_id, **body.model_dump())
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("", response_model=list[AccountRead])
async def list_accounts(
    chart_id: UUID | None = Query(default=None),
    tenant_id: UUID = Depends(require_tenant),
    pagination: PaginationParams = Depends(get_pagination),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = AccountRepository(db)
    if chart_id is not None:
        return await repo.list_by_chart(
            tenant_id, chart_id, offset=pagination.offset, limit=pagination.page_size
        )
    return await repo.list_by_tenant(tenant_id, offset=pagination.offset, limit=pagination.page_size)


@router.get("/{account_id}", response_model=AccountRead)
async def get_account(
    account_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = AccountRepository(db)
    entity = await repo.get(tenant_id, account_id)
    if entity is None:
        raise NotFoundError("حساب یافت نشد", error_code="account_not_found")
    return entity


@router.patch("/{account_id}", response_model=AccountRead)
async def update_account(
    account_id: UUID,
    body: AccountUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = AccountRepository(db)
    entity = await repo.get(tenant_id, account_id)
    if entity is None:
        raise NotFoundError("حساب یافت نشد", error_code="account_not_found")
    _apply_update(entity, body.model_dump(exclude_unset=True))
    await db.commit()
    await db.refresh(entity)
    return entity


@router.post("/{account_id}/archive", response_model=AccountRead)
async def archive_account(
    account_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = AccountRepository(db)
    entity = await repo.get(tenant_id, account_id)
    if entity is None:
        raise NotFoundError("حساب یافت نشد", error_code="account_not_found")
    entity.status = AccountStatus.INACTIVE
    await db.commit()
    await db.refresh(entity)
    return entity
