from __future__ import annotations

import argparse
from typing import Iterable

from app.api.deps import CurrentUserContext
from app.db.session import get_session_factory
from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim
from app.services.expense_claim_workflow_constants import BUDGET_MANAGER_APPROVAL_STAGE
from app.services.expense_claims import ExpenseClaimService


def _role_codes(employee: Employee) -> list[str]:
    return [
        str(role.role_code or "").strip().lower()
        for role in list(employee.roles or [])
        if str(role.role_code or "").strip()
    ]


def _is_direct_manager(employee: Employee | None, budget_manager: Employee | None) -> bool:
    if employee is None or budget_manager is None:
        return False
    if employee.manager_id and employee.manager_id == budget_manager.id:
        return True
    return employee.manager is not None and employee.manager.id == budget_manager.id


def _budget_manager_context(budget_manager: Employee) -> CurrentUserContext:
    return CurrentUserContext(
        username=str(budget_manager.email or budget_manager.employee_no or budget_manager.name or "").strip(),
        name=str(budget_manager.name or "").strip(),
        role_codes=_role_codes(budget_manager),
        is_admin=False,
        department_name=str(
            budget_manager.organization_unit.name
            if budget_manager.organization_unit is not None
            else ""
        ).strip(),
        grade=str(budget_manager.grade or "").strip(),
        employee_no=str(budget_manager.employee_no or "").strip(),
    )


def _iter_candidates(service: ExpenseClaimService) -> Iterable[tuple[ExpenseClaim, Employee]]:
    claims = service.db.query(ExpenseClaim).filter(
        ExpenseClaim.status == "submitted",
        ExpenseClaim.approval_stage == BUDGET_MANAGER_APPROVAL_STAGE,
    ).all()
    for claim in claims:
        if not service._is_expense_application_claim(claim):
            continue
        budget_manager = service._access_policy.resolve_department_budget_manager(claim)
        if budget_manager is None:
            continue
        if not _is_direct_manager(claim.employee, budget_manager):
            continue
        yield claim, budget_manager


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Repair application claims stuck at budget approval when the direct manager is the budget approver.",
    )
    parser.add_argument("--apply", action="store_true", help="Apply the repair. Without it, only prints candidates.")
    args = parser.parse_args()

    session_factory = get_session_factory()
    with session_factory() as db:
        service = ExpenseClaimService(db)
        candidates = list(_iter_candidates(service))
        print(f"candidates={len(candidates)}")
        for claim, budget_manager in candidates:
            print(
                "candidate "
                f"claim_no={claim.claim_no} "
                f"claim_id={claim.id} "
                f"employee={claim.employee_name} "
                f"budget_manager={budget_manager.name}"
            )
            if not args.apply:
                continue

            repaired = service.approve_claim(
                claim.id,
                _budget_manager_context(budget_manager),
                opinion="历史流程修复：直属领导与预算审批人为同一人，合并预算审批。",
            )
            print(
                "repaired "
                f"claim_no={repaired.claim_no if repaired is not None else claim.claim_no} "
                f"status={repaired.status if repaired is not None else ''} "
                f"stage={repaired.approval_stage if repaired is not None else ''}"
            )


if __name__ == "__main__":
    main()
