from datetime import date
from decimal import Decimal
from typing import Annotated

from fastapi import APIRouter, Depends, Query

from app.application.services.inventory_service import InventoryService
from app.application.services.report_service import ReportService
from app.domain.entities.user_registration import UserRegistration
from app.domain.enums import (
    CustomReportModule,
    DeptReportCompareWith,
    DeptReportSort,
    DeptReportView,
    ReportType,
    StockReportBasis,
    StockReportStatus,
    TxnReportPaymentStatus,
    TxnReportReferenceType,
    TxnReportTrendGranularity,
    TxnReportType,
)
from app.presentation.auth_dependencies import get_current_user
from app.presentation.company_dependencies import get_validated_company_id_query
from app.presentation.dependencies import get_inventory_service, get_report_service
from app.presentation.mappers import to_report_response
from app.presentation.schemas.common import DataTableResponse, MessageResponse
from app.presentation.schemas.inventory import (
    DepartmentIssueDashboardResponse,
    StockReportResponse,
)
from app.presentation.schemas.report import (
    BalanceSheetRequest,
    CustomReportCreate,
    CustomReportMetaResponse,
    CustomReportResponse,
    CustomReportResultResponse,
    CustomReportRunRequest,
    CustomReportUpdate,
    DepartmentReportResponse,
    FinancialReportResponse,
    IncomeStatementRequest,
    LedgerReportRequest,
    TransactionReportResponse,
    TrialBalanceRequest,
)

router = APIRouter(prefix="/reports", tags=["Reports"])

CompanyId = Annotated[str, Depends(get_validated_company_id_query)]


@router.get(
    "/dashboard",
    response_model=DepartmentIssueDashboardResponse,
    summary="Reports Dashboard (Issue to Department)",
)
async def get_reports_dashboard(
    company_id: CompanyId,
    from_date: date = Query(..., description="Period start (inclusive)"),
    to_date: date = Query(..., description="Period end (inclusive)"),
    department_id: str | None = None,
    from_warehouse_id: str | None = None,
    recent_limit: int = Query(10, ge=1, le=50),
    pending_limit: int = Query(10, ge=1, le=50),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """
    Aggregated Reports Dashboard for Issue to Department:
    KPIs with period-over-period change, daily trend, department/warehouse
    breakdowns, recent issues, pending approvals, and quick report links.
    """
    dashboard = await service.get_department_issue_dashboard(
        current_user,
        company_id,
        from_date,
        to_date,
        department_id=department_id,
        from_warehouse_id=from_warehouse_id,
        recent_limit=recent_limit,
        pending_limit=pending_limit,
    )
    return DepartmentIssueDashboardResponse.model_validate(
        dashboard.model_dump() if hasattr(dashboard, "model_dump") else dashboard
    )


@router.get(
    "/stock",
    response_model=StockReportResponse,
    summary="Stock Report",
)
async def get_stock_report(
    company_id: CompanyId,
    from_date: date | None = Query(
        None, description="Period start for opening stock (txns before this date)"
    ),
    to_date: date | None = Query(
        None, description="Period end (UI filter; current stock is as-of now)"
    ),
    report_basis: StockReportBasis = Query(
        StockReportBasis.CURRENT_STOCK,
        description="Report basis (currently only current_stock)",
    ),
    warehouse_id: str | None = Query(None, description="Filter by warehouse"),
    department_id: str | None = Query(
        None,
        description="Accepted for UI parity; not applied (stock is warehouse-scoped)",
    ),
    category_id: str | None = Query(None, description="Filter by item category"),
    item_type_id: str | None = Query(None, description="Filter by item type"),
    search: str | None = Query(
        None, description="Search item name, code (SKU), or barcode"
    ),
    stock_status: StockReportStatus | None = Query(
        None,
        description="in_stock | low_stock | out_of_stock | inactive",
    ),
    min_stock_value: Decimal | None = Query(
        None, description="Minimum stock value (on_hand * average_cost)"
    ),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=200),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """
    Stock Report dashboard data: KPI cards, stock summary table (paginated),
    stock value by warehouse, status overview, and top 5 items by stock value.
    """
    report = await service.get_stock_report(
        current_user,
        company_id,
        from_date=from_date,
        to_date=to_date,
        report_basis=report_basis,
        warehouse_id=warehouse_id,
        department_id=department_id,
        category_id=category_id,
        item_type_id=item_type_id,
        search=search,
        stock_status=stock_status,
        min_stock_value=min_stock_value,
        page=page,
        page_size=page_size,
    )
    return StockReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.get(
    "/transactions",
    response_model=TransactionReportResponse,
    summary="Transaction Reports",
)
async def get_transaction_report(
    company_id: CompanyId,
    from_date: date = Query(..., description="Period start (inclusive)"),
    to_date: date = Query(..., description="Period end (inclusive)"),
    transaction_type: TxnReportType | None = Query(
        None,
        description=(
            "sales_invoice | purchase_bill | payment_received | payment_made | "
            "sales_return | purchase_return | stock_transfer | credit_note"
        ),
    ),
    reference_type: TxnReportReferenceType | None = Query(
        None, description="customer | supplier | receipt | payment | transfer"
    ),
    warehouse_id: str | None = Query(None, description="Filter by warehouse"),
    department_id: str | None = Query(
        None, description="Accepted for UI parity; source documents are warehouse/party scoped"
    ),
    party: str | None = Query(None, description="Search customer or supplier name"),
    created_by_id: str | None = Query(None, description="Filter by creator user id"),
    payment_status: TxnReportPaymentStatus | None = Query(
        None, description="paid | partial | unpaid | n_a"
    ),
    min_amount: Decimal | None = Query(None, description="Minimum total amount"),
    max_amount: Decimal | None = Query(None, description="Maximum total amount"),
    trend_granularity: TxnReportTrendGranularity = Query(
        TxnReportTrendGranularity.DAILY,
        description="daily | weekly | monthly for the trend chart",
    ),
    page: int = Query(1, ge=1),
    page_size: int = Query(10, ge=1, le=200),
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    """
    Transaction Reports dashboard: KPI cards with period-over-period change,
    paginated transaction list, trend, type mix, and payment-status breakdown.
    Aggregates sales invoices, vendor bills, receipts, payments, stock transfers,
    and return/credit-note item transactions.
    """
    report = await service.get_transaction_report(
        current_user,
        company_id,
        from_date,
        to_date,
        transaction_type=transaction_type,
        reference_type=reference_type,
        warehouse_id=warehouse_id,
        department_id=department_id,
        party=party,
        created_by_id=created_by_id,
        payment_status=payment_status,
        min_amount=min_amount,
        max_amount=max_amount,
        trend_granularity=trend_granularity,
        page=page,
        page_size=page_size,
    )
    return TransactionReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.get(
    "/departments",
    response_model=DepartmentReportResponse,
    summary="Department Reports",
)
async def get_department_report(
    company_id: CompanyId,
    from_date: date = Query(..., description="Period start (inclusive)"),
    to_date: date = Query(..., description="Period end (inclusive)"),
    report_view: DeptReportView = Query(
        DeptReportView.SUMMARY, description="summary | detailed"
    ),
    compare_with: DeptReportCompareWith = Query(
        DeptReportCompareWith.PREVIOUS_PERIOD,
        description="Comparison period for KPI and row change %",
    ),
    warehouse_id: str | None = Query(None, description="Filter by warehouse"),
    department_id: str | None = Query(None, description="Filter by department"),
    search: str | None = Query(None, description="Search department name or code"),
    sort_by: DeptReportSort = Query(
        DeptReportSort.TRANSACTIONS,
        description="name | transactions | issued | received | net | total",
    ),
    sort_dir: str = Query("desc", description="asc | desc"),
    page: int = Query(1, ge=1),
    page_size: int = Query(8, ge=1, le=200),
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    """
    Department Reports dashboard: KPI cards with period-over-period change,
    paginated department summary (issued vs received), and chart series.
    Issued comes from department issues; received from purchase orders tagged
    to a department.
    """
    report = await service.get_department_report(
        current_user,
        company_id,
        from_date,
        to_date,
        report_view=report_view,
        compare_with=compare_with,
        warehouse_id=warehouse_id,
        department_id=department_id,
        search=search,
        sort_by=sort_by,
        sort_dir=sort_dir,
        page=page,
        page_size=page_size,
    )
    return DepartmentReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.post("/ledger", response_model=FinancialReportResponse)
async def generate_ledger(
    company_id: CompanyId,
    payload: LedgerReportRequest,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.generate_ledger(
        current_user,
        company_id,
        payload.account_id,
        payload.from_date,
        payload.to_date,
        payload.save,
    )
    return to_report_response(report)


@router.post("/trial-balance", response_model=FinancialReportResponse)
async def generate_trial_balance(
    company_id: CompanyId,
    payload: TrialBalanceRequest,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.generate_trial_balance(
        current_user,
        company_id,
        payload.as_of_date,
        payload.save,
    )
    return to_report_response(report)


@router.post("/income-statement", response_model=FinancialReportResponse)
async def generate_income_statement(
    company_id: CompanyId,
    payload: IncomeStatementRequest,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.generate_income_statement(
        current_user,
        company_id,
        payload.from_date,
        payload.to_date,
        payload.save,
    )
    return to_report_response(report)


@router.post("/balance-sheet", response_model=FinancialReportResponse)
async def generate_balance_sheet(
    company_id: CompanyId,
    payload: BalanceSheetRequest,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.generate_balance_sheet(
        current_user,
        company_id,
        payload.as_of_date,
        payload.save,
    )
    return to_report_response(report)


@router.get(
    "/custom/meta",
    response_model=CustomReportMetaResponse,
    summary="Custom Reports metadata",
)
async def get_custom_report_meta(
    company_id: CompanyId,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    """Available modules, fields, metrics, and quick-report templates."""
    meta = await service.get_custom_report_builder_meta(current_user, company_id)
    templates = []
    for template in meta.get("templates", []):
        item = dict(template)
        config = item.get("configuration")
        if hasattr(config, "model_dump"):
            item["configuration"] = config.model_dump(mode="json")
        templates.append(item)
    payload = {**meta, "templates": templates}
    # Nested domain configs must be dicts before response validation.
    return CustomReportMetaResponse.model_validate(payload)


@router.post(
    "/custom/run",
    response_model=CustomReportResultResponse,
    summary="Run Custom Report",
)
async def run_custom_report(
    company_id: CompanyId,
    payload: CustomReportRunRequest,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    """
    Execute a custom report configuration: grouped table, totals, trend,
    top parties, type mix, and payment-status breakdown.
    """
    from app.domain.entities.report import CustomReportConfig

    config = CustomReportConfig.model_validate(
        payload.configuration.model_dump()
        if hasattr(payload.configuration, "model_dump")
        else payload.configuration
    )
    result = await service.run_custom_report(
        current_user,
        company_id,
        config,
        page=payload.page,
        page_size=payload.page_size,
    )
    return CustomReportResultResponse.model_validate(
        result.model_dump() if hasattr(result, "model_dump") else result
    )


@router.get(
    "/custom",
    response_model=DataTableResponse[CustomReportResponse],
    summary="List saved Custom Reports",
)
async def list_custom_reports(
    company_id: CompanyId,
    module: CustomReportModule | None = None,
    search: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(50, ge=1, le=200),
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    items, total = await service.list_custom_reports(
        current_user,
        company_id,
        module=module,
        search=search,
        page=page,
        page_size=page_size,
    )
    data = [
        CustomReportResponse.model_validate(
            i.model_dump() if hasattr(i, "model_dump") else i
        )
        for i in items
    ]
    return DataTableResponse(
        data=data,
        total=total,
        page=page,
        page_size=page_size,
        total_pages=max(1, (total + page_size - 1) // page_size) if page_size else 1,
    )


@router.post(
    "/custom",
    response_model=CustomReportResponse,
    summary="Save Custom Report",
)
async def create_custom_report(
    company_id: CompanyId,
    payload: CustomReportCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    from app.domain.entities.report import CustomReportConfig

    config = CustomReportConfig.model_validate(
        payload.configuration.model_dump()
        if hasattr(payload.configuration, "model_dump")
        else payload.configuration
    )
    report = await service.create_custom_report(
        current_user,
        company_id,
        payload.name,
        config,
        description=payload.description,
        is_template=payload.is_template,
    )
    return CustomReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.get(
    "/custom/{report_id}",
    response_model=CustomReportResponse,
    summary="Get saved Custom Report",
)
async def get_custom_report(
    company_id: CompanyId,
    report_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.get_custom_report(current_user, company_id, report_id)
    return CustomReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.put(
    "/custom/{report_id}",
    response_model=CustomReportResponse,
    summary="Update saved Custom Report",
)
async def update_custom_report(
    company_id: CompanyId,
    report_id: str,
    payload: CustomReportUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    from app.domain.entities.report import CustomReportConfig

    config = None
    if payload.configuration is not None:
        config = CustomReportConfig.model_validate(
            payload.configuration.model_dump()
            if hasattr(payload.configuration, "model_dump")
            else payload.configuration
        )
    report = await service.update_custom_report(
        current_user,
        company_id,
        report_id,
        name=payload.name,
        description=payload.description,
        configuration=config,
        is_template=payload.is_template,
    )
    return CustomReportResponse.model_validate(
        report.model_dump() if hasattr(report, "model_dump") else report
    )


@router.delete(
    "/custom/{report_id}",
    response_model=MessageResponse,
    summary="Delete saved Custom Report",
)
async def delete_custom_report(
    company_id: CompanyId,
    report_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    await service.delete_custom_report(current_user, company_id, report_id)
    return MessageResponse(message="Custom report deleted")


@router.get("", response_model=list[FinancialReportResponse])
async def list_reports(
    company_id: CompanyId,
    report_type: ReportType | None = None,
    from_date: date | None = None,
    to_date: date | None = None,
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    reports = await service.list_reports(
        current_user,
        company_id,
        report_type,
        from_date,
        to_date,
        skip,
        limit,
    )
    return [to_report_response(report) for report in reports]


@router.get("/{report_id}", response_model=FinancialReportResponse)
async def get_report(
    company_id: CompanyId,
    report_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: ReportService = Depends(get_report_service),
):
    report = await service.get_report(current_user, company_id, report_id)
    return to_report_response(report)
