from __future__ import annotations

from sqlite3 import Connection

from fastapi import APIRouter, Depends, HTTPException, Query

from jarvis_finance.api.dependencies import get_db
from jarvis_finance.api.schemas.overview import PortfolioSummary
from jarvis_finance.api.schemas.performance_activation import (
    DailyValuationJobStatus,
    PerformanceBackfillConfirmRequest,
    PerformanceBackfillConfirmResponse,
    PerformanceBackfillPreviewRequest,
    PerformanceBackfillPreviewResponse,
    PerformanceReclassificationPreviewRequest,
    PerformanceReclassificationPreviewResponse,
    PerformanceSetupResponse,
    PerformanceSourceActivationConfirmRequest,
    PerformanceSourceActivationConfirmResponse,
    PerformanceSourceActivationPreviewRequest,
    PerformanceSourceActivationPreviewResponse,
    TrueWealthActivationPackagePreviewRequest,
    TrueWealthActivationPackagePreviewResponse,
    TrueWealthBankPaymentConfirmRequest,
    TrueWealthBankPaymentConfirmResponse,
    TrueWealthBankPaymentPreviewRequest,
    TrueWealthBankPaymentPreviewResponse,
    TrueWealthCashflowConfirmRequest,
    TrueWealthCashflowConfirmResponse,
    TrueWealthCashflowPreviewRequest,
    TrueWealthCashflowPreviewResponse,
    TrueWealthModelPreviewResponse,
    TrueWealthPerformanceViewResponse,
    TrueWealthRecipientRuleActivationRequest,
    TrueWealthRecipientRuleActivationResponse,
)
from jarvis_finance.api.schemas.portfolio_advisor import PortfolioAdvisorSnapshot
from jarvis_finance.api.schemas.portfolio_data import (
    DataSourcesResponse,
    IngestionConfirmRequest,
    IngestionConfirmResponse,
    IngestionHistoryResponse,
    IngestionPreviewRequest,
    IngestionPreviewResponse,
    ReconciliationResponse,
)
from jarvis_finance.api.schemas.portfolio_performance import (
    PerformanceCoverageResponse,
    PortfolioPerformanceResponse,
)
from jarvis_finance.api.schemas.portfolio_policy import (
    ActivePortfolioPolicyResponse,
    PolicyConfirmRequest,
    PolicyConfirmResponse,
    PolicyEvaluationResponse,
    PolicyHistoryItem,
    PolicyPreviewRequest,
    PolicyPreviewResponse,
    PortfolioPolicyResponse,
)
from jarvis_finance.api.schemas.reconciliation import ReconciliationSnapshotResponse
from jarvis_finance.api.schemas.wealth_cockpit import WealthCockpitResponse
from jarvis_finance.services.finance_command_center import build_finance_command_center
from jarvis_finance.services.performance_activation import (
    build_daily_valuation_job_status,
    confirm_performance_backfill,
    confirm_performance_source_activation,
    preview_performance_backfill,
    preview_performance_source_activation,
)
from jarvis_finance.services.performance_hardening import (
    build_activation_setup_overview,
    build_postfinance_component_preview,
    confirm_truewealth_cashflow_period,
    preview_performance_reclassification,
    preview_truewealth_cashflow_period,
)
from jarvis_finance.services.portfolio_advisor import get_portfolio_advisor_snapshot
from jarvis_finance.services.portfolio_analytics import build_portfolio_analytics
from jarvis_finance.services.portfolio_data import (
    build_portfolio_reconciliation,
    confirm_ingestion,
    ingestion_history,
    list_data_sources,
    preview_ingestion,
)
from jarvis_finance.services.portfolio_performance import (
    build_performance_coverage,
    build_portfolio_performance,
)
from jarvis_finance.services.portfolio_policy import (
    active_policy,
    confirm_policy,
    evaluate_policy,
    policy_detail,
    policy_history,
    preview_policy,
)
from jarvis_finance.services.portfolio_service import get_overview
from jarvis_finance.services.reconciliation_snapshot import build_reconciliation_snapshot
from jarvis_finance.services.truewealth_productization import (
    activate_truewealth_recipient_rule,
    confirm_truewealth_bank_payments,
    preview_truewealth_bank_payments,
)
from jarvis_finance.services.truewealth_valuation import (
    build_truewealth_activation_package_preview,
    build_truewealth_model_preview,
    build_truewealth_performance_view,
    public_truewealth_model_preview,
)
from jarvis_finance.services.wealth_cockpit import build_wealth_cockpit

router = APIRouter(tags=["overview"])


@router.get("/overview/finance-command-center")
def finance_command_center(month: str | None = None, conn: Connection = Depends(get_db)) -> dict:
    try:
        return build_finance_command_center(conn, month=month)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/overview", response_model=PortfolioSummary)
def overview(conn: Connection = Depends(get_db)) -> PortfolioSummary:
    return get_overview(conn)


@router.get("/portfolio/wealth-cockpit", response_model=WealthCockpitResponse)
def wealth_cockpit(
    period: str = Query(default="ytd", pattern="^(ytd|previous_year|12m|all)$"),
    as_of: str | None = None,
    data_cutoff: str | None = None,
    conn: Connection = Depends(get_db),
) -> dict:
    """Read-only household wealth view composed exclusively from stored canonical data."""
    try:
        return build_wealth_cockpit(
            conn,
            period=period,
            as_of=as_of,
            data_cutoff=data_cutoff,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/portfolio/reconciliation-snapshot", response_model=ReconciliationSnapshotResponse)
def reconciliation_snapshot(conn: Connection = Depends(get_db)) -> ReconciliationSnapshotResponse:
    """Read-only local quality view; it never imports, corrects, or confirms data."""
    return ReconciliationSnapshotResponse.model_validate(build_reconciliation_snapshot(conn))


@router.get("/portfolio/data-sources", response_model=DataSourcesResponse)
def portfolio_data_sources(conn: Connection = Depends(get_db)) -> dict:
    """Read source availability and redacted ingestion status without fetching providers."""
    return list_data_sources(conn)


@router.post("/portfolio/ingestion/preview", response_model=IngestionPreviewResponse)
def portfolio_ingestion_preview(request: IngestionPreviewRequest, conn: Connection = Depends(get_db)) -> dict:
    """Pure preview over already-normalized local records; no source is fetched or mutated."""
    try:
        return preview_ingestion(conn, request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/ingestion/confirm",
    response_model=IngestionConfirmResponse,
    responses={409: {"description": "Abgelaufene oder veränderte Preview"}},
)
def portfolio_ingestion_confirm(request: IngestionConfirmRequest, conn: Connection = Depends(get_db)) -> dict:
    """The only portfolio-ingestion write path: atomic, idempotent and audited."""
    try:
        return confirm_ingestion(conn, request.model_dump())
    except ValueError as exc:
        message = str(exc)
        status = 409 if any(word in message.lower() for word in ("abgelaufen", "veraltet", "verändert", "ausgangsrevision", "wiederverwendet")) else 400
        raise HTTPException(status_code=status, detail=message) from exc


@router.get("/portfolio/ingestion/history", response_model=IngestionHistoryResponse)
def portfolio_ingestion_history(
    limit: int = Query(default=50, ge=1, le=100),
    offset: int = Query(default=0, ge=0),
    conn: Connection = Depends(get_db),
) -> dict:
    return ingestion_history(conn, limit=limit, offset=offset)


@router.get("/portfolio/reconciliation", response_model=ReconciliationResponse)
def portfolio_reconciliation(
    as_of: str,
    data_cutoff: str | None = None,
    account_id: str | None = None,
    base_currency: str = "CHF",
    absolute_tolerance: str = "0.01",
    relative_tolerance: str = "0.001",
    limit: int = Query(default=200, ge=1, le=500),
    offset: int = Query(default=0, ge=0),
    conn: Connection = Depends(get_db),
) -> dict:
    """Read-only reproducible quantity, valuation, account-total and coverage reconciliation."""
    try:
        return build_portfolio_reconciliation(
            conn, as_of=as_of, data_cutoff=data_cutoff, account_id=account_id,
            base_currency=base_currency, absolute_tolerance=absolute_tolerance,
            relative_tolerance=relative_tolerance, limit=limit, offset=offset,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/portfolio/performance/coverage", response_model=PerformanceCoverageResponse)
def portfolio_performance_coverage(
    from_date: str | None = Query(default=None, alias="from"),
    to_date: str | None = Query(default=None, alias="to"),
    conn: Connection = Depends(get_db),
) -> dict:
    """Role-bound coverage matrix; never estimates unavailable performance."""
    if (from_date is None) != (to_date is None):
        raise HTTPException(status_code=400, detail="Performance coverage requires both from and to")
    try:
        return build_performance_coverage(conn, from_date=from_date, to_date=to_date)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/portfolio/performance/daily-job", response_model=DailyValuationJobStatus)
def portfolio_daily_valuation_job(conn: Connection = Depends(get_db)) -> dict:
    """Read-only scheduler/run diagnosis; never invokes a provider or writes a snapshot."""
    return build_daily_valuation_job_status(conn)


@router.get("/portfolio/performance/setup", response_model=PerformanceSetupResponse)
def portfolio_performance_setup(conn: Connection = Depends(get_db)) -> dict:
    """Single compact read-only activation setup projection."""
    return build_activation_setup_overview(conn)


@router.get("/portfolio/performance/postfinance-components")
def portfolio_postfinance_component_preview(
    day: str,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return build_postfinance_component_preview(conn, day=day)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/reclassification/preview",
    response_model=PerformanceReclassificationPreviewResponse,
)
def portfolio_performance_reclassification_preview(
    request: PerformanceReclassificationPreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return preview_performance_reclassification(conn, **request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/truewealth-cashflows/preview",
    response_model=TrueWealthCashflowPreviewResponse,
)
def truewealth_cashflow_period_preview(
    request: TrueWealthCashflowPreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return preview_truewealth_cashflow_period(conn, **request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/truewealth-cashflows/confirm",
    response_model=TrueWealthCashflowConfirmResponse,
    responses={409: {"description": "Preview changed or confirmation id reused"}},
)
def truewealth_cashflow_period_confirm(
    request: TrueWealthCashflowConfirmRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return confirm_truewealth_cashflow_period(conn, request.model_dump())
    except ValueError as exc:
        message = str(exc)
        status = 409 if any(
            word in message.lower()
            for word in ("stale", "different payload", "already used")
        ) else 400
        raise HTTPException(status_code=status, detail=message) from exc


@router.get(
    "/portfolio/performance/truewealth/view",
    response_model=TrueWealthPerformanceViewResponse,
)
def truewealth_performance_view(
    requested_from: str,
    requested_to: str,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return build_truewealth_performance_view(
            conn, requested_from=requested_from, requested_to=requested_to
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get(
    "/portfolio/performance/truewealth/model-preview",
    response_model=TrueWealthModelPreviewResponse,
)
def truewealth_model_preview(as_of: str, conn: Connection = Depends(get_db)) -> dict:
    try:
        return public_truewealth_model_preview(build_truewealth_model_preview(conn, as_of=as_of))
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/truewealth/activation-package-preview",
    response_model=TrueWealthActivationPackagePreviewResponse,
)
def truewealth_activation_package_preview(
    request: TrueWealthActivationPackagePreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        payload = request.model_dump()
        bank = preview_truewealth_bank_payments(
            conn,
            **{
                key: payload[key]
                for key in (
                    "period_from", "period_to", "recipient_name",
                    "recipient_account_identity", "counterparty_hashes",
                    "reference_tokens", "collision_decisions", "attestation",
                )
            },
        )
        return build_truewealth_activation_package_preview(
            conn,
            bank_preview=bank,
            performance_from=payload["performance_from"],
            performance_to=payload["performance_to"],
            model_as_of=payload["model_as_of"],
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/truewealth-bank-payments/preview",
    response_model=TrueWealthBankPaymentPreviewResponse,
)
def truewealth_bank_payment_preview(
    request: TrueWealthBankPaymentPreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Read only: scan already imported Raiffeisen rows; never fetch or mutate."""
    try:
        return preview_truewealth_bank_payments(conn, **request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/truewealth-bank-payments/confirm",
    response_model=TrueWealthBankPaymentConfirmResponse,
    responses={409: {"description": "Preview changed or confirmation id reused"}},
)
def truewealth_bank_payment_confirm(
    request: TrueWealthBankPaymentConfirmRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    try:
        return confirm_truewealth_bank_payments(conn, request.model_dump())
    except ValueError as exc:
        message = str(exc)
        status = 409 if any(word in message.lower() for word in ("stale", "already used", "became stale")) else 400
        raise HTTPException(status_code=status, detail=message) from exc


@router.post(
    "/portfolio/performance/truewealth-recipient-rule/activate",
    response_model=TrueWealthRecipientRuleActivationResponse,
)
def truewealth_recipient_rule_activate(
    request: TrueWealthRecipientRuleActivationRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Separate rule activation boundary; historical confirmation never calls it."""
    try:
        return activate_truewealth_recipient_rule(conn, request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/activation/preview",
    response_model=PerformanceSourceActivationPreviewResponse,
)
def portfolio_performance_activation_preview(
    request: PerformanceSourceActivationPreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Read-only source-contract preview; it never creates scopes, coverage or values."""
    try:
        return preview_performance_source_activation(conn, **request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/activation/confirm",
    response_model=PerformanceSourceActivationConfirmResponse,
    responses={409: {"description": "Preview changed or expired"}},
)
def portfolio_performance_activation_confirm(
    request: PerformanceSourceActivationConfirmRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Explicit, fingerprint-bound source activation without financial snapshot writes."""
    try:
        return confirm_performance_source_activation(conn, request.model_dump())
    except ValueError as exc:
        status = 409 if "stale" in str(exc).lower() else 400
        raise HTTPException(status_code=status, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/backfill/preview",
    response_model=PerformanceBackfillPreviewResponse,
)
def portfolio_performance_backfill_preview(
    request: PerformanceBackfillPreviewRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Pure local preview; provider calls and financial writes are forbidden here."""
    try:
        return preview_performance_backfill(conn, **request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.post(
    "/portfolio/performance/backfill/confirm",
    response_model=PerformanceBackfillConfirmResponse,
    responses={409: {"description": "Preview changed or expired"}},
)
def portfolio_performance_backfill_confirm(
    request: PerformanceBackfillConfirmRequest,
    conn: Connection = Depends(get_db),
) -> dict:
    """Explicit, fingerprint-bound, audited and idempotent backfill write boundary."""
    try:
        return confirm_performance_backfill(conn, request.model_dump())
    except ValueError as exc:
        status = 409 if "stale" in str(exc).lower() else 400
        raise HTTPException(status_code=status, detail=str(exc)) from exc


@router.get(
    "/portfolio/performance",
    response_model=PortfolioPerformanceResponse,
    responses={400: {"description": "Ungültiger Performance-Zeitraum oder Parameter"}},
)
def portfolio_performance(
    from_date: str = Query(alias="from"),
    to_date: str = Query(alias="to"),
    method: str = Query(default="both"),
    account_id: str | None = None,
    scope: str = Query(default="portfolio", pattern="^(portfolio|postfinance|truewealth|crypto)$"),
    base_currency: str = Query(default="CHF"),
    data_cutoff: str | None = None,
    conn: Connection = Depends(get_db),
) -> dict:
    """Pure read-only calculation from stored activities and versioned snapshots."""

    try:
        if account_id is not None and scope != "portfolio":
            raise ValueError("Konto und fachlicher Scope dürfen nicht gleichzeitig gewählt werden")
        role_map = {
            "postfinance": ("postfinance_etrading_depot", "postfinance_etrading_cash"),
            "truewealth": ("canonical_truewealth_total_value",),
            "crypto": ("crypto_portfolio",),
        }
        selected_account_ids: list[str] | None = None
        if scope != "portfolio":
            roles = role_map[scope]
            placeholders = ",".join("?" for _ in roles)
            selected_account_ids = [
                str(row[0])
                for row in conn.execute(
                    f"""SELECT account_id FROM performance_scope_classifications
                        WHERE included=1 AND decision_version='investment_performance_scope_v1'
                          AND classification_role IN ({placeholders})
                        ORDER BY account_id""",
                    roles,
                ).fetchall()
            ]
        return build_portfolio_performance(
            conn,
            from_date=from_date,
            to_date=to_date,
            method=method,
            account_id=account_id,
            selected_account_ids=selected_account_ids,
            base_currency=base_currency,
            data_cutoff=data_cutoff,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/portfolio/analytics")
def portfolio_analytics(
    period: str = Query(default="1y", pattern="^(1m|3m|6m|1y|2y)$"),
    conn: Connection = Depends(get_db),
) -> dict:
    """Read-only market/FX, benchmark and risk projection from stored confirmed snapshots."""
    return build_portfolio_analytics(conn, period=period)


@router.get("/portfolio/policy", response_model=ActivePortfolioPolicyResponse)
def portfolio_policy(conn: Connection = Depends(get_db)) -> dict:
    return active_policy(conn)


@router.get("/portfolio/policy/history", response_model=list[PolicyHistoryItem])
def portfolio_policy_history(conn: Connection = Depends(get_db)) -> list[dict]:
    return policy_history(conn)


@router.post("/portfolio/policy/preview", response_model=PolicyPreviewResponse)
def portfolio_policy_preview(request: PolicyPreviewRequest, conn: Connection = Depends(get_db)) -> dict:
    return preview_policy(conn, request.model_dump())


@router.post("/portfolio/policy/confirm", response_model=PolicyConfirmResponse)
def portfolio_policy_confirm(request: PolicyConfirmRequest, conn: Connection = Depends(get_db)) -> dict:
    try:
        return confirm_policy(conn, request.model_dump())
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/portfolio/policy/evaluation", response_model=PolicyEvaluationResponse)
def portfolio_policy_evaluation(conn: Connection = Depends(get_db)) -> dict:
    return evaluate_policy(conn)


@router.get(
    "/portfolio/policy/{policy_id}",
    response_model=PortfolioPolicyResponse,
    responses={404: {"description": "Policy-Version nicht gefunden"}},
)
def portfolio_policy_detail(policy_id: str, conn: Connection = Depends(get_db)) -> dict:
    policy = policy_detail(conn, policy_id)
    if policy is None:
        raise HTTPException(status_code=404, detail="Policy-Version nicht gefunden")
    return policy


@router.get("/portfolio/advisor", response_model=PortfolioAdvisorSnapshot)
def portfolio_advisor(conn: Connection = Depends(get_db)) -> PortfolioAdvisorSnapshot:
    return get_portfolio_advisor_snapshot(conn)
