"""Billing ledger — tracks partner charges for completed jobs."""

from __future__ import annotations

import json
import math
import re

import structlog

from hive_compute.config import get_settings
from hive_compute.coordinator.database import get_db_ctx, record_event

logger = structlog.get_logger()

DEFAULT_RATE_PER_MINUTE = 0.006  # $0.006/min = $0.36/hr (matches OpenAI Whisper pricing)


def get_billing_quality_tier(input_config: str | dict | None) -> str | None:
    """Extract the billing quality tier from serialized job input config."""
    if isinstance(input_config, dict):
        payload = input_config
    elif input_config:
        try:
            payload = json.loads(input_config)
        except json.JSONDecodeError:
            logger.warning("Invalid input_config while resolving billing tier")
            return None
    else:
        return None

    billing_quality_tier = payload.get("billing_quality_tier")
    return billing_quality_tier if isinstance(billing_quality_tier, str) and billing_quality_tier.strip() else None


def resolve_effective_rate_per_minute(rate_per_minute: float, billing_quality_tier: str | None = None) -> float:
    """Apply the configured quality-tier multiplier to a partner's base rate."""
    multiplier = get_settings().get_billing_quality_multiplier(billing_quality_tier)
    return rate_per_minute * multiplier


def estimate_billing_amount_cents(
    duration_seconds: float,
    rate_per_minute: float,
    billing_quality_tier: str | None = None,
) -> int:
    """Estimate cents billed for a given audio duration and partner rate."""
    duration_minutes = duration_seconds / 60.0
    effective_rate_per_minute = resolve_effective_rate_per_minute(rate_per_minute, billing_quality_tier)
    return math.ceil(duration_minutes * effective_rate_per_minute * 100)


def get_partner_budget_snapshot(partner_id: str) -> dict:
    """Return the current month's billed and reserved spend for a partner."""
    with get_db_ctx() as db:
        partner = db.execute(
            "SELECT rate_per_minute, credits_usd, monthly_budget_cents FROM partners WHERE partner_id = ?",
            (partner_id,),
        ).fetchone()

        billed = db.execute(
            """
            SELECT SUM(amount_cents) AS total_cents
            FROM billing_items
            WHERE partner_id = ?
              AND created_at >= datetime('now', 'start of month')
            """,
            (partner_id,),
        ).fetchone()

        reserved = db.execute(
            """
            SELECT SUM(estimated_cost_cents) AS total_cents
            FROM jobs
            WHERE partner_id = ?
              AND status IN ('pending', 'claimed', 'processing')
              AND created_at >= datetime('now', 'start of month')
            """,
            (partner_id,),
        ).fetchone()

    return {
        "partner_id": partner_id,
        "rate_per_minute": partner["rate_per_minute"] if partner else DEFAULT_RATE_PER_MINUTE,
        "credits_usd": partner["credits_usd"] if partner else None,
        "monthly_budget_cents": partner["monthly_budget_cents"] if partner else None,
        "billed_cents": billed["total_cents"] or 0,
        "reserved_cents": reserved["total_cents"] or 0,
    }


def get_partner_credit_balance(partner_id: str) -> float | None:
    with get_db_ctx() as db:
        row = db.execute(
            "SELECT credits_usd FROM partners WHERE partner_id = ?",
            (partner_id,),
        ).fetchone()
    if not row:
        return None
    return row["credits_usd"]


def add_partner_credits(partner_id: str, amount_usd: float) -> dict | None:
    with get_db_ctx() as db:
        row = db.execute(
            """
            UPDATE partners
            SET credits_usd = ROUND(COALESCE(credits_usd, 0) + ?, 2)
            WHERE partner_id = ?
            RETURNING partner_id, credits_usd
            """,
            (amount_usd, partner_id),
        ).fetchone()
    if not row:
        return None
    return {"partner_id": row["partner_id"], "credits_usd": row["credits_usd"]}


def create_billing_item(
    job_id: str,
    partner_id: str,
    duration_seconds: float,
    rate_per_minute: float | None = None,
    billing_quality_tier: str | None = None,
) -> int:
    """Create a billing row for a completed job. Returns amount in cents."""
    partner_row = None
    if rate_per_minute is None or billing_quality_tier is None:
        with get_db_ctx() as db:
            if rate_per_minute is None:
                partner_row = db.execute(
                    "SELECT rate_per_minute, credits_usd FROM partners WHERE partner_id = ?",
                    (partner_id,),
                ).fetchone()
                rate_per_minute = partner_row["rate_per_minute"] if partner_row else DEFAULT_RATE_PER_MINUTE
            if billing_quality_tier is None:
                job = db.execute(
                    "SELECT input_config FROM jobs WHERE job_id = ?",
                    (job_id,),
                ).fetchone()
                billing_quality_tier = get_billing_quality_tier(job["input_config"] if job else None)

    duration_minutes = duration_seconds / 60.0
    effective_rate_per_minute = resolve_effective_rate_per_minute(rate_per_minute, billing_quality_tier)
    amount_cents = estimate_billing_amount_cents(duration_seconds, rate_per_minute, billing_quality_tier)

    with get_db_ctx() as db:
        if partner_row is None:
            partner_row = db.execute(
                "SELECT credits_usd FROM partners WHERE partner_id = ?",
                (partner_id,),
            ).fetchone()
        # UNIQUE constraint on job_id prevents double-billing atomically
        result = db.execute(
            """
            INSERT OR IGNORE INTO billing_items (partner_id, job_id, duration_seconds, rate_per_minute, amount_cents)
            VALUES (?, ?, ?, ?, ?)
            """,
            (partner_id, job_id, duration_seconds, effective_rate_per_minute, amount_cents),
        )
        if result.rowcount == 0:
            logger.warning("Billing item already exists", job_id=job_id)
            return 0

        record_event(
            db, job_id, "billed",
            to_status="completed", actor="billing",
            detail=(
                f"partner={partner_id}, {duration_minutes:.1f}min @ ${effective_rate_per_minute}/min"
                f" (tier={billing_quality_tier or 'standard'}) = {amount_cents}¢"
            ),
        )

        if partner_row and partner_row["credits_usd"] is not None:
            updated_balance = round(max(float(partner_row["credits_usd"]) - (amount_cents / 100.0), 0.0), 2)
            db.execute(
                "UPDATE partners SET credits_usd = ? WHERE partner_id = ?",
                (updated_balance, partner_id),
            )
            record_event(
                db, job_id, "credits_debited",
                to_status="completed", actor="billing",
                detail=f"partner={partner_id}, debited=${amount_cents / 100.0:.2f}, balance=${updated_balance:.2f}",
            )

    logger.info("Billing item created", job_id=job_id, partner_id=partner_id, amount_cents=amount_cents)
    return amount_cents


def get_partner_usage(partner_id: str, period: str | None = None) -> dict:
    """Get billing summary for a partner. Period format: 'YYYY-MM' or 'YYYY-MM-DD'."""
    with get_db_ctx() as db:
        if period and not re.match(r"^\d{4}-\d{2}(-\d{2})?$", period):
            return {"partner_id": partner_id, "error": "Invalid period format (use YYYY-MM or YYYY-MM-DD)"}
        if period:
            rows = db.execute(
                """
                SELECT SUM(amount_cents) as total_cents, SUM(duration_seconds) as total_seconds,
                       COUNT(*) as job_count
                FROM billing_items
                WHERE partner_id = ? AND created_at LIKE ?
                """,
                (partner_id, f"{period}%"),
            ).fetchone()
        else:
            rows = db.execute(
                """
                SELECT SUM(amount_cents) as total_cents, SUM(duration_seconds) as total_seconds,
                       COUNT(*) as job_count
                FROM billing_items WHERE partner_id = ?
                """,
                (partner_id,),
            ).fetchone()

    return {
        "partner_id": partner_id,
        "period": period or "all_time",
        "total_cents": rows["total_cents"] or 0,
        "total_seconds": rows["total_seconds"] or 0.0,
        "total_minutes": round((rows["total_seconds"] or 0) / 60, 2),
        "job_count": rows["job_count"] or 0,
    }


def get_billing_items(partner_id: str, limit: int = 50, offset: int = 0) -> list[dict]:
    """Get billing line items for a partner."""
    with get_db_ctx() as db:
        rows = db.execute(
            """
            SELECT * FROM billing_items
            WHERE partner_id = ?
            ORDER BY created_at DESC LIMIT ? OFFSET ?
            """,
            (partner_id, limit, offset),
        ).fetchall()

    return [
        {
            "billing_id": row["billing_id"],
            "job_id": row["job_id"],
            "duration_seconds": row["duration_seconds"],
            "rate_per_minute": row["rate_per_minute"],
            "amount_cents": row["amount_cents"],
            "status": row["status"],
            "created_at": row["created_at"],
        }
        for row in rows
    ]
