"""Templates domain — HTTP router.

Admin endpoints (require platform admin):
  POST   /api/admin/templates            — register a new template
  GET    /api/admin/templates            — list all templates (incl. inactive)
  GET    /api/admin/templates/{ref-or-id}       — get template by stable ref or ID
  PATCH  /api/admin/templates/{ref-or-id}       — update template
  POST   /api/admin/templates/{ref-or-id}/deploy — trigger a demo redeploy
  DELETE /api/admin/templates/{ref-or-id}       — soft-delete template

Public endpoints (authenticated user):
  GET    /api/templates                  — list active templates for caller's plan
  GET    /api/templates/{ref}            — get one template by ref if active and allowed for caller's plan
"""
from __future__ import annotations

from typing import Optional
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.database import get_db
from src.core.exceptions import NotFoundError
from src.domain.admin.router import require_admin
from src.domain.auth.router import get_current_user
from src.domain.auth.models import User
from src.domain.templates.repository import TemplateRepository
from src.domain.templates.schemas import (
    TemplateBootstrapCreate,
    TemplateBootstrapResponse,
    TemplateCreate,
    TemplateDeployResponse,
    TemplateListResponse,
    TemplateResponse,
    TemplateUpdate,
)
from src.domain.templates.service import TemplateService

admin_router = APIRouter(prefix="/api/admin/templates", tags=["admin", "templates"])
public_router = APIRouter(prefix="/api/templates", tags=["templates"])


def _svc(db: AsyncSession) -> TemplateService:
    return TemplateService(TemplateRepository(db))


async def _get_admin_template(svc: TemplateService, template_key: str) -> Optional[TemplateResponse]:
    """Resolve the stable admin template ref while retaining UUID compatibility."""
    try:
        template_id = UUID(template_key)
    except ValueError:
        return await svc.get_by_ref(template_key.strip().lower())
    return await svc.get(template_id)


# ── Admin ──────────────────────────────────────────────────────────────────────


@admin_router.post("", response_model=TemplateResponse, status_code=201)
async def register_template(
    body: TemplateCreate,
    admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """Register a new template and enqueue the initial demo deployment."""
    svc = _svc(db)
    try:
        template = await svc.register(body, admin_user_id=admin.id)
    except ValueError as exc:
        raise HTTPException(status_code=422, detail=str(exc)) from exc

    # Commit before enqueuing so the worker can load the template record.
    await db.commit()

    # Enqueue the initial CF Pages deploy + KV routing for the main-branch demo.
    try:
        from src.workers.enqueue import enqueue_template_deploy
        await enqueue_template_deploy(str(template.id), environment="production")
    except Exception as exc:
        import structlog
        structlog.get_logger().warning("template_deploy_enqueue_failed", template_id=str(template.id), error=str(exc))

    return template


@admin_router.post("/create", response_model=TemplateBootstrapResponse, status_code=201)
async def create_starter_template(
    body: TemplateBootstrapCreate,
    admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """Create a template catalog entry, GitHub starter repo, and enqueue demo deploys."""
    svc = _svc(db)
    try:
        out = await svc.bootstrap_starter_template(body, admin_user_id=admin.id)
    except ValueError as exc:
        raise HTTPException(status_code=422, detail=str(exc)) from exc

    await db.commit()

    try:
        from src.workers.enqueue import enqueue_template_deploy

        out.production_deploy_enqueued = await enqueue_template_deploy(
            str(out.template_id),
            environment="production",
        )
        out.development_deploy_enqueued = await enqueue_template_deploy(
            str(out.template_id),
            environment="development",
        )
    except Exception as exc:
        import structlog

        structlog.get_logger().warning(
            "template_bootstrap_deploy_enqueue_failed",
            template_id=str(out.template_id),
            error=str(exc),
        )

    return out


@admin_router.get("", response_model=TemplateListResponse)
async def list_templates_admin(
    include_inactive: bool = Query(
        True,
        description="When false, only active templates (for filtering in the admin UI).",
    ),
    _admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """List templates for the admin panel (defaults to all non-deleted, including inactive)."""
    svc = _svc(db)
    items = await svc.list_all(include_inactive=include_inactive)
    return TemplateListResponse(items=items, total=len(items))


@admin_router.get("/{template_key}", response_model=TemplateResponse)
async def get_template_admin(
    template_key: str,
    _admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    svc = _svc(db)
    t = await _get_admin_template(svc, template_key)
    if not t:
        raise HTTPException(status_code=404, detail="Template not found.")
    return t


@admin_router.patch("/{template_key}", response_model=TemplateResponse)
async def update_template(
    template_key: str,
    body: TemplateUpdate,
    _admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    svc = _svc(db)
    template = await _get_admin_template(svc, template_key)
    if not template:
        raise HTTPException(status_code=404, detail="Template not found.")
    t = await svc.update(template.id, body)
    if not t:
        raise HTTPException(status_code=404, detail="Template not found.")
    return t


@admin_router.post("/{template_key}/deploy", response_model=TemplateDeployResponse)
async def trigger_template_deploy(
    template_key: str,
    environment: str = Query("production", description="'production' (main branch) or 'development'"),
    _admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    """Admin: (re-)deploy the template demo for the given environment."""
    svc = _svc(db)
    t = await _get_admin_template(svc, template_key)
    if not t:
        raise HTTPException(status_code=404, detail="Template not found.")
    if environment not in ("production", "development"):
        raise HTTPException(status_code=400, detail="environment must be 'production' or 'development'.")

    try:
        from src.workers.enqueue import enqueue_template_deploy
        await enqueue_template_deploy(str(t.id), environment=environment)
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"Failed to enqueue deploy: {exc}") from exc

    return TemplateDeployResponse(
        template_id=t.id,
        ref=t.ref,
        environment=environment,
        status="queued",
    )


@admin_router.delete("/{template_key}", status_code=204, response_model=None)
async def delete_template(
    template_key: str,
    _admin: User = Depends(require_admin),
    db: AsyncSession = Depends(get_db),
):
    svc = _svc(db)
    template = await _get_admin_template(svc, template_key)
    if not template:
        raise HTTPException(status_code=404, detail="Template not found.")
    deleted = await svc.delete(template.id)
    if not deleted:
        raise HTTPException(status_code=404, detail="Template not found.")


# ── Public ─────────────────────────────────────────────────────────────────────


@public_router.get("", response_model=TemplateListResponse)
async def list_templates(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """List active templates available to the caller's plan."""
    plan_slug = await _resolve_caller_plan(current_user.id, db)
    svc = _svc(db)
    items = await svc.list_for_plan(plan_slug)
    return TemplateListResponse(items=items, total=len(items))


@public_router.get("/{ref}", response_model=TemplateResponse)
async def get_template_by_ref(
    ref: str,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """Return template detail only when active and allowed for the caller's plan (same as list)."""
    plan_slug = await _resolve_caller_plan(current_user.id, db)
    svc = _svc(db)
    t = await svc.get_by_ref_for_plan(ref.strip().lower(), plan_slug)
    if not t:
        raise HTTPException(status_code=404, detail="Template not found.")
    return t


async def _resolve_caller_plan(user_id: UUID, db: AsyncSession) -> str:
    """Return the plan slug for the caller's active subscription (best-effort).

    Falls back to 'free' so the query still works even if no subscription is found.
    """
    try:
        from sqlalchemy import select
        from src.domain.workspaces.models import Workspace, WorkspaceMembership
        from src.domain.billing.models import Subscription

        result = await db.execute(
            select(Subscription.plan_slug)
            .join(Workspace, Workspace.id == Subscription.workspace_id)
            .join(WorkspaceMembership, WorkspaceMembership.workspace_id == Workspace.id)
            .where(WorkspaceMembership.user_id == user_id)
            .where(Subscription.status == "active")
            .limit(1)
        )
        slug = result.scalar_one_or_none()
        return slug or "free"
    except Exception:
        return "free"
