from fastapi import APIRouter, Depends, Query, status
from sqlalchemy.orm import Session
from typing import Optional, List
from utils.utils import get_db
from . import handler
from .schema import (
    CalculationsessionCreate,
    CalculationsessionUpdate,
    CalculationsessionResponse,
    CalculationCreate,
    CalculationUpdate,
    CalculationResponse,
    CalculationDetailsResponse,
    AmortizationentryCreate,
    AmortizationentryUpdate,
    AmortizationentryResponse,
    LoanCalculationInput,
    SessionTrackInput,
)

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


# CalculationSession routes
@router.post(
    "/sessions",
    response_model=CalculationsessionResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create Calculation Session",
    description="Creates a new calculation session for tracking anonymous user activity. Returns 409 if a session with the same session_id already exists.",
)
def create_session_route(data: CalculationsessionCreate, db: Session = Depends(get_db)):
    return handler.create_calculationsession(db, data)


@router.get(
    "/sessions",
    response_model=List[CalculationsessionResponse],
    summary="List Calculation Sessions",
    description="Returns a paginated list of calculation sessions. Supports optional filtering by session_id.",
)
def list_sessions_route(
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
    session_id: Optional[str] = None,
    db: Session = Depends(get_db),
):
    return handler.list_calculationsessions(db, limit, offset, session_id)


@router.get(
    "/sessions/{id}",
    response_model=CalculationsessionResponse,
    summary="Get Calculation Session",
    description="Retrieves a single calculation session by ID. Returns 404 if the session does not exist.",
)
def get_session_route(id: str, db: Session = Depends(get_db)):
    return handler.get_calculationsession(db, id)


@router.patch(
    "/sessions/{id}",
    response_model=CalculationsessionResponse,
    summary="Update Calculation Session",
    description="Updates an existing calculation session. Only provided fields are updated. Returns 404 if the session does not exist, 409 if session_id conflicts with another session.",
)
def update_session_route(id: str, data: CalculationsessionUpdate, db: Session = Depends(get_db)):
    return handler.update_calculationsession(db, id, data)


@router.delete(
    "/sessions/{id}",
    status_code=status.HTTP_200_OK,
    summary="Delete Calculation Session",
    description="Deletes a calculation session and all associated calculations and amortization entries. Returns 404 if the session does not exist.",
)
def delete_session_route(id: str, db: Session = Depends(get_db)):
    return handler.delete_calculationsession(db, id)


# Calculation routes
@router.post(
    "/calculations",
    response_model=CalculationResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create Calculation",
    description="Creates a new calculation record. Validates that the referenced session exists. Returns 404 if session not found.",
)
def create_calculation_route(data: CalculationCreate, db: Session = Depends(get_db)):
    return handler.create_calculation(db, data)


@router.get(
    "/calculations",
    response_model=List[CalculationResponse],
    summary="List Calculations",
    description="Returns a paginated list of calculations. Supports optional filtering by session_id to retrieve calculations for a specific session.",
)
def list_calculations_route(
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
    session_id: Optional[str] = None,
    db: Session = Depends(get_db),
):
    return handler.list_calculations(db, limit, offset, session_id)


@router.get(
    "/calculations/{id}",
    response_model=CalculationResponse,
    summary="Get Calculation",
    description="Retrieves a single calculation by ID. Returns 404 if the calculation does not exist.",
)
def get_calculation_route(id: str, db: Session = Depends(get_db)):
    return handler.get_calculation(db, id)


@router.patch(
    "/calculations/{id}",
    response_model=CalculationResponse,
    summary="Update Calculation",
    description="Updates an existing calculation. Only provided fields are updated. Validates that any new session_id exists. Returns 404 if calculation or session not found.",
)
def update_calculation_route(id: str, data: CalculationUpdate, db: Session = Depends(get_db)):
    return handler.update_calculation(db, id, data)


@router.delete(
    "/calculations/{id}",
    status_code=status.HTTP_200_OK,
    summary="Delete Calculation",
    description="Deletes a calculation and all associated amortization entries. Returns 404 if the calculation does not exist.",
)
def delete_calculation_route(id: str, db: Session = Depends(get_db)):
    return handler.delete_calculation(db, id)


# AmortizationEntry routes
@router.post(
    "/amortization-entries",
    response_model=AmortizationentryResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create Amortization Entry",
    description="Creates a new amortization entry. Validates that the referenced calculation exists. Returns 404 if calculation not found.",
)
def create_amortization_route(data: AmortizationentryCreate, db: Session = Depends(get_db)):
    return handler.create_amortizationentry(db, data)


@router.get(
    "/amortization-entries",
    response_model=List[AmortizationentryResponse],
    summary="List Amortization Entries",
    description="Returns a paginated list of amortization entries. Supports optional filtering by calculation_id to retrieve entries for a specific calculation.",
)
def list_amortization_route(
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
    calculation_id: Optional[str] = None,
    db: Session = Depends(get_db),
):
    return handler.list_amortizationentries(db, limit, offset, calculation_id)


@router.get(
    "/amortization-entries/{id}",
    response_model=AmortizationentryResponse,
    summary="Get Amortization Entry",
    description="Retrieves a single amortization entry by ID. Returns 404 if the entry does not exist.",
)
def get_amortization_route(id: str, db: Session = Depends(get_db)):
    return handler.get_amortizationentry(db, id)


@router.patch(
    "/amortization-entries/{id}",
    response_model=AmortizationentryResponse,
    summary="Update Amortization Entry",
    description="Updates an existing amortization entry. Only provided fields are updated. Validates that any new calculation_id exists. Returns 404 if entry or calculation not found.",
)
def update_amortization_route(id: str, data: AmortizationentryUpdate, db: Session = Depends(get_db)):
    return handler.update_amortizationentry(db, id, data)


@router.delete(
    "/amortization-entries/{id}",
    status_code=status.HTTP_200_OK,
    summary="Delete Amortization Entry",
    description="Deletes an amortization entry. Returns 404 if the entry does not exist.",
)
def delete_amortization_route(id: str, db: Session = Depends(get_db)):
    return handler.delete_amortizationentry(db, id)


# Workflow routes
@router.post(
    "/calculations/calculate",
    response_model=CalculationDetailsResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Perform Loan Calculation",
    description="Performs a complete loan calculation including monthly payment, total interest, and generates the full amortization schedule. Validates all inputs according to business rules, creates calculation and all amortization entries in a single transaction, updates session calculation count, and returns the calculation with complete schedule. Returns 404 if session not found, 400 for validation errors.",
)
def calculate_loan_route(data: LoanCalculationInput, db: Session = Depends(get_db)):
    return handler.calculate_loan(db, data)


@router.get(
    "/calculations/{id}/details",
    response_model=CalculationDetailsResponse,
    summary="Get Calculation With Details",
    description="Retrieves a calculation with its complete amortization schedule. Returns the calculation record along with all associated amortization entries ordered by payment number. Returns 404 if the calculation does not exist.",
)
def get_calculation_details_route(id: str, db: Session = Depends(get_db)):
    return handler.get_calculation_details(db, id)


@router.get(
    "/calculations/{id}/amortization",
    response_model=List[AmortizationentryResponse],
    summary="Get Amortization Schedule",
    description="Retrieves the complete amortization schedule for a calculation. Returns an ordered list of all amortization entries showing payment breakdown for each period. Returns 404 if the calculation does not exist.",
)
def get_amortization_schedule_route(id: str, db: Session = Depends(get_db)):
    return handler.get_amortization_schedule(db, id)


@router.post(
    "/sessions/track",
    response_model=CalculationsessionResponse,
    summary="Track Session",
    description="Creates a new session on first visit or updates an existing session's last access time. Used for tracking anonymous user activity and analytics. Accepts session_id, user_agent, and anonymized ip_address. Returns the created or updated session record.",
)
def track_session_route(data: SessionTrackInput, db: Session = Depends(get_db)):
    return handler.track_session(db, data)


@router.get(
    "/sessions/{session_id}/calculations",
    response_model=List[CalculationResponse],
    summary="Get Session Calculations",
    description="Retrieves all calculations associated with a specific session. Returns a paginated list of calculations ordered by creation date. Returns 404 if the session does not exist.",
)
def get_session_calculations_route(
    session_id: str,
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
    db: Session = Depends(get_db),
):
    return handler.get_session_calculations(db, session_id, limit, offset)