"""
BIBIVIDI — Marketplace revenue split (TODO-22)

Computes and records per-author payout rows for paid chain executions.

Revenue model
─────────────
When a paid chain skill executes:
  • The chain author earns CHAIN_SHARE (40 %) of the skill's marketplace_price_eur.
  • Each dep author earns an equal share of DEP_POOL (40 %) unless step_weights
    overrides the allocation (weights must sum ≤ 1.0).
  • The platform retains the remainder (≥ 20 %).

Only skills whose author_id is set receive a row; the platform keeps any
unattributed share.
"""
from __future__ import annotations

import logging
from collections.abc import Sequence
from dataclasses import dataclass, field
from datetime import UTC, datetime

import aiosqlite

from bvd.config import settings

logger = logging.getLogger(__name__)

# Default revenue split constants
CHAIN_SHARE = 0.40   # fraction going to the chain author
DEP_POOL = 0.40      # fraction shared among dep skill authors


@dataclass
class PayoutRow:
    skill_id: str
    author_id: str | None
    role: str                   # 'chain' | 'dep'
    step_num: int | None        # None for chain author
    weight: float
    gross_revenue_eur: float
    author_share_eur: float


@dataclass
class AuthorSplit:
    gross_revenue_eur: float
    rows: list[PayoutRow] = field(default_factory=list)


def compute_author_split(
    chain_script: object,              # Script with marketplace_price_eur, author_id, chain_ids
    dep_scripts: Sequence[object],      # Script objects for each dep (in order)
) -> AuthorSplit:
    """Compute payout rows for a single chain execution.

    ``chain_script.marketplace_price_eur`` is the gross revenue for this
    execution. If it is None or ≤ 0 an empty split is returned.

    ``dep_scripts`` must be in the same order as ``chain_script.chain_ids``.
    Missing dep scripts (None entries) are treated as having no author.
    """
    price = getattr(chain_script, "marketplace_price_eur", None)
    if not price or price <= 0:
        return AuthorSplit(gross_revenue_eur=0.0)

    rows: list[PayoutRow] = []

    # ── Chain author row ─────────────────────────────────────────────────────
    chain_author = getattr(chain_script, "author_id", None)
    rows.append(
        PayoutRow(
            skill_id=chain_script.id,  # type: ignore[attr-defined]
            author_id=chain_author,
            role="chain",
            step_num=None,
            weight=CHAIN_SHARE,
            gross_revenue_eur=price,
            author_share_eur=round(price * CHAIN_SHARE, 6),
        )
    )

    # ── Dep author rows ───────────────────────────────────────────────────────
    if not dep_scripts:
        return AuthorSplit(gross_revenue_eur=price, rows=rows)

    # Resolve per-dep weights from step_weights or equal split
    step_weights: dict[str, float] = getattr(chain_script, "step_weights", None) or {}
    n_deps = len(dep_scripts)

    for step_num, dep in enumerate(dep_scripts):
        if dep is None:
            continue
        dep_id = dep.id  # type: ignore[attr-defined]
        # Custom weight if provided, else equal share of DEP_POOL
        if step_weights and dep_id in step_weights:
            weight = float(step_weights[dep_id])
        else:
            weight = DEP_POOL / n_deps

        author_share = round(price * weight, 6)
        rows.append(
            PayoutRow(
                skill_id=dep_id,
                author_id=getattr(dep, "author_id", None),
                role="dep",
                step_num=step_num,
                weight=weight,
                gross_revenue_eur=price,
                author_share_eur=author_share,
            )
        )

    return AuthorSplit(gross_revenue_eur=price, rows=rows)


async def record_execution_payouts(
    execution_id: int,
    split: AuthorSplit,
) -> None:
    """Persist payout rows to marketplace_payouts for a completed execution."""
    if not split.rows:
        return

    now = datetime.now(UTC).isoformat()
    assert settings.db_path is not None
    async with aiosqlite.connect(settings.db_path) as db:  # type: ignore[arg-type]
        for row in split.rows:
            await db.execute(
                """
                INSERT INTO marketplace_payouts
                    (execution_id, skill_id, author_id, role, step_num,
                     weight, gross_revenue_eur, author_share_eur, status, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?)
                """,
                (
                    execution_id,
                    row.skill_id,
                    row.author_id,
                    row.role,
                    row.step_num,
                    row.weight,
                    row.gross_revenue_eur,
                    row.author_share_eur,
                    now,
                ),
            )
        await db.commit()
    logger.info(
        "Recorded %d payout row(s) for execution %d (gross €%.4f)",
        len(split.rows),
        execution_id,
        split.gross_revenue_eur,
    )


async def list_pending_payouts(limit: int = 200) -> list[dict]:
    """Return pending payout rows ordered by created_at descending."""
    assert settings.db_path is not None
    try:
        async with aiosqlite.connect(settings.db_path) as db:  # type: ignore[arg-type]
            db.row_factory = aiosqlite.Row
            async with db.execute(
                """
                SELECT id, execution_id, skill_id, author_id, role, step_num,
                       weight, gross_revenue_eur, author_share_eur, status, created_at
                FROM marketplace_payouts
                WHERE status = 'pending'
                ORDER BY created_at DESC
                LIMIT ?
                """,
                (limit,),
            ) as cur:
                rows = await cur.fetchall()
        return [dict(r) for r in rows]
    except Exception:
        return []


async def mark_payout_paid(payout_id: int) -> bool:
    """Mark a single payout row as paid. Returns True if found."""
    assert settings.db_path is not None
    now = datetime.now(UTC).isoformat()
    try:
        async with aiosqlite.connect(settings.db_path) as db:  # type: ignore[arg-type]
            async with db.execute(
                "SELECT id FROM marketplace_payouts WHERE id = ? AND status = 'pending'",
                (payout_id,),
            ) as cur:
                row = await cur.fetchone()
            if not row:
                return False
            await db.execute(
                "UPDATE marketplace_payouts SET status = 'paid', paid_at = ? WHERE id = ?",
                (now, payout_id),
            )
            await db.commit()
        return True
    except Exception as exc:
        logger.error("mark_payout_paid(%d) failed: %s", payout_id, exc)
        return False
