"""
Billing domain: checkout, webhooks, subscription management.
Payment provider: paddle
"""

import json
from datetime import datetime
from functools import wraps
from pathlib import Path

from quart import Blueprint, render_template, request, redirect, url_for, flash, g, jsonify, session

import httpx


from ..core import config, fetch_one, fetch_all, execute

from ..core import verify_hmac_signature

from ..auth.routes import login_required



# Blueprint with its own template folder
bp = Blueprint(
    "billing",
    __name__,
    template_folder=str(Path(__file__).parent / "templates"),
    url_prefix="/billing",
)


# =============================================================================
# SQL Queries
# =============================================================================

async def get_subscription(user_id: int) -> dict | None:
    """Get user's subscription."""
    return await fetch_one(
        "SELECT * FROM subscriptions WHERE user_id = ? ORDER BY created_at DESC LIMIT 1",
        (user_id,)
    )


async def upsert_subscription(
    user_id: int,
    plan: str,
    status: str,
    provider_customer_id: str,
    provider_subscription_id: str,
    current_period_end: str = None,
) -> int:
    """Create or update subscription."""
    now = datetime.utcnow().isoformat()

    customer_col = "paddle_customer_id"  # legacy column, kept for existing rows
    subscription_col = "provider_subscription_id"


    existing = await fetch_one("SELECT id FROM subscriptions WHERE user_id = ?", (user_id,))

    if existing:
        await execute(
            f"""UPDATE subscriptions
                SET plan = ?, status = ?, {customer_col} = ?, {subscription_col} = ?,
                    current_period_end = ?, updated_at = ?
                WHERE user_id = ?""",
            (plan, status, provider_customer_id, provider_subscription_id,
             current_period_end, now, user_id),
        )
        return existing["id"]
    else:
        return await execute(
            f"""INSERT INTO subscriptions
                (user_id, plan, status, {customer_col}, {subscription_col},
                 current_period_end, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (user_id, plan, status, provider_customer_id, provider_subscription_id,
             current_period_end, now, now),
        )



async def get_subscription_by_provider_id(subscription_id: str) -> dict | None:
    return await fetch_one(
        "SELECT * FROM subscriptions WHERE provider_subscription_id = ?",
        (subscription_id,)
    )



async def update_subscription_status(provider_subscription_id: str, status: str, **extra) -> None:
    """Update subscription status by provider subscription ID."""
    extra["updated_at"] = datetime.utcnow().isoformat()
    extra["status"] = status
    sets = ", ".join(f"{k} = ?" for k in extra)
    values = list(extra.values())

    values.append(provider_subscription_id)
    await execute(f"UPDATE subscriptions SET {sets} WHERE provider_subscription_id = ?", tuple(values))



async def can_access_feature(user_id: int, feature: str) -> bool:
    """Check if user can access a feature based on their plan."""
    sub = await get_subscription(user_id)
    plan = sub["plan"] if sub and sub["status"] in ("active", "on_trial", "cancelled") else "free"
    return feature in config.PLAN_FEATURES.get(plan, [])


async def is_within_limits(user_id: int, resource: str, current_count: int) -> bool:
    """Check if user is within their plan limits."""
    sub = await get_subscription(user_id)
    plan = sub["plan"] if sub and sub["status"] in ("active", "on_trial", "cancelled") else "free"
    limit = config.PLAN_LIMITS.get(plan, {}).get(resource, 0)
    if limit == -1:
        return True
    return current_count < limit


# =============================================================================
# Access Gating
# =============================================================================

def subscription_required(allowed=("active", "on_trial", "cancelled")):
    """Decorator to gate routes behind active subscription."""
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            if "user_id" not in session:
                return redirect(url_for("auth.login"))
            sub = await get_subscription(session["user_id"])
            if not sub or sub["status"] not in allowed:
                return redirect(url_for("billing.pricing"))
            return await func(*args, **kwargs)
        return wrapper
    return decorator


# =============================================================================
# Routes
# =============================================================================

@bp.route("/pricing")
async def pricing():
    """Pricing page."""
    user_sub = None
    if "user_id" in session:
        user_sub = await get_subscription(session["user_id"])
    return await render_template("pricing.html", subscription=user_sub)


@bp.route("/success")
@login_required
async def success():
    """Checkout success page."""
    return await render_template("success.html")



# =============================================================================
# Paddle Implementation
# =============================================================================

@bp.route("/checkout/<plan>", methods=["POST"])
@login_required
async def checkout(plan: str):
    """Create Paddle checkout via API."""
    price_id = config.PADDLE_PRICES.get(plan)
    if not price_id:
        await flash("Invalid plan selected.", "error")
        return redirect(url_for("billing.pricing"))

    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.paddle.com/transactions",
            headers={
                "Authorization": f"Bearer {config.PADDLE_API_KEY}",
                "Content-Type": "application/json",
            },
            json={
                "items": [{"price_id": price_id, "quantity": 1}],
                "custom_data": {"user_id": str(g.user["id"]), "plan": plan},
                "checkout": {
                    "url": f"{config.BASE_URL}/billing/success",
                },
            },
        )
        response.raise_for_status()

    checkout_url = response.json()["data"]["checkout"]["url"]
    return redirect(checkout_url)


@bp.route("/manage", methods=["POST"])
@login_required
async def manage():
    """Redirect to Paddle customer portal."""
    sub = await get_subscription(g.user["id"])
    if not sub or not sub.get("provider_subscription_id"):
        await flash("No active subscription found.", "error")
        return redirect(url_for("dashboard.settings"))

    async with httpx.AsyncClient() as client:
        response = await client.get(
            f"https://api.paddle.com/subscriptions/{sub['provider_subscription_id']}",
            headers={"Authorization": f"Bearer {config.PADDLE_API_KEY}"},
        )
        response.raise_for_status()

    portal_url = response.json()["data"]["management_urls"]["update_payment_method"]
    return redirect(portal_url)


@bp.route("/cancel", methods=["POST"])
@login_required
async def cancel():
    """Cancel subscription via Paddle API."""
    sub = await get_subscription(g.user["id"])
    if sub and sub.get("provider_subscription_id"):
        async with httpx.AsyncClient() as client:
            await client.post(
                f"https://api.paddle.com/subscriptions/{sub['provider_subscription_id']}/cancel",
                headers={
                    "Authorization": f"Bearer {config.PADDLE_API_KEY}",
                    "Content-Type": "application/json",
                },
                json={"effective_from": "next_billing_period"},
            )
    return redirect(url_for("dashboard.settings"))


@bp.route("/webhook/paddle", methods=["POST"])
async def webhook():
    """Handle Paddle webhooks."""
    payload = await request.get_data()
    sig = request.headers.get("Paddle-Signature", "")

    if not verify_hmac_signature(payload, sig, config.PADDLE_WEBHOOK_SECRET):
        return jsonify({"error": "Invalid signature"}), 400

    event = json.loads(payload)
    event_type = event.get("event_type")
    data = event.get("data", {})
    custom_data = data.get("custom_data", {})
    user_id = custom_data.get("user_id")

    if event_type == "subscription.activated":
        plan = custom_data.get("plan", "starter")
        await upsert_subscription(
            user_id=int(user_id) if user_id else 0,
            plan=plan,
            status="active",
            provider_customer_id=str(data.get("customer_id", "")),
            provider_subscription_id=data.get("id", ""),
            current_period_end=data.get("current_billing_period", {}).get("ends_at"),
        )

    elif event_type == "subscription.updated":
        await update_subscription_status(
            data.get("id", ""),
            status=data.get("status", "active"),
            current_period_end=data.get("current_billing_period", {}).get("ends_at"),
        )

    elif event_type == "subscription.canceled":
        await update_subscription_status(data.get("id", ""), status="cancelled")

    elif event_type == "subscription.past_due":
        await update_subscription_status(data.get("id", ""), status="past_due")

    return jsonify({"received": True})



