"""
Admin API for trial drip email template management.

Endpoints:
  GET    /admin/trial-emails/templates          list all templates
  GET    /admin/trial-emails/templates/:id      get single template
  PUT    /admin/trial-emails/templates/:id      update subject/body/enabled
  POST   /admin/trial-emails/preview            preview rendered HTML
  POST   /admin/trial-emails/templates/:id/test-send  send test to admin
  GET    /admin/trial-emails/sends              recent sends (audit log)
  GET    /admin/trial-emails/unsubscribe        signed unsubscribe link
  POST   /admin/trial-emails/webhooks/resend    bounce/complaint webhook
"""

import logging
import os
from datetime import datetime, timezone
from typing import Optional, List

from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from sqlalchemy import select, func, update as sa_update
from sqlalchemy.ext.asyncio import AsyncSession

from ...db import get_session
from ...models import Account, TrialEmailTemplate, TrialEmailSend
from ...auth_security import require_admin, AdminContext
from ...services.admin_audit import log_admin_action
from ...services.trial_drip_service import (
    render_email,
    verify_unsubscribe_token,
)

log = logging.getLogger(__name__)

router = APIRouter(prefix="/admin/trial-emails", tags=["Admin"])


# ── Pydantic schemas ────────────────────────────────────────────────────────

class TemplateOut(BaseModel):
    id: str
    slug: str
    day_offset: int
    subject: str
    body_markdown: str
    enabled: bool
    sort_order: int
    updated_at: str
    send_count: int = 0


class TemplateUpdatePayload(BaseModel):
    subject: Optional[str] = None
    body_markdown: Optional[str] = None
    enabled: Optional[bool] = None


class PreviewPayload(BaseModel):
    subject: str
    body_markdown: str


class PreviewOut(BaseModel):
    subject: str
    html: str
    text: str


class SendOut(BaseModel):
    id: str
    template_slug: str
    day_offset: int
    recipient_email: str
    status: str
    resend_email_id: Optional[str]
    is_test: bool
    sent_at: str


# ── Template endpoints ───────────────────────────────────────────────────────

@router.get("/templates")
async def list_templates(
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    result = await db.execute(
        select(TrialEmailTemplate).order_by(TrialEmailTemplate.sort_order)
    )
    templates = result.scalars().all()

    # Send counts per template (exclude test sends).
    count_result = await db.execute(
        select(
            TrialEmailSend.template_id,
            func.count(TrialEmailSend.id).label("cnt"),
        )
        .where(TrialEmailSend.is_test == False)  # noqa: E712
        .group_by(TrialEmailSend.template_id)
    )
    counts = {row.template_id: row.cnt for row in count_result}

    return {
        "ok": True,
        "templates": [
            TemplateOut(
                id=t.id,
                slug=t.slug,
                day_offset=t.day_offset,
                subject=t.subject,
                body_markdown=t.body_markdown,
                enabled=t.enabled,
                sort_order=t.sort_order,
                updated_at=t.updated_at.isoformat() if t.updated_at else "",
                send_count=counts.get(t.id, 0),
            )
            for t in templates
        ],
    }


@router.get("/templates/{template_id}")
async def get_template(
    template_id: str,
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    result = await db.execute(
        select(TrialEmailTemplate).where(TrialEmailTemplate.id == template_id)
    )
    t = result.scalar_one_or_none()
    if not t:
        raise HTTPException(404, "Template not found")
    return {
        "ok": True,
        "template": TemplateOut(
            id=t.id,
            slug=t.slug,
            day_offset=t.day_offset,
            subject=t.subject,
            body_markdown=t.body_markdown,
            enabled=t.enabled,
            sort_order=t.sort_order,
            updated_at=t.updated_at.isoformat() if t.updated_at else "",
        ),
    }


@router.put("/templates/{template_id}")
async def update_template(
    template_id: str,
    payload: TemplateUpdatePayload,
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    result = await db.execute(
        select(TrialEmailTemplate).where(TrialEmailTemplate.id == template_id)
    )
    t = result.scalar_one_or_none()
    if not t:
        raise HTTPException(404, "Template not found")

    changes = {}
    if payload.subject is not None:
        t.subject = payload.subject
        changes["subject"] = payload.subject
    if payload.body_markdown is not None:
        t.body_markdown = payload.body_markdown
        changes["body_markdown"] = "(updated)"
    if payload.enabled is not None:
        t.enabled = payload.enabled
        changes["enabled"] = payload.enabled
    t.updated_at = datetime.now(timezone.utc)

    db.add(t)
    await db.commit()

    await log_admin_action(
        admin_user_id=admin.user_id,
        admin_email=admin.email or "",
        action="trial_email_template_update",
        db=db,
        target_account_id=None,
        details={"template_id": template_id, **changes},
    )

    return {"ok": True}


# ── Preview ──────────────────────────────────────────────────────────────────

@router.post("/preview")
async def preview_email(
    payload: PreviewPayload,
    admin: AdminContext = Depends(require_admin),
):
    """Render a preview with sample data (no database write)."""
    fake_template = TrialEmailTemplate(
        id="preview",
        slug="preview",
        day_offset=0,
        subject=payload.subject,
        body_markdown=payload.body_markdown,
        enabled=True,
        sort_order=0,
    )
    subject, text_body, html_body = render_email(
        fake_template,
        first_name="Jane",
        org_name="Acme Corp",
        trial_end_date="March 15, 2026",
        account_id="preview-account",
    )
    return PreviewOut(subject=subject, html=html_body, text=text_body)


# ── Test send ────────────────────────────────────────────────────────────────

@router.post("/templates/{template_id}/test-send")
async def test_send(
    template_id: str,
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    """Send the template to the admin's own email as a test."""
    from ...services.email_notifications import _send_via_resend_with_id

    result = await db.execute(
        select(TrialEmailTemplate).where(TrialEmailTemplate.id == template_id)
    )
    t = result.scalar_one_or_none()
    if not t:
        raise HTTPException(404, "Template not found")

    # In dev mode AdminContext returns "dev-admin@localhost" which isn't
    # a real address.  Fall back to ADMIN_TEST_EMAIL or RESEND_FROM_EMAIL.
    recipient = admin.email
    if not recipient or "localhost" in recipient:
        recipient = (
            os.getenv("ADMIN_TEST_EMAIL")
            or os.getenv("RESEND_FROM_EMAIL")
        )
    if not recipient:
        raise HTTPException(
            400,
            "No admin email available for test send. "
            "Set ADMIN_TEST_EMAIL or RESEND_FROM_EMAIL.",
        )

    subject, text_body, html_body = render_email(
        t,
        first_name="Admin",
        org_name="FoundryOps (Test)",
        trial_end_date="February 28, 2026",
        account_id="test-account",
    )

    resend_id = await _send_via_resend_with_id(
        recipient,
        f"[TEST] {subject}",
        text_body,
        html_body=html_body,
        tags=[{"name": "message_type", "value": "trial_drip_test"}],
    )

    # Record the test send (is_test=True bypasses unique constraint).
    send = TrialEmailSend(
        template_id=t.id,
        account_id="test",
        user_id=admin.user_id,
        recipient_email=recipient,
        day_offset=t.day_offset,
        resend_email_id=resend_id,
        status="sent" if resend_id else "error",
        is_test=True,
    )
    db.add(send)
    await db.commit()

    await log_admin_action(
        admin_user_id=admin.user_id,
        admin_email=admin.email,
        action="trial_email_test_send",
        db=db,
        details={"template_id": template_id, "resend_id": resend_id},
    )

    return {"ok": True, "resend_email_id": resend_id}


# ── Manual invitation send ────────────────────────────────────────────────────

class InvitationPayload(BaseModel):
    recipient_email: str
    recipient_name: str = ""


@router.post("/send-invitation")
async def send_invitation(
    payload: InvitationPayload,
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    """Send the manual_invitation template to an arbitrary email address."""
    from ...services.email_notifications import _send_via_resend_with_id

    result = await db.execute(
        select(TrialEmailTemplate).where(TrialEmailTemplate.slug == "manual_invitation")
    )
    t = result.scalar_one_or_none()
    if not t:
        raise HTTPException(404, "Invitation template not found. Run migration 012.")

    first_name = (
        payload.recipient_name.split()[0]
        if payload.recipient_name.strip()
        else payload.recipient_email.split("@")[0].split(".")[0].title()
    )

    subject, text_body, html_body = render_email(
        t,
        first_name=first_name,
        org_name="",
        trial_end_date="",
        account_id="invitation",
    )

    resend_id = await _send_via_resend_with_id(
        payload.recipient_email,
        subject,
        text_body,
        html_body=html_body,
        tags=[{"name": "message_type", "value": "trial_invitation"}],
    )

    send = TrialEmailSend(
        template_id=t.id,
        account_id="invitation",
        user_id=admin.user_id,
        recipient_email=payload.recipient_email,
        day_offset=t.day_offset,
        resend_email_id=resend_id,
        status="sent" if resend_id else "error",
        is_test=True,  # bypass idempotency constraint
    )
    db.add(send)
    await db.commit()

    await log_admin_action(
        admin_user_id=admin.user_id,
        admin_email=admin.email or "",
        action="trial_invitation_send",
        db=db,
        details={
            "recipient": payload.recipient_email,
            "resend_id": resend_id,
        },
    )

    return {"ok": True, "resend_email_id": resend_id}


# ── Send log ─────────────────────────────────────────────────────────────────

@router.get("/sends")
async def list_sends(
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
    limit: int = Query(default=50, le=200),
    offset: int = Query(default=0, ge=0),
    status: Optional[str] = Query(default=None),
):
    query = (
        select(TrialEmailSend, TrialEmailTemplate.slug)
        .join(
            TrialEmailTemplate,
            TrialEmailSend.template_id == TrialEmailTemplate.id,
        )
        .order_by(TrialEmailSend.sent_at.desc())
    )
    if status:
        query = query.where(TrialEmailSend.status == status)
    query = query.offset(offset).limit(limit)

    result = await db.execute(query)
    rows = result.all()

    count_q = select(func.count(TrialEmailSend.id))
    if status:
        count_q = count_q.where(TrialEmailSend.status == status)
    total = (await db.execute(count_q)).scalar() or 0

    return {
        "ok": True,
        "sends": [
            SendOut(
                id=row.TrialEmailSend.id,
                template_slug=row.slug,
                day_offset=row.TrialEmailSend.day_offset,
                recipient_email=row.TrialEmailSend.recipient_email,
                status=row.TrialEmailSend.status,
                resend_email_id=row.TrialEmailSend.resend_email_id,
                is_test=row.TrialEmailSend.is_test,
                sent_at=row.TrialEmailSend.sent_at.isoformat(),
            )
            for row in rows
        ],
        "total": total,
    }


# ── Unsubscribe ──────────────────────────────────────────────────────────────

@router.get("/unsubscribe")
async def unsubscribe(
    a: str = Query(..., description="Account ID"),
    t: str = Query(..., description="HMAC token"),
    db: AsyncSession = Depends(get_session),
):
    """Stateless unsubscribe — validates HMAC token and sets do_not_contact."""
    if not verify_unsubscribe_token(a, t):
        raise HTTPException(403, "Invalid or expired unsubscribe link")

    result = await db.execute(select(Account).where(Account.id == a))
    account = result.scalar_one_or_none()
    if not account:
        raise HTTPException(404, "Account not found")

    account.do_not_contact = True
    account.do_not_contact_since = datetime.now(timezone.utc)
    db.add(account)
    await db.commit()

    log.info("Unsubscribed account %s via trial drip link", a)
    return HTMLResponse(
        content="""<!DOCTYPE html><html><head><meta charset="utf-8">
        <style>body{font-family:-apple-system,sans-serif;display:flex;
        justify-content:center;align-items:center;min-height:100vh;
        background:#f4f4f5;margin:0;}
        .card{background:#fff;padding:48px;border-radius:12px;
        text-align:center;box-shadow:0 4px 6px rgba(0,0,0,.1);max-width:400px;}
        h1{color:#111827;font-size:20px;margin:0 0 8px;}
        p{color:#6b7280;font-size:14px;margin:0;}</style>
        </head><body><div class="card">
        <h1>You've been unsubscribed</h1>
        <p>You won't receive any more trial emails from FoundryOps.</p>
        </div></body></html>""",
        status_code=200,
    )


# ── Resend webhook (bounce / complaint) ─────────────────────────────────────

@router.post("/webhooks/resend", include_in_schema=False)
async def resend_webhook(
    request: Request,
    db: AsyncSession = Depends(get_session),
):
    """Handle Resend bounce/complaint webhooks to update trial_email_sends."""
    # Verify webhook signature.
    webhook_secret = os.getenv("RESEND_WEBHOOK_SECRET")
    if webhook_secret:
        svix_id = request.headers.get("svix-id")
        svix_timestamp = request.headers.get("svix-timestamp")
        svix_signature = request.headers.get("svix-signature")
        if not (svix_id and svix_timestamp and svix_signature):
            raise HTTPException(401, "Missing webhook signature headers")
        # Signature verification using svix if available, otherwise log warning.
        try:
            from svix.webhooks import Webhook

            wh = Webhook(webhook_secret)
            raw_body = await request.body()
            wh.verify(
                raw_body,
                {
                    "svix-id": svix_id,
                    "svix-timestamp": svix_timestamp,
                    "svix-signature": svix_signature,
                },
            )
        except ImportError:
            log.warning(
                "svix library not installed — webhook signature NOT verified"
            )
        except Exception as exc:
            log.warning("Resend webhook signature invalid: %s", exc)
            raise HTTPException(401, "Invalid webhook signature")
    else:
        log.warning("RESEND_WEBHOOK_SECRET not set — webhook unverified")

    body = await request.json()
    event_type = body.get("type", "")
    data = body.get("data", {})
    email_id = data.get("email_id")

    if event_type in ("email.bounced", "email.complained") and email_id:
        status = "bounced" if "bounced" in event_type else "complained"
        await db.execute(
            sa_update(TrialEmailSend)
            .where(TrialEmailSend.resend_email_id == email_id)
            .values(status=status)
        )
        await db.commit()
        log.info("Resend webhook: %s for email_id=%s", event_type, email_id)

    return {"ok": True}
