"""Billing router — invoices and payments."""
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_payment import InvoicePayment
from src.apps.billing.schemas.requests import RecordPaymentRequest
from src.apps.billing.schemas.responses import InvoicePaymentResponse, InvoiceResponse
from src.apps.billing.services.invoice_service import InvoiceService

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

_service = InvoiceService()


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

@router.get("/subscription", response_model=dict)
async def get_subscription(
    current_user: User = Depends(require_min_role(UserRole.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    """Return the tenant's current subscription plan info."""
    from src.apps.tenants.models.account import Account
    from src.apps.tenants.models.subscription import Subscription

    # Get account
    result = await db.execute(
        select(Account).where(Account.id == tenant_id)
    )
    account = result.scalar_one_or_none()
    if not account:
        raise NotFoundError("Account not found")

    # Get latest subscription
    sub_result = await db.execute(
        select(Subscription)
        .where(Subscription.account_id == account.id)
        .order_by(Subscription.created_at.desc())
        .limit(1)
    )
    sub = sub_result.scalar_one_or_none()

    plan_prices = {"starter": 149, "professional": 349, "enterprise": 749}
    plan = account.plan or "starter"

    return success(data={
        "plan": plan,
        "status": account.status,
        "amount_cad": (float(sub.amount_cad) if sub and sub.amount_cad else None) or plan_prices.get(plan, 149),
        "billing_cycle": sub.billing_cycle if sub else "monthly",
        "payment_method_last4": sub.payment_method_last4 if sub else None,
        "stripe_subscription_id": sub.stripe_subscription_id if sub else None,
        "current_period_start": sub.current_period_start.isoformat() if sub and sub.current_period_start else None,
        "current_period_end": sub.current_period_end.isoformat() if sub and sub.current_period_end else None,
    })


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

@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.STAFF)),
    tenant_id: str = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
):
    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(i) for i in items],
        total,
        page,
        page_size,
    )


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

@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"'},
    )


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

@router.get("/invoices/{invoice_id}")
async def get_invoice(
    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.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),
):
    # Verify invoice belongs to tenant before listing its payments
    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)
