"""Billing router — invoices, payments, and subscription management."""
from __future__ import annotations

from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Query
from fastapi.responses import StreamingResponse
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.constants import UserRole
from src.core.dependencies import require_min_role, require_tenant
from src.core.exceptions import NotFoundError
from src.core.schemas.response import paginated, success
from src.database.session import get_db
from src.apps.auth.models.user import User
from src.apps.billing.models.invoice import Invoice
from src.apps.billing.models.invoice_payment import InvoicePayment
from src.apps.billing.schemas.requests import RecordPaymentRequest
from src.apps.billing.schemas.responses import InvoicePaymentResponse, InvoiceResponse
from src.apps.billing.schemas.subscription import (
    BillingInvoiceItem,
    ConfirmPaymentRequest,
    UpdatePlanRequest,
)
from src.apps.billing.services.invoice_service import InvoiceService
from src.apps.billing.services.subscription_service import SubscriptionService

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

_service = InvoiceService()
_sub_service = SubscriptionService()


# ── GET /subscription ─────────────────────────────────────────────────────────

@router.get("/subscription")
async def get_subscription(
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Return the tenant's current subscription plan info."""
    data = await _sub_service.get_subscription(db, tenant_id)
    return success(data)


# ── PATCH /subscription/plan ──────────────────────────────────────────────────

@router.patch("/subscription/plan")
async def update_subscription_plan(
    body: UpdatePlanRequest,
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Change the tenant's subscription plan via Stripe."""
    data = await _sub_service.update_plan(db, tenant_id, body.plan)
    return success(data, f"Plan updated to {body.plan.capitalize()}")


# ── GET /invoices ─────────────────────────────────────────────────────────────

def _plan_name_from_amount(amount: float) -> str:
    """Infer the subscription plan from the invoice amount (fixed CAD prices)."""
    if amount <= 149:
        return "Starter"
    if amount <= 349:
        return "Professional"
    return "Enterprise"


@router.get("/invoices")
async def list_invoices(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    search: Optional[str] = Query(None),
    status: Optional[str] = Query(None),
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Paginated invoice list for the billing page."""
    items, total = await _service.get_list(
        db,
        tenant_id=tenant_id,
        search=search,
        status=status,
        page=page,
        page_size=page_size,
    )

    return paginated(
        [InvoiceResponse.model_validate(inv) for inv in items],
        total,
        page,
        page_size,
    )


# ── GET /invoices/export  (MUST be before /{invoice_id}) ─────────────────────

@router.get("/invoices/export")
async def export_invoices(
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Export all invoices as a billing-page CSV (Invoice, Date, Plan, Amount, Status)."""
    generator = _service.export_billing_csv(db, tenant_id=tenant_id, plan_name=None)
    return StreamingResponse(
        generator,
        media_type="text/csv",
        headers={"Content-Disposition": 'attachment; filename="invoices.csv"'},
    )


# ── GET /invoices/export-csv  (legacy alias) ──────────────────────────────────

@router.get("/invoices/export-csv")
async def export_invoices_csv(
    search: Optional[str] = Query(None),
    status: Optional[str] = Query(None),
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    generator = _service.export_csv(db, tenant_id=tenant_id, search=search, status=status)
    return StreamingResponse(
        generator,
        media_type="text/csv",
        headers={"Content-Disposition": 'attachment; filename="invoices.csv"'},
    )


# ── POST /invoices/{invoice_id}/payment-intent ────────────────────────────────

@router.post("/invoices/{invoice_id}/payment-intent", status_code=201)
async def create_payment_intent(
    invoice_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Create a Stripe PaymentIntent for a failed invoice."""
    client_secret = await _service.create_payment_intent(
        db, tenant_id=tenant_id, invoice_id=invoice_id
    )
    return success({"client_secret": client_secret})


# ── POST /invoices/{invoice_id}/confirm-payment ───────────────────────────────

@router.post("/invoices/{invoice_id}/confirm-payment")
async def confirm_payment(
    invoice_id: UUID,
    body: ConfirmPaymentRequest,
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Verify a Stripe PaymentIntent succeeded and mark the invoice paid."""
    invoice = await _service.confirm_payment(
        db,
        tenant_id=tenant_id,
        invoice_id=invoice_id,
        payment_intent_id=body.payment_intent_id,
    )
    return success(
        {"invoice_id": str(invoice.id), "status": invoice.status},
        "Payment successful.",
    )


# ── GET /invoices/{invoice_id}/pdf ────────────────────────────────────────────

@router.get("/invoices/{invoice_id}/pdf")
async def download_invoice_pdf(
    invoice_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Stream a PDF for the invoice."""
    invoice = await _service.get_by_id(db, tenant_id=tenant_id, invoice_id=invoice_id)
    pdf_bytes = await _service.generate_pdf(db, tenant_id=tenant_id, invoice_id=invoice_id)

    filename = f"{invoice.invoice_number}.pdf"
    return StreamingResponse(
        iter([bytes(pdf_bytes)]),
        media_type="application/pdf",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


# ── GET /invoices/{invoice_id} ────────────────────────────────────────────────

@router.get("/invoices/{invoice_id}")
async def get_invoice(
    invoice_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.ADMINISTRATOR)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    invoice = await _service.get_by_id(db, tenant_id=tenant_id, invoice_id=invoice_id)
    return success(InvoiceResponse.model_validate(invoice))


# ── POST /invoices/{invoice_id}/payment ───────────────────────────────────────

@router.post("/invoices/{invoice_id}/payment", status_code=201)
async def record_payment(
    invoice_id: UUID,
    body: RecordPaymentRequest,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    invoice = await _service.record_payment(
        db,
        tenant_id=tenant_id,
        invoice_id=invoice_id,
        amount=body.amount,
        payment_date=body.payment_date,
        payment_method=body.payment_method,
        reference_number=body.reference_number,
        user_id=current_user.id,
    )
    return success(InvoiceResponse.model_validate(invoice), "Payment recorded")


# ── POST /invoices/{invoice_id}/remind ────────────────────────────────────────

@router.post("/invoices/{invoice_id}/remind")
async def send_reminder(
    invoice_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    invoice = await _service.send_reminder(
        db,
        tenant_id=tenant_id,
        invoice_id=invoice_id,
        user_id=current_user.id,
    )
    return success(InvoiceResponse.model_validate(invoice), "Reminder logged")


# ── GET /invoices/{invoice_id}/payments ───────────────────────────────────────

@router.get("/invoices/{invoice_id}/payments")
async def list_payments(
    invoice_id: UUID,
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    await _service.get_by_id(db, tenant_id=tenant_id, invoice_id=invoice_id)

    result = await db.execute(
        select(InvoicePayment)
        .where(
            InvoicePayment.invoice_id == invoice_id,
            InvoicePayment.tenant_id == tenant_id,
        )
        .order_by(InvoicePayment.created_at.desc())
    )
    items = [InvoicePaymentResponse.model_validate(p) for p in result.scalars().all()]
    return success(items)
