from datetime import date

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

from app.db.session import get_db
from app.models.operations import AccountingPeriod, OverheadEntry, StatementSnapshot
from app.schemas.operations import (
    AccountingPeriodCreate,
    AccountingPeriodRead,
    CostAllocationGenerateResult,
    CostAllocationRead,
    OverheadEntryCreate,
    OverheadEntryRead,
    ProductMaterialCostBoardRead,
    StatementSnapshotCreate,
    StatementSnapshotRead,
)
from app.services.auth import AuthContext, require_authenticated_user
from app.services.operations import (
    build_product_material_cost_board,
    build_statement_summary,
    generate_cost_allocations,
    get_cost_allocation_rows,
    get_overheads_query,
    get_periods_query,
    get_statement_snapshots_query,
    to_decimal,
)
from app.services.sales_planning import code_token, next_doc_no

router = APIRouter(dependencies=[Depends(require_authenticated_user)])


def build_statement_snapshot_no(db: Session, period: AccountingPeriod, statement_type: str, report_date: date) -> str:
    date_part = report_date.strftime("%Y%m%d")
    period_part = code_token(period.period_code, f"PER{period.id:04d}", 8)
    type_part = code_token(statement_type, "STM", 8)
    return next_doc_no(db, StatementSnapshot, "snapshot_no", f"FIN-NB-{date_part}-{period_part}-{type_part}")


@router.get("/product-material-cost-board", response_model=ProductMaterialCostBoardRead)
def get_product_material_cost_board(
    date_from: date | None = Query(default=None),
    date_to: date | None = Query(default=None),
    db: Session = Depends(get_db),
) -> ProductMaterialCostBoardRead:
    end_date = date_to or date.today()
    start_date = date_from or end_date.replace(day=1)
    return build_product_material_cost_board(db, date_from=start_date, date_to=end_date)


@router.get("/periods", response_model=list[AccountingPeriodRead])
def list_accounting_periods(
    limit: int = Query(default=50, ge=1, le=200),
    db: Session = Depends(get_db),
) -> list[AccountingPeriodRead]:
    rows = db.execute(get_periods_query(limit=limit)).mappings().all()
    return [AccountingPeriodRead.model_validate(dict(row)) for row in rows]


@router.post("/periods", response_model=AccountingPeriodRead)
def create_accounting_period(payload: AccountingPeriodCreate, db: Session = Depends(get_db)) -> AccountingPeriodRead:
    period_code = f"{payload.year_no}{payload.month_no:02d}"
    period = AccountingPeriod(
        period_code=period_code,
        year_no=payload.year_no,
        month_no=payload.month_no,
        start_date=payload.start_date,
        end_date=payload.end_date,
        close_status=payload.close_status,
        closed_at=None,
    )
    db.add(period)
    db.commit()

    row = db.execute(get_periods_query(limit=1).where(AccountingPeriod.id == period.id)).mappings().first()
    if not row:
        raise HTTPException(status_code=500, detail="财务期间创建后读取失败")
    return AccountingPeriodRead.model_validate(dict(row))


@router.get("/overheads", response_model=list[OverheadEntryRead])
def list_overhead_entries(
    limit: int = Query(default=100, ge=1, le=300),
    db: Session = Depends(get_db),
) -> list[OverheadEntryRead]:
    rows = db.execute(get_overheads_query(limit=limit)).mappings().all()
    return [OverheadEntryRead.model_validate(dict(row)) for row in rows]


@router.post("/overheads", response_model=OverheadEntryRead)
def create_overhead_entry(
    payload: OverheadEntryCreate,
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> OverheadEntryRead:
    period = db.get(AccountingPeriod, payload.period_id)
    if not period:
        raise HTTPException(status_code=404, detail="财务期间不存在")

    entry = OverheadEntry(
        period_id=payload.period_id,
        overhead_type=payload.overhead_type,
        dept_id=payload.dept_id,
        amount=to_decimal(payload.amount, "0.01"),
        allocation_basis=payload.allocation_basis,
        description=payload.description,
        created_by=context.user.id,
    )
    db.add(entry)
    db.commit()

    row = db.execute(get_overheads_query(limit=1).where(OverheadEntry.id == entry.id)).mappings().first()
    if not row:
        raise HTTPException(status_code=500, detail="制造费用录入后读取失败")
    return OverheadEntryRead.model_validate(dict(row))


@router.get("/cost-allocations", response_model=list[CostAllocationRead])
def list_cost_allocations(
    period_id: int | None = Query(default=None),
    limit: int = Query(default=100, ge=1, le=500),
    db: Session = Depends(get_db),
) -> list[CostAllocationRead]:
    return get_cost_allocation_rows(db, period_id=period_id, limit=limit)


@router.post("/generate-cost-allocations/{period_id}", response_model=CostAllocationGenerateResult)
def generate_period_cost_allocations(period_id: int, db: Session = Depends(get_db)) -> CostAllocationGenerateResult:
    period = db.get(AccountingPeriod, period_id)
    if not period:
        raise HTTPException(status_code=404, detail="财务期间不存在")
    rows = generate_cost_allocations(db, period_id)
    db.commit()
    return CostAllocationGenerateResult(
        period_id=period.id,
        period_code=period.period_code,
        generated_count=len(rows),
        rows=rows,
    )


@router.get("/statements", response_model=list[StatementSnapshotRead])
def list_statement_snapshots(
    limit: int = Query(default=100, ge=1, le=300),
    db: Session = Depends(get_db),
) -> list[StatementSnapshotRead]:
    rows = db.execute(get_statement_snapshots_query(limit=limit)).mappings().all()
    return [StatementSnapshotRead.model_validate(dict(row)) for row in rows]


@router.post("/statements", response_model=StatementSnapshotRead)
def create_statement_snapshot(
    payload: StatementSnapshotCreate,
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> StatementSnapshotRead:
    period = db.get(AccountingPeriod, payload.period_id)
    if not period:
        raise HTTPException(status_code=404, detail="财务期间不存在")

    summary_json = build_statement_summary(db, payload.period_id, payload.report_date)
    snapshot = StatementSnapshot(
        snapshot_no=build_statement_snapshot_no(db, period, payload.statement_type, payload.report_date),
        period_id=payload.period_id,
        statement_type=payload.statement_type,
        report_date=payload.report_date,
        summary_json=summary_json,
        created_by=context.user.id,
    )
    db.add(snapshot)
    db.commit()

    row = db.execute(get_statement_snapshots_query(limit=1).where(StatementSnapshot.id == snapshot.id)).mappings().first()
    if not row:
        raise HTTPException(status_code=500, detail="财务报表快照创建后读取失败")
    return StatementSnapshotRead.model_validate(dict(row))
