"""
Checkout API router — merchant-authenticated endpoints.

ROUTE ORDERING: Fixed paths BEFORE /{checkout_literal} to prevent capture conflicts:
  GET  /checkouts/summary
  GET  /checkouts/next-number
  GET  /checkouts/analytics/summary
  GET  /checkouts/analytics/report
  GET  /checkouts/analytics/export
  POST /checkouts/bulk-actions
  GET  /checkouts
  POST /checkouts
  GET  /checkouts/{checkout_literal}
  ...
"""
from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

import csv
import io

from fastapi import APIRouter, Depends, Path, Query
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

logger = logging.getLogger(__name__)

from src.core.database import get_db
from src.core.exceptions import NotFoundError, APIException
from src.apps.auth.utils.auth import get_current_merchant
from src.apps.checkouts import crud as checkout_crud
from src.apps.checkouts import services as checkout_services
from src.apps.checkouts.schemas.checkout_schemas import (
    BulkActionRequest,
    CheckoutAnalyticsResponse,
    CheckoutListItemSchema,
    CheckoutResponse,
    CheckoutSettingsResponse,
    CheckoutSettingsUpdate,
    CheckoutSummaryResponse,
    CreateCheckoutRequest,
    ShareCheckoutRequest,
    UpdateCheckoutRequest,
)

router = APIRouter()

_CHECKOUT_LITERAL = Path(
    ...,
    pattern=r"^CHK\d{1,17}$",
    max_length=20,
    description="Checkout literal identifier, e.g. CHK000001",
)


def _get_checkout_or_404(db: Session, literal: str, merchant_id: int):
    checkout = checkout_crud.get_checkout_by_literal(db, literal, merchant_id)
    if checkout is None:
        raise NotFoundError(message=f"Checkout '{literal}' not found.")
    return checkout


# ─── Fixed paths first ────────────────────────────────────────────────────────

@router.get("/summary", response_model=CheckoutSummaryResponse)
async def get_checkout_summary(
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    return checkout_crud.get_checkout_summary(db, current_merchant.id)


@router.get("/next-number")
async def get_next_checkout_number(
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    return {"next_literal": checkout_crud.generate_checkout_literal(db)}


@router.get("/analytics/summary")
async def get_analytics_summary(
    period: str = Query("30d"),
    from_date: Optional[str] = Query(None, alias="from"),
    to_date: Optional[str] = Query(None, alias="to"),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    return {
        "period": period,
        "total_checkouts_active": checkout_crud.get_checkout_summary(db, current_merchant.id)["active_count"],
        "total_collected": 0.0,
        "total_link_views": checkout_crud.get_checkout_summary(db, current_merchant.id)["total_link_views"],
        "overall_conversion_rate": 0.0,
    }


@router.get("/analytics/report")
async def get_analytics_report(
    period: str = Query("30d"),
    from_date: Optional[str] = Query(None, alias="from"),
    to_date: Optional[str] = Query(None, alias="to"),
    search: Optional[str] = Query(None, description="Search by checkout title"),
    checkout_type: Optional[List[str]] = Query(None, description="Filter by checkout type (MERCHANT_DEFINED, PAYER_DEFINED)"),
    status: Optional[List[str]] = Query(None, description="Filter by status (ACTIVE, INACTIVE, DRAFT, EXPIRED)"),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    """Per-checkout analytics breakdown for the Checkouts report page."""
    from datetime import datetime as dt
    parsed_from = None
    parsed_to = None
    if from_date:
        try:
            parsed_from = dt.strptime(from_date, "%Y-%m-%d")
        except ValueError:
            pass
    if to_date:
        try:
            parsed_to = dt.strptime(to_date, "%Y-%m-%d")
        except ValueError:
            pass

    rows_db, _ = checkout_crud.list_checkouts(
        db, current_merchant.id,
        q=search,
        from_date=parsed_from,
        to_date=parsed_to,
        statuses=status,
        checkout_types=checkout_type,
        page_size=200,
    )
    rows = []
    for c in rows_db:
        analytics = checkout_crud.get_checkout_analytics(db, c.id, current_merchant.id)
        rows.append({
            "checkout_literal": c.checkout_literal,
            "title": c.title,
            "checkout_type": c.checkout_type,
            "status": c.status,
            "views": analytics["link_views"],
            "transactions": analytics["transaction_count"],
            "total_collected": analytics["total_collected"],
            "conversion_rate": analytics["conversion_rate"],
            "unique_payers": analytics["unique_payers"],
        })
    return {"rows": rows, "total": len(rows)}


@router.get("/analytics/export")
async def export_analytics(
    period: str = Query("30d"),
    from_date: Optional[str] = Query(None, alias="from"),
    to_date: Optional[str] = Query(None, alias="to"),
    search: Optional[str] = Query(None, description="Search by checkout title"),
    checkout_type: Optional[List[str]] = Query(None, description="Filter by checkout type"),
    status: Optional[List[str]] = Query(None, description="Filter by status"),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    """CSV export of per-checkout analytics."""
    from datetime import datetime as dt
    parsed_from = None
    parsed_to = None
    if from_date:
        try:
            parsed_from = dt.strptime(from_date, "%Y-%m-%d")
        except ValueError:
            pass
    if to_date:
        try:
            parsed_to = dt.strptime(to_date, "%Y-%m-%d")
        except ValueError:
            pass

    rows_db, _ = checkout_crud.list_checkouts(
        db, current_merchant.id,
        q=search,
        from_date=parsed_from,
        to_date=parsed_to,
        statuses=status,
        checkout_types=checkout_type,
        page_size=500,
    )
    fieldnames = [
        "Title", "Type", "Status", "Views",
        "Transactions", "Total Collected ($)", "Conversion Rate (%)", "Unique Payers",
    ]
    output = io.StringIO()
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    for c in rows_db:
        analytics = checkout_crud.get_checkout_analytics(db, c.id, current_merchant.id)
        writer.writerow({
            "Title": c.title,
            "Type": "Merchant Defined" if c.checkout_type == "MERCHANT_DEFINED" else "Payer Defined",
            "Status": c.status,
            "Views": analytics["link_views"],
            "Transactions": analytics["transaction_count"],
            "Total Collected ($)": f"{analytics['total_collected']:.2f}",
            "Conversion Rate (%)": f"{analytics['conversion_rate']:.1f}",
            "Unique Payers": analytics["unique_payers"],
        })
    output.seek(0)
    return StreamingResponse(
        iter([output.getvalue()]),
        media_type="text/csv",
        headers={"Content-Disposition": "attachment; filename=checkouts-report.csv"},
    )


@router.post("/bulk-actions")
async def bulk_actions(
    payload: BulkActionRequest,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkouts = checkout_crud.get_checkouts_by_literals(db, payload.checkout_literals, current_merchant.id)
    affected = []
    merchant = current_merchant

    for checkout in checkouts:
        try:
            if payload.action == "activate":
                checkout_services.activate_checkout(db, checkout, merchant.id, merchant.user_id if hasattr(merchant, "user_id") else None)
            elif payload.action == "deactivate":
                checkout_services.deactivate_checkout(db, checkout, merchant.id)
            elif payload.action == "delete":
                checkout_crud.soft_delete_checkout(db, checkout)
            affected.append(checkout.checkout_literal)
        except Exception as e:
            logger.warning("Bulk action %s failed for %s: %s", payload.action, checkout.checkout_literal, e)

    db.commit()
    return {"affected": affected, "action": payload.action}


# ─── List / Create ────────────────────────────────────────────────────────────

@router.get("")
async def list_checkouts(
    status: Optional[str] = Query(None),
    checkout_type: Optional[str] = Query(None),
    q: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    rows, total = checkout_crud.list_checkouts(
        db, current_merchant.id,
        status=status, checkout_type=checkout_type, q=q,
        page=page, page_size=page_size,
    )
    items = []
    for c in rows:
        active_link = next((l for l in (c.links or []) if l.status == "ACTIVE"), None)
        items.append({
            "id": c.id,
            "checkout_literal": c.checkout_literal,
            "title": c.title,
            "checkout_type": c.checkout_type,
            "status": c.status,
            "amount": c.max_amount if c.checkout_type == "PAYER_DEFINED" else c.amount,
            "max_amount": c.max_amount if c.checkout_type == "PAYER_DEFINED" else None,
            "currency": c.currency,
            "active_link_token": active_link.token if active_link else None,
            "click_count": active_link.click_count if active_link else 0,
            "created_at": c.created_at,
        })
    return {"items": items, "total": total, "page": page, "page_size": page_size}


@router.post("", status_code=201)
async def create_checkout(
    payload: CreateCheckoutRequest,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    data = payload.model_dump(exclude={"is_draft"})
    # is_draft overrides status if explicitly provided
    if payload.is_draft is True:
        data["status"] = "DRAFT"
    elif payload.is_draft is False:
        data["status"] = "ACTIVE"
    checkout = checkout_services.create_checkout(db, data, current_merchant.id)
    db.commit()
    db.refresh(checkout)
    return _build_checkout_response(db, checkout)


# ─── Per-checkout endpoints ───────────────────────────────────────────────────

@router.get("/{checkout_literal}")
async def get_checkout(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    return _build_checkout_response(db, checkout)


@router.put("/{checkout_literal}")
async def update_checkout(
    payload: UpdateCheckoutRequest,
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    data = payload.model_dump(exclude={"regenerate_link", "is_draft"}, exclude_unset=True)
    regenerate_link = payload.regenerate_link
    # is_draft overrides status if explicitly provided
    if payload.is_draft is True:
        data["status"] = "DRAFT"
    elif payload.is_draft is False:
        data["status"] = "ACTIVE"

    checkout_crud.update_checkout(db, checkout, data)

    if regenerate_link and checkout.status == "ACTIVE":
        checkout_services.regenerate_link(db, checkout, current_merchant.id)

    db.commit()
    db.refresh(checkout)
    return _build_checkout_response(db, checkout)


@router.delete("/{checkout_literal}", status_code=204)
async def delete_checkout(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    # Revoke active link on delete
    active_link = checkout_crud.get_active_link(db, checkout.id)
    if active_link:
        checkout_crud.revoke_checkout_link(db, active_link)
    checkout_crud.soft_delete_checkout(db, checkout)
    db.commit()


@router.post("/{checkout_literal}/activate")
async def activate_checkout(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    checkout_services.activate_checkout(db, checkout, current_merchant.id)
    db.commit()
    db.refresh(checkout)
    return _build_checkout_response(db, checkout)


@router.post("/{checkout_literal}/deactivate")
async def deactivate_checkout(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    checkout_services.deactivate_checkout(db, checkout, current_merchant.id)
    db.commit()
    db.refresh(checkout)
    return _build_checkout_response(db, checkout)


@router.post("/{checkout_literal}/duplicate")
async def duplicate_checkout(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    new_checkout = checkout_services.duplicate_checkout(db, checkout, current_merchant.id)
    db.commit()
    db.refresh(new_checkout)
    return _build_checkout_response(db, new_checkout)


@router.post("/{checkout_literal}/link/regenerate")
async def regenerate_link(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    new_link = checkout_services.regenerate_link(db, checkout, current_merchant.id)
    db.commit()
    return {"token": new_link.token, "status": new_link.status}


@router.post("/{checkout_literal}/share")
async def share_checkout(
    payload: ShareCheckoutRequest,
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    result = checkout_services.share_checkout(db, checkout, payload.model_dump(), current_merchant.id)
    db.commit()
    return result


@router.get("/{checkout_literal}/analytics")
async def get_checkout_analytics(
    checkout_literal: str = _CHECKOUT_LITERAL,
    period: str = Query("30d"),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    analytics = checkout_crud.get_checkout_analytics(db, checkout.id, current_merchant.id, period=period)
    analytics["period"] = period
    return analytics


@router.get("/{checkout_literal}/payers")
async def get_checkout_payers(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    payers = checkout_crud.get_checkout_payers(db, checkout.id, current_merchant.id)
    return {"items": payers, "total": len(payers)}


@router.get("/{checkout_literal}/activity")
async def get_checkout_activity(
    checkout_literal: str = _CHECKOUT_LITERAL,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    from sqlalchemy import select as sa_select, func as sa_func
    from src.apps.checkouts.models.checkout_activity import CheckoutActivity
    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    stmt = (
        sa_select(CheckoutActivity)
        .where(CheckoutActivity.checkout_id == checkout.id)
        .order_by(CheckoutActivity.created_at.desc())
        .offset((page - 1) * page_size)
        .limit(page_size)
    )
    activities = db.execute(stmt).scalars().all()
    total_stmt = sa_select(sa_func.count()).select_from(CheckoutActivity).where(
        CheckoutActivity.checkout_id == checkout.id
    )
    total = db.execute(total_stmt).scalar_one()
    return {
        "items": [
            {
                "id": a.id,
                "event_type": a.event_type,
                "description": a.description,
                "created_at": a.created_at,
                "actor_user_id": a.actor_user_id,
                "actor_customer_id": a.actor_customer_id,
            }
            for a in activities
        ],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


@router.get("/{checkout_literal}/transactions")
async def get_checkout_transactions(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    from sqlalchemy import select as _select
    from src.apps.transactions.models.transactions import Transactions
    from src.apps.payment_requests.models.payment_request import PaymentRequest

    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    stmt = (
        _select(Transactions)
        .join(PaymentRequest, Transactions.payment_request_id == PaymentRequest.id)
        .where(
            PaymentRequest.checkout_id == checkout.id,
            PaymentRequest.deleted_at.is_(None),
        )
        .order_by(Transactions.ocurred_at.desc())
    )
    items = db.execute(stmt).scalars().all()
    return {
        "items": [
            {
                "id": t.id,
                "txn_literal": t.txn_literal,
                "txn_amount": t.txn_amount,
                "txn_status": t.txn_status,
                "txn_type": t.txn_type,
                "billing_name": t.billing_name,
                "occurred_at": t.occurred_at or t.ocurred_at,
            }
            for t in items
        ],
        "total": len(items),
    }


@router.get("/{checkout_literal}/invoices")
async def get_checkout_invoices(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    from sqlalchemy import select as _select
    from src.apps.invoices.models.invoice import Invoice
    from src.apps.payment_requests.models.payment_request import PaymentRequest

    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    stmt = (
        _select(Invoice)
        .join(PaymentRequest, Invoice.payment_request_id == PaymentRequest.id)
        .where(
            PaymentRequest.checkout_id == checkout.id,
            Invoice.deleted_at.is_(None),
            PaymentRequest.deleted_at.is_(None),
        )
        .order_by(Invoice.created_at.desc())
    )
    items = db.execute(stmt).scalars().all()
    return {
        "items": [
            {
                "id": inv.id,
                "invoice_literal": inv.invoice_literal,
                "invoice_id": inv.invoice_id,
                "amount": inv.amount,
                "status": inv.status_text,
                "due_date": inv.due_date,
                "billing_date": inv.billing_date,
                "paid_amount": inv.paid_amount,
                "paid_date": inv.paid_date,
                "customer_name": " ".join(
                    filter(None, [inv.customer.first_name, inv.customer.last_name])
                ) if inv.customer else None,
                "created_at": inv.created_at,
            }
            for inv in items
        ],
        "total": len(items),
    }


@router.get("/{checkout_literal}/authorizations")
async def get_checkout_authorizations(
    checkout_literal: str = _CHECKOUT_LITERAL,
    current_merchant=Depends(get_current_merchant),
    db: Session = Depends(get_db),
):
    from sqlalchemy import select as _select
    from src.apps.payment_requests.models.payment_request_authorizations import PaymentRequestAuthorizations
    from src.apps.payment_requests.models.payment_request import PaymentRequest

    checkout = _get_checkout_or_404(db, checkout_literal, current_merchant.id)
    stmt = (
        _select(PaymentRequestAuthorizations)
        .join(PaymentRequest, PaymentRequestAuthorizations.payment_request_id == PaymentRequest.id)
        .where(
            PaymentRequest.checkout_id == checkout.id,
            PaymentRequest.deleted_at.is_(None),
        )
        .order_by(PaymentRequestAuthorizations.authorization_date.desc())
    )
    items = db.execute(stmt).scalars().all()
    return {
        "items": [
            {
                "id": auth.id,
                "authorization_literal": auth.authorization_literal,
                "authorization_type": auth.authorization_type,
                "signing_name": auth.signing_name,
                "is_verified": auth.is_verified,
                "authorization_date": auth.authorization_date,
            }
            for auth in items
        ],
        "total": len(items),
    }


# ─── Response builder ─────────────────────────────────────────────────────────

def _build_checkout_response(db: Session, checkout) -> dict:
    active_link = checkout_crud.get_active_link(db, checkout.id)
    link_data = None
    if active_link:
        link_data = {
            "id": active_link.id,
            "token": active_link.token,
            "status": active_link.status,
            "click_count": active_link.click_count,
            "created_at": active_link.created_at,
        }
    line_items = []
    for li in (checkout.line_items or []):
        line_items.append({
            "id": li.id,
            "product_id": li.product_id,
            # Return both title and description so the frontend can populate
            # the item name field correctly when loading a saved draft.
            "title": li.description,
            "description": li.description,
            "quantity": li.quantity,
            "unit_price": li.unit_price,
            "discount_amount": li.discount_amount,
        })
    return {
        "id": checkout.id,
        "checkout_literal": checkout.checkout_literal,
        "title": checkout.title,
        "description": checkout.description,
        "checkout_type": checkout.checkout_type,
        "status": checkout.status,
        "amount": checkout.amount,
        "currency": checkout.currency,
        "min_amount": checkout.min_amount,
        "max_amount": checkout.max_amount,
        "suggested_amounts": checkout.suggested_amounts,
        "default_amount": checkout.default_amount,
        "tip_enabled": checkout.tip_enabled,
        "tip_type": checkout.tip_type,
        "tip_percentages": checkout.tip_percentages,
        "tip_allow_custom": checkout.tip_allow_custom,
        "tax_percent": checkout.tax_percent,
        "tax_type": checkout.tax_type,
        "payment_frequency": checkout.payment_frequency,
        "authorization_type": checkout.authorization_type,
        "authorization_method": checkout.authorization_method,
        "require_billing_address": checkout.require_billing_address,
        "require_cvv": checkout.require_cvv,
        "split_config": checkout.split_config,
        "recurring_config": checkout.recurring_config,
        "require_payer_verification": checkout.require_payer_verification,
        "payer_verification_type": checkout.payer_verification_type,
        "expires_at": checkout.expires_at,
        "allow_link_regeneration": checkout.allow_link_regeneration,
        "redirect_url": checkout.redirect_url,
        "thank_you_message": checkout.thank_you_message,
        "active_link": link_data,
        "line_items": line_items,
        "adjustment_config": checkout.adjustment_config,
        "created_at": checkout.created_at,
        "updated_at": checkout.updated_at,
    }


