"""REST API for subscription plan CRUD.

Endpoints:
  GET  /api/plans/          — list active plans (public)
  GET  /api/plans/admin     — list all plans inc. inactive (admin only)
  POST /api/plans/          — create plan (admin only)
  GET  /api/plans/{plan_id} — get single active plan (public)
  PUT  /api/plans/{plan_id} — update plan (admin only)
  DELETE /api/plans/{plan_id} — delete plan (admin only)
  POST /api/plans/seed      — seed default plans (admin only)
  POST /api/plans/reorder   — set sort_order for multiple plans (admin only)
"""

import json
import logging
from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session

from app.database import get_db
from app.models import SubscriptionPlan

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/plans", tags=["plans"])

DbSession = Annotated[Session, Depends(get_db)]


# ---------------------------------------------------------------------------
# Auth helper (admin-only)
# ---------------------------------------------------------------------------


def _require_admin(request: Request) -> dict:
    """Ensure the caller is an admin. Raises 403 otherwise."""
    user = request.session.get("user")
    if not user or not user.get("is_admin"):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
    return user


AdminUser = Annotated[dict, Depends(_require_admin)]


# ---------------------------------------------------------------------------
# Pydantic schemas
# ---------------------------------------------------------------------------


class PlanUpsert(BaseModel):
    """Body for creating or updating a subscription plan."""

    name: str
    tagline: str | None = None
    price_monthly: float = 0.0
    price_yearly: float = 0.0
    trial_days: int = 0
    lifetime_file_limit: int = 0
    daily_upload_limit: int = 0
    monthly_upload_limit: int = 0
    max_storage_destinations: int = 0
    max_ocr_pages_monthly: int = 0
    max_file_size_mb: int = 0
    max_mailboxes: int = 0
    overage_percent: int = Field(default=20, ge=0, le=200)
    allow_overage_billing: bool = False
    overage_price_per_doc: float | None = None
    overage_price_per_ocr_page: float | None = None
    is_active: bool = True
    is_highlighted: bool = False
    badge_text: str | None = None
    cta_text: str = "Get started"
    sort_order: int = 0
    features: list[str] = []
    api_access: bool = False
    stripe_price_id_monthly: str | None = None
    stripe_price_id_yearly: str | None = None


class ReorderBody(BaseModel):
    """Body for reordering plans."""

    order: list[str]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _plan_to_response(plan: SubscriptionPlan) -> dict[str, Any]:
    features: list[str] = []
    if plan.features:
        try:
            features = json.loads(plan.features)
        except (json.JSONDecodeError, TypeError):
            features = []
    return {
        "id": plan.id,
        "plan_id": plan.plan_id,
        "name": plan.name,
        "tagline": plan.tagline,
        "price_monthly": plan.price_monthly,
        "price_yearly": plan.price_yearly,
        "trial_days": plan.trial_days,
        "lifetime_file_limit": plan.lifetime_file_limit,
        "daily_upload_limit": plan.daily_upload_limit,
        "monthly_upload_limit": plan.monthly_upload_limit,
        "max_storage_destinations": plan.max_storage_destinations,
        "max_ocr_pages_monthly": plan.max_ocr_pages_monthly,
        "max_file_size_mb": plan.max_file_size_mb,
        "max_mailboxes": plan.max_mailboxes,
        "overage_percent": plan.overage_percent,
        "allow_overage_billing": plan.allow_overage_billing,
        "overage_price_per_doc": plan.overage_price_per_doc,
        "overage_price_per_ocr_page": plan.overage_price_per_ocr_page,
        "is_active": plan.is_active,
        "is_highlighted": plan.is_highlighted,
        "badge_text": plan.badge_text,
        "cta_text": plan.cta_text,
        "sort_order": plan.sort_order,
        "features": features,
        "api_access": plan.api_access,
        "stripe_price_id_monthly": plan.stripe_price_id_monthly,
        "stripe_price_id_yearly": plan.stripe_price_id_yearly,
        "created_at": plan.created_at.isoformat() if plan.created_at else None,
        "updated_at": plan.updated_at.isoformat() if plan.updated_at else None,
    }


def _apply_body(plan: SubscriptionPlan, body: PlanUpsert) -> None:
    """Apply PlanUpsert fields onto a SubscriptionPlan ORM object."""
    plan.name = body.name
    plan.tagline = body.tagline
    plan.price_monthly = body.price_monthly
    plan.price_yearly = body.price_yearly
    plan.trial_days = body.trial_days
    plan.lifetime_file_limit = body.lifetime_file_limit
    plan.daily_upload_limit = body.daily_upload_limit
    plan.monthly_upload_limit = body.monthly_upload_limit
    plan.max_storage_destinations = body.max_storage_destinations
    plan.max_ocr_pages_monthly = body.max_ocr_pages_monthly
    plan.max_file_size_mb = body.max_file_size_mb
    plan.max_mailboxes = body.max_mailboxes
    plan.overage_percent = body.overage_percent
    plan.allow_overage_billing = body.allow_overage_billing
    plan.overage_price_per_doc = body.overage_price_per_doc
    plan.overage_price_per_ocr_page = body.overage_price_per_ocr_page
    plan.is_active = body.is_active
    plan.is_highlighted = body.is_highlighted
    plan.badge_text = body.badge_text
    plan.cta_text = body.cta_text
    plan.sort_order = body.sort_order
    plan.features = json.dumps(body.features)
    plan.api_access = body.api_access
    plan.stripe_price_id_monthly = body.stripe_price_id_monthly or None
    plan.stripe_price_id_yearly = body.stripe_price_id_yearly or None


# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------


@router.get("/", summary="List active plans (public)")
def list_active_plans(db: DbSession) -> dict[str, Any]:
    """Return all active plans in sort order. Public endpoint — no auth required."""
    plans = (
        db.query(SubscriptionPlan)
        .filter(SubscriptionPlan.is_active.is_(True))
        .order_by(SubscriptionPlan.sort_order)
        .all()
    )
    return {"plans": [_plan_to_response(p) for p in plans]}


@router.get("/admin", summary="List all plans including inactive (admin only)")
def list_all_plans(db: DbSession, _admin: AdminUser) -> dict[str, Any]:
    """Return all plans (active and inactive) in sort order. Admin only."""
    plans = db.query(SubscriptionPlan).order_by(SubscriptionPlan.sort_order).all()
    return {"plans": [_plan_to_response(p) for p in plans]}


@router.post("/seed", summary="Seed default plans (admin only)", status_code=status.HTTP_200_OK)
def seed_plans(db: DbSession, _admin: AdminUser) -> dict[str, Any]:
    """Seed the subscription_plans table from TIER_DEFAULTS. No-op if plans already exist."""
    from app.utils.subscription import seed_default_plans

    inserted = seed_default_plans(db)
    return {"inserted": inserted, "message": f"Seeded {inserted} default plan(s)."}


@router.post("/reorder", summary="Reorder plans (admin only)")
def reorder_plans(body: ReorderBody, db: DbSession, _admin: AdminUser) -> dict[str, Any]:
    """Update sort_order for each plan_id in *body.order* (position = index in list)."""
    updated = 0

    # Fetch all requested plans in a single query to avoid N+1
    plan_ids = body.order
    plans = db.query(SubscriptionPlan).filter(SubscriptionPlan.plan_id.in_(plan_ids)).all()

    # Build a map for fast O(1) lookup
    plan_map = {p.plan_id: p for p in plans}

    for sort_order, plan_id in enumerate(plan_ids):
        plan = plan_map.get(plan_id)
        if plan:
            plan.sort_order = sort_order
            updated += 1

    try:
        db.commit()
    except Exception:
        db.rollback()
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to reorder plans")
    return {"updated": updated}


@router.post("/", summary="Create a new plan (admin only)", status_code=status.HTTP_201_CREATED)
def create_plan(plan_id: str, body: PlanUpsert, db: DbSession, _admin: AdminUser) -> dict[str, Any]:
    """Create a new subscription plan with the given *plan_id* slug."""
    existing = db.query(SubscriptionPlan).filter(SubscriptionPlan.plan_id == plan_id).first()
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Plan '{plan_id}' already exists.",
        )
    plan = SubscriptionPlan(plan_id=plan_id)
    _apply_body(plan, body)
    db.add(plan)
    try:
        db.commit()
        db.refresh(plan)
    except Exception:
        db.rollback()
        raise
    logger.info("Admin created subscription plan '%s'", plan_id)
    return _plan_to_response(plan)


@router.get("/{plan_id}", summary="Get a single active plan (public)")
def get_plan(plan_id: str, db: DbSession) -> dict[str, Any]:
    """Return a single active plan by plan_id. Public endpoint."""
    plan = (
        db.query(SubscriptionPlan)
        .filter(
            SubscriptionPlan.plan_id == plan_id,
            SubscriptionPlan.is_active.is_(True),
        )
        .first()
    )
    if not plan:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Plan '{plan_id}' not found.")
    return _plan_to_response(plan)


@router.put("/{plan_id}", summary="Update an existing plan (admin only)")
def update_plan(plan_id: str, body: PlanUpsert, db: DbSession, _admin: AdminUser) -> dict[str, Any]:
    """Update an existing subscription plan. Admin only."""
    plan = db.query(SubscriptionPlan).filter(SubscriptionPlan.plan_id == plan_id).first()
    if not plan:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Plan '{plan_id}' not found.")
    _apply_body(plan, body)
    try:
        db.commit()
        db.refresh(plan)
    except Exception:
        db.rollback()
        raise
    logger.info("Admin updated subscription plan '%s'", plan_id)
    return _plan_to_response(plan)


@router.delete("/{plan_id}", summary="Delete a plan (admin only)", status_code=status.HTTP_204_NO_CONTENT)
def delete_plan(plan_id: str, db: DbSession, _admin: AdminUser) -> None:
    """Delete a subscription plan. Admin only."""
    plan = db.query(SubscriptionPlan).filter(SubscriptionPlan.plan_id == plan_id).first()
    if not plan:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Plan '{plan_id}' not found.")
    try:
        db.delete(plan)
        db.commit()
    except Exception:
        db.rollback()
        raise
    logger.info("Admin deleted subscription plan '%s'", plan_id)
