"""
Beta signup API.

Public endpoint:
  POST /api/v2/beta-signup              Accept form submissions (no auth)

Admin endpoints:
  GET  /api/v2/admin/beta-submissions   List pending/all submissions
  POST /api/v2/admin/beta-submissions/:id/approve   Approve & send invitation
  POST /api/v2/admin/beta-submissions/:id/reject    Reject submission
"""

import logging
import re
from datetime import datetime, timezone
from typing import Optional
from urllib.parse import urlparse

from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from ...db import get_session
from ...models import BetaSubmission
from ...auth_security import require_admin, AdminContext
from ...services.admin_audit import log_admin_action
from ...services.admin_alerts import notify_beta_signup_received

log = logging.getLogger(__name__)

# ── Free email domain blocklist ─────────────────────────────────────────────
FREE_EMAIL_DOMAINS = frozenset({
    "gmail.com", "googlemail.com", "yahoo.com", "yahoo.co.uk",
    "hotmail.com", "hotmail.co.uk", "outlook.com", "outlook.co.uk",
    "live.com", "msn.com", "aol.com", "icloud.com", "me.com", "mac.com",
    "mail.com", "protonmail.com", "proton.me", "zoho.com", "yandex.com",
    "gmx.com", "gmx.de", "fastmail.com", "tutanota.com", "hey.com",
    "inbox.com", "mail.ru", "qq.com", "163.com", "126.com",
    "rediffmail.com", "comcast.net", "verizon.net", "att.net",
    "sbcglobal.net", "cox.net", "charter.net", "earthlink.net",
    "optonline.net", "frontier.com", "windstream.net",
})

SOURCE_ALLOWED_CHARS = re.compile(r"[^a-z0-9._:-]+")


def _normalize_source(value: Optional[str]) -> Optional[str]:
    if not value:
        return None
    normalized = SOURCE_ALLOWED_CHARS.sub("_", value.strip().lower())
    normalized = normalized.strip("._:-_")
    if not normalized:
        return None
    return normalized[:50]


def _resolve_signup_source(payload: "BetaSignupPayload", request: Request) -> str:
    candidates = [
        payload.source,
        payload.utm_source,
        request.query_params.get("source"),
        request.query_params.get("utm_source"),
        request.headers.get("x-signup-source"),
        request.headers.get("x-utm-source"),
    ]
    for candidate in candidates:
        normalized = _normalize_source(candidate)
        if normalized:
            return normalized

    referer = request.headers.get("referer") or request.headers.get("referrer")
    if referer:
        hostname = urlparse(referer).hostname
        if hostname:
            normalized = _normalize_source(f"ref:{hostname}")
            if normalized:
                return normalized

    return "launch_site"

# ── Public router (no auth) ────────────────────────────────────────────────

public_router = APIRouter(tags=["Beta"])


class BetaSignupPayload(BaseModel):
    email: str
    use_case: str = ""
    name: str = ""
    source: Optional[str] = None
    utm_source: Optional[str] = None


@public_router.post("/beta-signup")
async def beta_signup(
    payload: BetaSignupPayload,
    request: Request,
    db: AsyncSession = Depends(get_session),
):
    """Public endpoint — accepts beta signup form submissions."""
    email = payload.email.strip().lower()
    if not email or "@" not in email:
        return JSONResponse(
            status_code=422,
            content={"ok": False, "error": "Valid email address required"},
        )

    domain = email.split("@", 1)[1]
    is_business = domain not in FREE_EMAIL_DOMAINS
    source = _resolve_signup_source(payload, request)

    # Idempotent — if email already exists, just return success.
    existing = await db.execute(
        select(BetaSubmission).where(BetaSubmission.email == email)
    )
    if existing.scalar_one_or_none():
        return {"ok": True, "message": "You're already on the list."}

    submission = BetaSubmission(
        email=email,
        name=payload.name.strip(),
        use_case=payload.use_case.strip(),
        domain=domain,
        is_business_email=is_business,
        source=source,
        status="pending",
    )
    db.add(submission)
    await db.commit()

    try:
        await notify_beta_signup_received(
            email=submission.email,
            name=submission.name,
            domain=submission.domain,
            use_case=submission.use_case,
            source=submission.source,
            is_business_email=submission.is_business_email,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
            submitted_at=submission.submitted_at,
        )
    except Exception as exc:
        log.warning("Failed to send beta signup admin alert: %s", exc)

    log.info(
        "[BETA] New signup: %s (domain=%s, business=%s, source=%s)",
        email, domain, is_business, source,
    )
    return {"ok": True, "message": "Thanks! We'll be in touch."}


# ── Admin router ────────────────────────────────────────────────────────────

admin_router = APIRouter(prefix="/admin/beta-submissions", tags=["Admin"])


class BetaSubmissionOut(BaseModel):
    id: str
    email: str
    name: str
    use_case: str
    domain: str
    is_business_email: bool
    source: str
    status: str
    admin_notes: Optional[str]
    submitted_at: str
    reviewed_at: Optional[str]


@admin_router.get("")
async def list_submissions(
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
    status: Optional[str] = Query(default=None),
    limit: int = Query(default=100, le=500),
    offset: int = Query(default=0, ge=0),
):
    """List beta submissions, optionally filtered by status."""
    query = select(BetaSubmission).order_by(BetaSubmission.submitted_at.desc())
    if status:
        query = query.where(BetaSubmission.status == status)
    query = query.offset(offset).limit(limit)

    result = await db.execute(query)
    submissions = result.scalars().all()

    # Counts by status
    count_result = await db.execute(
        select(BetaSubmission.status, func.count(BetaSubmission.id))
        .group_by(BetaSubmission.status)
    )
    counts = {row[0]: row[1] for row in count_result}

    return {
        "ok": True,
        "submissions": [
            BetaSubmissionOut(
                id=s.id,
                email=s.email,
                name=s.name,
                use_case=s.use_case,
                domain=s.domain,
                is_business_email=s.is_business_email,
                source=s.source,
                status=s.status,
                admin_notes=s.admin_notes,
                submitted_at=s.submitted_at.isoformat() if s.submitted_at else "",
                reviewed_at=s.reviewed_at.isoformat() if s.reviewed_at else None,
            )
            for s in submissions
        ],
        "counts": counts,
    }


class ApprovePayload(BaseModel):
    notes: Optional[str] = None


@admin_router.post("/{submission_id}/approve")
async def approve_submission(
    submission_id: str,
    payload: ApprovePayload = ApprovePayload(),
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    """Approve a beta submission and send the trial invitation email."""
    from ...services.email_notifications import _send_via_resend_with_id
    from ...services.trial_drip_service import render_email
    from ...models import TrialEmailTemplate

    result = await db.execute(
        select(BetaSubmission).where(BetaSubmission.id == submission_id)
    )
    sub = result.scalar_one_or_none()
    if not sub:
        raise HTTPException(404, "Submission not found")
    if sub.status == "sent":
        raise HTTPException(400, "Invitation already sent for this submission")

    # Look up invitation template
    tpl_result = await db.execute(
        select(TrialEmailTemplate).where(
            TrialEmailTemplate.slug == "manual_invitation"
        )
    )
    template = tpl_result.scalar_one_or_none()
    if not template:
        raise HTTPException(404, "Invitation template not found. Run migration 012.")

    first_name = (
        sub.name.split()[0]
        if sub.name.strip()
        else sub.email.split("@")[0].split(".")[0].title()
    )

    subject, text_body, html_body = render_email(
        template,
        first_name=first_name,
        org_name="",
        trial_end_date="",
        account_id="beta-invite",
    )

    resend_id = await _send_via_resend_with_id(
        sub.email,
        subject,
        text_body,
        html_body=html_body,
        tags=[{"name": "message_type", "value": "beta_invitation"}],
    )

    # Also record in trial_email_sends for audit trail
    from ...models import TrialEmailSend

    send = TrialEmailSend(
        template_id=template.id,
        account_id="beta-invite",
        user_id=admin.user_id,
        recipient_email=sub.email,
        day_offset=template.day_offset,
        resend_email_id=resend_id,
        status="sent" if resend_id else "error",
        is_test=True,  # bypass idempotency constraint
    )
    db.add(send)

    # Update submission status
    sub.status = "sent"
    sub.reviewed_at = datetime.now(timezone.utc)
    if payload.notes:
        sub.admin_notes = payload.notes
    db.add(sub)
    await db.commit()

    await log_admin_action(
        admin_user_id=admin.user_id,
        admin_email=admin.email or "",
        action="beta_submission_approve",
        db=db,
        details={"submission_id": submission_id, "email": sub.email, "resend_id": resend_id},
    )

    log.info("[BETA] Approved and sent invitation to %s", sub.email)
    return {"ok": True, "resend_email_id": resend_id}


@admin_router.post("/{submission_id}/reject")
async def reject_submission(
    submission_id: str,
    payload: ApprovePayload = ApprovePayload(),
    admin: AdminContext = Depends(require_admin),
    db: AsyncSession = Depends(get_session),
):
    """Reject a beta submission."""
    result = await db.execute(
        select(BetaSubmission).where(BetaSubmission.id == submission_id)
    )
    sub = result.scalar_one_or_none()
    if not sub:
        raise HTTPException(404, "Submission not found")

    sub.status = "rejected"
    sub.reviewed_at = datetime.now(timezone.utc)
    if payload.notes:
        sub.admin_notes = payload.notes
    db.add(sub)
    await db.commit()

    await log_admin_action(
        admin_user_id=admin.user_id,
        admin_email=admin.email or "",
        action="beta_submission_reject",
        db=db,
        details={"submission_id": submission_id, "email": sub.email},
    )

    return {"ok": True}
