from __future__ import annotations
from datetime import date
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
from app.database import get_db
from app.models.accounting import Loan, LoanSchedule
from app.models.audit import AuditLog
from app.services.auth import require_permission
from app.services.accounting import AccountingError
from app.services.loan_service import (
    create_loan, activate_loan, pay_loan_installments, verify_loan, loan_detail,
)

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


class LoanCreate(BaseModel):
    lender_name: str
    lender_type: str = "bank"
    principal: float
    interest_rate_nominal: float = 0
    interest_rate_period: str = "year"
    start_date: date = Field(default_factory=date.today)
    tenure_months: int = 12
    repayment_frequency: str = "monthly"
    branch_id: Optional[str] = None
    account_id: Optional[str] = None
    liability_account_id: Optional[str] = None
    interest_expense_account_id: Optional[str] = None
    notes: Optional[str] = None


class LoanActivateIn(BaseModel):
    method: Optional[str] = None  # cash/bank


class LoanPayIn(BaseModel):
    installment_ids: list[str]
    pay_date: date = Field(default_factory=date.today)
    method: str = "cash"
    branch_id: Optional[str] = None


@router.get("/loans")
def list_loans(status: Optional[str] = None, db: Session = Depends(get_db),
               _=Depends(require_permission("accounting", "view"))):
    q = db.query(Loan).order_by(Loan.created_at.desc())
    if status:
        q = q.filter(Loan.status == status)
    loans = q.all()
    return [loan_detail(db, loan) for loan in loans]


@router.post("/loans", status_code=201)
def create_loan_endpoint(data: LoanCreate, db: Session = Depends(get_db),
                         current_user=Depends(require_permission("accounting", "create"))):
    try:
        loan = create_loan(
            db,
            lender_name=data.lender_name,
            lender_type=data.lender_type,
            principal=data.principal,
            interest_rate_nominal=data.interest_rate_nominal,
            interest_rate_period=data.interest_rate_period,
            start_date=data.start_date,
            tenure_months=data.tenure_months,
            repayment_frequency=data.repayment_frequency,
            branch_id=data.branch_id,
            account_id=data.account_id,
            liability_account_id=data.liability_account_id,
            interest_expense_account_id=data.interest_expense_account_id,
            notes=data.notes,
            created_by=current_user.id,
        )
        db.add(AuditLog(user_id=current_user.id, action="create_loan",
                        module="accounting", record_id=loan.id,
                        new_value=f"{loan.loan_number}: {loan.principal}"))
        db.commit()
    except AccountingError as exc:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(exc))
    return loan_detail(db, loan)


@router.get("/loans/{loan_id}/verify")
def verify_loan_endpoint(loan_id: str, db: Session = Depends(get_db),
                         _=Depends(require_permission("accounting", "view"))):
    try:
        return verify_loan(db, loan_id)
    except AccountingError as exc:
        raise HTTPException(status_code=404, detail=str(exc))


@router.post("/loans/{loan_id}/activate")
def activate_loan_endpoint(loan_id: str, data: LoanActivateIn | None = None,
                           db: Session = Depends(get_db),
                           current_user=Depends(require_permission("accounting", "edit"))):
    try:
        result = activate_loan(db, loan_id, current_user.id,
                               method=data.method if data else None)
    except AccountingError as exc:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(exc))
    db.add(AuditLog(user_id=current_user.id, action="activate_loan",
                    module="accounting", record_id=loan_id))
    db.commit()
    entry = result["entry"]
    return {"loan": loan_detail(db, result["loan"]),
            "journal_entry_id": entry.id,
            "entry_number": entry.entry_number}


@router.post("/loans/{loan_id}/pay")
def pay_loan_endpoint(loan_id: str, data: LoanPayIn, db: Session = Depends(get_db),
                      current_user=Depends(require_permission("accounting", "edit"))):
    try:
        result = pay_loan_installments(
            db, loan_id, current_user.id,
            installment_ids=data.installment_ids,
            pay_date=data.pay_date,
            method=data.method,
            branch_id=data.branch_id,
        )
    except AccountingError as exc:
        db.rollback()
        raise HTTPException(status_code=400, detail=str(exc))
    db.add(AuditLog(user_id=current_user.id, action="pay_loan",
                    module="accounting", record_id=loan_id,
                    new_value=f"paid {result['paid_amount']}"))
    db.commit()
    loan = db.query(Loan).filter(Loan.id == loan_id).first()
    return {"loan": loan_detail(db, loan),
            "paid_amount": result["paid_amount"],
            "remaining_installments": result["remaining_installments"]}


@router.delete("/loans/{loan_id}")
def delete_loan_endpoint(loan_id: str, db: Session = Depends(get_db),
                         current_user=Depends(require_permission("accounting", "delete"))):
    loan = db.query(Loan).filter(Loan.id == loan_id).first()
    if not loan:
        raise HTTPException(404, detail="Loan not found")
    if loan.status != "draft":
        raise HTTPException(400, detail="Only draft loans can be deleted")
    for schedule in db.query(LoanSchedule).filter(LoanSchedule.loan_id == loan.id).all():
        db.delete(schedule)
    db.delete(loan)
    db.add(AuditLog(user_id=current_user.id, action="delete_loan",
                    module="accounting", record_id=loan_id,
                    new_value=loan.loan_number))
    db.commit()
    return {"detail": "Loan deleted"}