"""
Yield Domain Routing API.

This handles incoming HTTP requests to yield domains:
1. Detect the domain from the Host header
2. Look up the yield configuration
3. Track the click
4. Redirect to the appropriate affiliate landing page

In production, this runs on a separate subdomain or IP (yield.pounce.ch)
that yield domains CNAME to.
"""

import logging
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Optional
from uuid import uuid4

from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import and_, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db
from app.config import get_settings
from app.models.yield_domain import YieldDomain, YieldTransaction, AffiliatePartner
from app.services.intent_detector import detect_domain_intent
from app.services.telemetry import track_event
from app.services.yield_landing_page import render_yield_landing_html

logger = logging.getLogger(__name__)
settings = get_settings()

router = APIRouter(prefix="/r", tags=["yield-routing"])

# Revenue split
USER_REVENUE_SHARE = Decimal("0.70")


def hash_ip(ip: str) -> str:
    """Hash IP for privacy-compliant storage."""
    import hashlib
    # Salt to prevent trivial rainbow table lookups.
    return hashlib.sha256(f"{ip}|{settings.secret_key}".encode()).hexdigest()[:32]


def _get_client_ip(request: Request) -> Optional[str]:
    # Prefer proxy headers when behind nginx
    xff = request.headers.get("x-forwarded-for")
    if xff:
        # first IP in list
        ip = xff.split(",")[0].strip()
        if ip:
            return ip
    cf_ip = request.headers.get("cf-connecting-ip")
    if cf_ip:
        return cf_ip.strip()
    return request.client.host if request.client else None


def _safe_tracking_url(template: str, *, click_id: str, domain: str, domain_id: int, partner: str) -> str:
    try:
        return template.format(
            click_id=click_id,
            domain=domain,
            domain_id=domain_id,
            partner=partner,
        )
    except KeyError as e:
        raise HTTPException(
            status_code=500,
            detail=f"Partner tracking_url_template uses unsupported placeholder: {str(e)}",
        )


def generate_tracking_url(
    partner: AffiliatePartner,
    yield_domain: YieldDomain,
    click_id: str,
) -> str:
    """
    Generate the tracking URL for a partner.
    
    Most affiliate networks expect parameters like:
    - clickid / subid: Our click tracking ID
    - ref: Domain name or user reference
    """
    if not partner.tracking_url_template:
        raise HTTPException(
            status_code=503,
            detail=f"Partner routing not configured for {partner.slug}. Missing tracking_url_template.",
        )

    return _safe_tracking_url(
        partner.tracking_url_template,
        click_id=click_id,
        domain=yield_domain.domain,
        domain_id=yield_domain.id,
        partner=partner.slug,
    )


@router.get("/{domain}")
async def route_yield_domain(
    domain: str,
    request: Request,
    db: AsyncSession = Depends(get_db),
    direct: bool = Query(False, description="Direct redirect without landing page"),
):
    """
    Route traffic for a yield domain.
    
    This is the main entry point for yield domain traffic.
    
    Query params:
    - direct: If true, redirect immediately without landing page
    """
    domain = domain.lower().strip()

    # Find yield domain (must be connected + active)
    yield_domain = (
        await db.execute(
            select(YieldDomain).where(
                and_(
                    YieldDomain.domain == domain,
                    YieldDomain.status == "active",
                    YieldDomain.dns_verified == True,
                    or_(YieldDomain.connected_at.is_not(None), YieldDomain.dns_verified_at.is_not(None)),
                )
            )
        )
    ).scalar_one_or_none()

    if not yield_domain:
        logger.warning(f"Route request for unknown/inactive/unconnected domain: {domain}")
        raise HTTPException(status_code=404, detail="Domain not active for yield routing.")

    # Resolve partner
    partner: Optional[AffiliatePartner] = None
    if yield_domain.partner_id:
        partner = (
            await db.execute(
                select(AffiliatePartner).where(
                    and_(
                        AffiliatePartner.id == yield_domain.partner_id,
                        AffiliatePartner.is_active == True,
                    )
                )
            )
        ).scalar_one_or_none()

    if not partner and yield_domain.detected_intent:
        # Match full detected intent first (e.g. medical_dental)
        partner = (
            await db.execute(
                select(AffiliatePartner)
                .where(
                    and_(
                        AffiliatePartner.is_active == True,
                        AffiliatePartner.intent_categories.ilike(f"%{yield_domain.detected_intent}%"),
                    )
                )
                .order_by(AffiliatePartner.priority.desc())
            )
        ).scalar_one_or_none()

    if not partner:
        raise HTTPException(status_code=503, detail="No active partner available for this domain intent.")

    # Landing page mode: do NOT record a click yet.
    # The CTA will call this endpoint again with direct=true, which records the click + redirects.
    if not direct:
        cta_url = f"/api/v1/r/{yield_domain.domain}?direct=true"
        try:
            html = render_yield_landing_html(yield_domain=yield_domain, cta_url=cta_url)
        except Exception as e:
            raise HTTPException(status_code=503, detail=f"Landing page not available: {e}")

        await track_event(
            db,
            event_name="yield_landing_view",
            request=request,
            user_id=yield_domain.user_id,
            is_authenticated=None,
            source="routing",
            domain=yield_domain.domain,
            yield_domain_id=yield_domain.id,
            metadata={"partner": partner.slug},
        )
        await db.commit()
        return HTMLResponse(content=html, status_code=200)

    # Rate limit: max 120 clicks/10min per IP per domain
    client_ip = _get_client_ip(request)
    ip_hash = hash_ip(client_ip) if client_ip else None
    if ip_hash:
        cutoff = datetime.utcnow() - timedelta(minutes=10)
        recent = (
            await db.execute(
                select(func.count(YieldTransaction.id)).where(
                    and_(
                        YieldTransaction.yield_domain_id == yield_domain.id,
                        YieldTransaction.event_type == "click",
                        YieldTransaction.ip_hash == ip_hash,
                        YieldTransaction.created_at >= cutoff,
                    )
                )
            )
        ).scalar() or 0
        if recent >= 120:
            raise HTTPException(status_code=429, detail="Too many requests. Please slow down.")

    # Compute click economics (only CPC can be accounted immediately)
    gross = Decimal("0")
    net = Decimal("0")
    currency = (partner.payout_currency or "CHF").upper()
    if (partner.payout_type or "").lower() == "cpc":
        gross = partner.payout_amount or Decimal("0")
        net = (gross * USER_REVENUE_SHARE).quantize(Decimal("0.01"))

    click_id = uuid4().hex
    destination_url = generate_tracking_url(partner, yield_domain, click_id)

    user_agent = request.headers.get("user-agent")
    referrer = request.headers.get("referer")
    geo_country = request.headers.get("cf-ipcountry") or request.headers.get("x-country")
    geo_country = geo_country.strip().upper() if geo_country else None

    transaction = YieldTransaction(
        yield_domain_id=yield_domain.id,
        event_type="click",
        partner_slug=partner.slug,
        click_id=click_id,
        destination_url=destination_url[:2000],
        gross_amount=gross,
        net_amount=net,
        currency=currency,
        referrer=referrer[:500] if referrer else None,
        user_agent=user_agent[:500] if user_agent else None,
        geo_country=geo_country[:2] if geo_country else None,
        ip_hash=ip_hash,
        status="confirmed",
        confirmed_at=datetime.utcnow(),
    )
    db.add(transaction)

    yield_domain.total_clicks += 1
    yield_domain.last_click_at = datetime.utcnow()
    if net > 0:
        yield_domain.total_revenue += net

    await track_event(
        db,
        event_name="yield_click",
        request=request,
        user_id=yield_domain.user_id,
        is_authenticated=None,
        source="routing",
        domain=yield_domain.domain,
        yield_domain_id=yield_domain.id,
        click_id=click_id,
        metadata={"partner": partner.slug, "currency": currency, "net_amount": float(net)},
    )

    await db.commit()

    return RedirectResponse(url=destination_url, status_code=302)


@router.get("/")
async def yield_routing_info():
    """Info endpoint for yield routing service."""
    return {
        "service": "Pounce Yield Routing",
        "version": "2.0.0",
        "docs": f"{settings.site_url}/docs#/yield-routing",
        "status": "active",
    }


# ============================================================================
# Host-based routing (for production deployment)
# ============================================================================

@router.api_route("/catch-all", methods=["GET", "HEAD"])
async def catch_all_route(
    request: Request,
    db: AsyncSession = Depends(get_db),
):
    """
    Catch-all route for host-based routing.
    
    In production, this endpoint handles requests where the Host header
    is the yield domain itself (e.g., zahnarzt-zuerich.ch).
    
    This requires:
    1. Yield domains to CNAME to yield.pounce.ch
    2. Nginx/Caddy to route all hosts to this backend
    3. This endpoint to parse the Host header
    """
    host = request.headers.get("host", "").lower()
    
    # Remove port if present
    if ":" in host:
        host = host.split(":")[0]
    
    # Skip our own domains
    our_domains = ["pounce.ch", "localhost", "127.0.0.1"]
    if any(host.endswith(d) for d in our_domains):
        return {"status": "not a yield domain", "host": host}
    
    # If host matches a connected yield domain, route it
    _ = (
        await db.execute(
            select(YieldDomain.id).where(
                and_(
                    YieldDomain.domain == host,
                    YieldDomain.status == "active",
                    YieldDomain.dns_verified == True,
                    or_(YieldDomain.connected_at.is_not(None), YieldDomain.dns_verified_at.is_not(None)),
                )
            )
        )
    ).scalar_one_or_none()

    if not _:
        raise HTTPException(status_code=404, detail="Host not configured for yield routing.")

    return RedirectResponse(url=f"/api/v1/r/{host}?direct=false", status_code=302)

