"""Cola de propuestas de negocio (lab propone → humano acepta → Bridge ejecuta)."""

from __future__ import annotations

import json
import secrets
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from app.core.config import get_settings
from app.services import brand, bridge_exec
from app.services import ai_cv

RISK_TIERS = ("L0", "L1", "L2", "L3", "L4")
KINDS = (
    "marketing_campaign",
    "brand_swap",
    "code_fix",
    "ops",
    "pricing",
    "product",
)


def _store_path() -> Path:
    p = get_settings().data_dir / "proposals.json"
    if not p.exists():
        p.write_text("[]", encoding="utf-8")
    return p


def _load() -> list[dict[str, Any]]:
    try:
        data = json.loads(_store_path().read_text(encoding="utf-8"))
        return data if isinstance(data, list) else []
    except Exception:  # noqa: BLE001
        return []


def _save(items: list[dict[str, Any]]) -> None:
    _store_path().write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")


def list_proposals(status: str | None = None) -> list[dict[str, Any]]:
    items = _load()
    if status:
        items = [i for i in items if i.get("status") == status]
    return sorted(items, key=lambda x: x.get("created_at") or "", reverse=True)


def get_proposal(pid: str) -> dict[str, Any] | None:
    for i in _load():
        if i.get("id") == pid:
            return i
    return None


def _upsert(item: dict[str, Any]) -> dict[str, Any]:
    items = _load()
    for idx, it in enumerate(items):
        if it.get("id") == item["id"]:
            items[idx] = item
            _save(items)
            return item
    items.insert(0, item)
    _save(items)
    return item


def create_proposal(
    *,
    title: str,
    kind: str = "marketing_campaign",
    risk_tier: str = "L1",
    prompt_for_bridge: str,
    impact_kpi: list[str] | None = None,
    files_hint: list[str] | None = None,
    rationale: str = "",
    source: str = "lab_agent",
) -> dict[str, Any]:
    if kind not in KINDS:
        kind = "product"
    if risk_tier not in RISK_TIERS:
        risk_tier = "L1"
    now = datetime.now(timezone.utc).isoformat()
    item = {
        "id": f"jw-{datetime.now(timezone.utc).strftime('%Y%m%d')}-{secrets.token_hex(3)}",
        "app": "jobsworld",
        "kind": kind,
        "title": title.strip()[:200],
        "risk_tier": risk_tier,
        "impact_kpi": impact_kpi or ["waitlist_signups", "paid_conversions"],
        "prompt_for_bridge": prompt_for_bridge.strip(),
        "files_hint": files_hint or [],
        "rationale": rationale.strip()[:2000],
        "source": source,
        "status": "proposed",
        "created_at": now,
        "updated_at": now,
        "execution": None,
    }
    return _upsert(item)


def set_status(pid: str, status: str, extra: dict[str, Any] | None = None) -> dict[str, Any]:
    item = get_proposal(pid)
    if not item:
        raise FileNotFoundError(pid)
    item["status"] = status
    item["updated_at"] = datetime.now(timezone.utc).isoformat()
    if extra:
        item.update(extra)
    return _upsert(item)


def reject(pid: str, reason: str = "") -> dict[str, Any]:
    return set_status(pid, "rejected", {"reject_reason": reason})


def accept_and_execute(pid: str, *, wait: bool = False) -> dict[str, Any]:
    item = get_proposal(pid)
    if not item:
        raise FileNotFoundError(pid)
    if item.get("status") not in {"proposed", "accepted", "failed"}:
        raise ValueError(f"estado inválido: {item.get('status')}")

    risk = item.get("risk_tier") or "L1"
    if risk == "L4":
        raise ValueError("L4 (dinero/legal) no se auto-ejecuta")

    brand_pack = brand.get_active()
    prompt = (
        f"MODO EJECUCIÓN JobsWorld — propuesta aceptada por admin.\n"
        f"Marca activa: {brand_pack.get('name')} ({brand_pack.get('id')}).\n"
        f"Risk: {risk}. Kind: {item.get('kind')}.\n"
        f"Archivos sugeridos: {', '.join(item.get('files_hint') or []) or 'los que toquen'}.\n"
        f"Propón diffs concretos (propose). No inventes secretos ni toques Stripe keys.\n\n"
        f"## Título\n{item.get('title')}\n\n"
        f"## Rationale\n{item.get('rationale')}\n\n"
        f"## Tarea\n{item.get('prompt_for_bridge')}\n"
    )

    set_status(pid, "executing")
    result = bridge_exec.execute_prompt(prompt, wait=wait)
    status = "done" if result.get("ok") else "failed"
    return set_status(
        pid,
        status if wait or not result.get("ok") else "executing",
        {
            "execution": {
                "at": datetime.now(timezone.utc).isoformat(),
                "bridge": result,
            }
        },
    )


SEED_PROMPTS = [
    {
        "title": "Landing: gancho precio irresistible + prueba social",
        "kind": "marketing_campaign",
        "risk_tier": "L2",
        "impact_kpi": ["landing_cta_clicks", "waitlist_signups"],
        "files_hint": ["frontend/src/pages/LandingPage.tsx", "frontend/src/pages/PricingPage.tsx"],
        "rationale": "Competimos con Teal/Huntr (~25–40€). Nuestro héroe debe ser volumen a precio bajo.",
        "prompt_for_bridge": (
            "Mejora LandingPage y PricingPage para enfatizar: plan Profesional a ~15€/mes, "
            "España-first + remoto INT, IA local, CV con revisión humana. CTA claro a /pricing y demo. "
            "Mantén BrandPack CSS vars (--brand-*). No rompas móvil/PWA."
        ),
    },
    {
        "title": "Campaña LinkedIn 7 días (copy listo)",
        "kind": "marketing_campaign",
        "risk_tier": "L1",
        "impact_kpi": ["utm_linkedin", "waitlist_signups"],
        "files_hint": ["docs/campaigns/", "backend/app/api/marketing.py"],
        "rationale": "Canal #1 para empleo ES. Necesitamos secuencia coherente con UTMs.",
        "prompt_for_bridge": (
            "Crea docs/campaigns/linkedin-week1.md con 7 posts (ES), hooks, CTA a waitlist "
            "con UTM utm_source=linkedin&utm_campaign=week1. Incluye variantes para junior y senior."
        ),
    },
    {
        "title": "Onboarding → valor en 3 minutos",
        "kind": "product",
        "risk_tier": "L2",
        "impact_kpi": ["onboarding_done", "first_adapt"],
        "files_hint": ["frontend/src/pages/OnboardingPage.tsx", "frontend/src/pages/DashboardPage.tsx"],
        "rationale": "Retención: el usuario debe sentir wow antes del día 1.",
        "prompt_for_bridge": (
            "Acelera onboarding: 3 pasos máximos (perfil → CV → feed). "
            "En dashboard muestra checklist de primeros resultados y upsell suave a Profesional."
        ),
    },
    {
        "title": "SEO: página insight salarial semanal compartible",
        "kind": "marketing_campaign",
        "risk_tier": "L1",
        "impact_kpi": ["organic_visits", "referral_shares"],
        "files_hint": ["frontend/src/pages/WeeklyIndexPage.tsx", "frontend/src/pages/SalaryIndexPublicPage.tsx"],
        "rationale": "Lead magnet gratuito que atrae suscriptores.",
        "prompt_for_bridge": (
            "Potencia WeeklyIndexPage / SalaryIndexPublicPage como lead magnet: "
            "título SEO, resumen shareable, CTA registro gratis + upgrade."
        ),
    },
]


def generate_from_lab(*, use_llm: bool = True, count: int = 4) -> list[dict[str, Any]]:
    """Genera propuestas: LLM lab si responde JSON; si no, seed de producto."""
    created: list[dict[str, Any]] = []
    if use_llm:
        system = (
            "Eres estratega de producto de un portal de empleo ES+INT (JobsWorld). "
            "Responde SOLO JSON array de 3-5 objetos con keys: "
            "title, kind, risk_tier, impact_kpi, files_hint, rationale, prompt_for_bridge. "
            "kind in marketing_campaign|product|pricing|ops|code_fix|brand_swap. "
            "risk_tier L0-L3. Objetivo: muchas suscripciones mensuales a precio irresistible."
        )
        active = brand.get_active()
        user = (
            f"Marca: {active.get('name')}. Tagline: {active.get('tagline')}. "
            "Planes: Free / Esencial ~8€ / Profesional ~15€ (héroe) / Carrera Max ~25€. "
            "Diferenciadores: España-first, IA local Ollama, CV con gate humano, portales ES+INT. "
            "Propón acciones concretas ejecutables por un agente Cursor en el repo."
        )
        try:
            raw = ai_cv._chat(system, user)  # type: ignore[attr-defined]
            text = raw.strip()
            if text.startswith("```"):
                text = text.strip("`")
                if text.startswith("json"):
                    text = text[4:].strip()
            start = text.find("[")
            end = text.rfind("]")
            if start >= 0 and end > start:
                arr = json.loads(text[start : end + 1])
                if isinstance(arr, list):
                    for obj in arr[:count]:
                        if not isinstance(obj, dict) or not obj.get("title"):
                            continue
                        created.append(
                            create_proposal(
                                title=str(obj.get("title")),
                                kind=str(obj.get("kind") or "product"),
                                risk_tier=str(obj.get("risk_tier") or "L1"),
                                prompt_for_bridge=str(obj.get("prompt_for_bridge") or obj.get("title")),
                                impact_kpi=list(obj.get("impact_kpi") or []),
                                files_hint=list(obj.get("files_hint") or []),
                                rationale=str(obj.get("rationale") or ""),
                                source="lab_ollama",
                            )
                        )
        except Exception:  # noqa: BLE001
            pass

    if not created:
        for seed in SEED_PROMPTS[:count]:
            created.append(create_proposal(**seed, source="seed_playbook"))
    return created


def ensure_seed_if_empty() -> list[dict[str, Any]]:
    items = _load()
    if items:
        return items
    return generate_from_lab(use_llm=False, count=4)
