"""Inteligencia de carrera: salario, entrevista, briefing empresa — con fallbacks."""

from __future__ import annotations

import re
from collections import defaultdict
from datetime import datetime, timedelta, timezone
from statistics import median
from typing import Any

from sqlalchemy import or_
from sqlalchemy.orm import Session

from app.models import Job, JobScore, Profile, User
from app.services import ai_cv


def salary_intelligence(db: Session, user: User, location: str | None = None, query: str | None = None) -> dict[str, Any]:
    q = db.query(Job).filter(Job.salary_min.isnot(None))
    if location:
        q = q.filter(Job.location.ilike(f"%{location.strip()}%"))
    if query:
        like = f"%{query.strip()}%"
        q = q.filter(or_(Job.title.ilike(like), Job.description.ilike(like)))
    rows = q.order_by(Job.scraped_at.desc()).limit(500).all()
    by_currency: dict[str, list[int]] = defaultdict(list)
    samples = []
    for j in rows:
        cur = (j.salary_currency or "EUR").upper()
        if j.salary_min:
            by_currency[cur].append(int(j.salary_min))
            samples.append(
                {
                    "title": j.title,
                    "company": j.company,
                    "location": j.location,
                    "salary_min": j.salary_min,
                    "salary_max": j.salary_max,
                    "currency": cur,
                    "source": j.source,
                }
            )
    bands = {}
    for cur, vals in by_currency.items():
        vals = sorted(vals)
        bands[cur] = {
            "count": len(vals),
            "p25": vals[max(0, len(vals) // 4)],
            "median": int(median(vals)),
            "p75": vals[min(len(vals) - 1, (3 * len(vals)) // 4)],
            "min": vals[0],
            "max": vals[-1],
        }
    profile = user.profile
    tip = []
    if profile and profile.salary_min and bands:
        cur = (profile.salary_currency or "EUR").upper()
        b = bands.get(cur) or next(iter(bands.values()), None)
        if b:
            if profile.salary_min > b["p75"]:
                tip.append(
                    f"Tu mínimo ({profile.salary_min} {cur}) está por encima del P75 del mercado en muestra ({b['p75']}). "
                    "Considera flexibilidad o roles más senior/remotos US."
                )
            elif profile.salary_min < b["p25"]:
                tip.append(
                    f"Tu mínimo está por debajo del P25 ({b['p25']} {cur}). Puedes negociar al alza en matches altos."
                )
            else:
                tip.append("Tu expectativa salarial está alineada con la banda media de la muestra.")
    if not bands:
        tip.append("Aún hay pocas ofertas con salario publicado. Sincroniza Fuentes ES/INT y vuelve a consultar.")
    return {
        "filters": {"location": location or "", "query": query or ""},
        "bands": bands,
        "sample_size": len(samples),
        "samples": samples[:12],
        "negotiation_tips": tip
        + [
            "Ancla con el P75 de roles similares + evidencia de impacto en CV aprobado.",
            "Si la oferta no publica sueldo, pregunta rango en el primer contacto (dato JobsWorld: contactos extraídos).",
        ],
        "wow": "Salary Intel usa tu feed real (no tablas genéricas USA) — ventaja local España/EU.",
    }


def _fallback_interview(job: Job, profile: Profile | None) -> dict[str, Any]:
    title = job.title or "el puesto"
    skills = (profile.skills or [])[:6] if profile else []
    questions = [
        f"Cuéntame un logro medible relacionado con «{title}».",
        "¿Por qué esta empresa/oferta y no otra similar?",
        "Describe un conflicto o incidencia y cómo la resolviste.",
        "¿Qué harías en tus primeros 30/60/90 días?",
        "¿Cómo te organizas en remoto/híbrido?",
    ]
    if skills:
        questions.append(f"¿Cómo has aplicado {skills[0]} en un proyecto real?")
    if job.requires_english_fluent:
        questions.append("Can you walk us through a recent project in English? (prep honesta si no eres fluido)")
    star = [
        "Situation: contexto breve",
        "Task: tu responsabilidad",
        "Action: qué hiciste tú (verbos de impacto)",
        "Result: métrica o aprendizaje",
    ]
    return {
        "mode": "heuristic",
        "job_id": job.id,
        "questions": questions,
        "star_framework": star,
        "talking_points": [
            f"Alinea 3 skills del perfil con keywords de la oferta: {(job.tags or [])[:5]}",
            "Prepara 1 historia de fracaso + aprendizaje (credibilidad).",
            "Cierra preguntando timeline y criterios de decisión.",
        ],
        "red_flags_to_ask": [
            "¿El equipo es remoto real o 'remote-ish' con solapamiento horario imposible?",
            "¿Hay presupuesto/rango salarial definido?",
        ],
    }


def interview_prep(db: Session, user: User, job: Job) -> dict[str, Any]:
    profile = user.profile
    base = _fallback_interview(job, profile)
    prompt = (
        f"Oferta: {job.title} @ {job.company}\nUbicación: {job.location}\n"
        f"Descripción (recorte):\n{(job.description or '')[:3500]}\n\n"
        f"Perfil: roles={getattr(profile, 'desired_roles', None)}, skills={getattr(profile, 'skills', None)}, "
        f"EN fluido={getattr(profile, 'english_fluent', False)}\n\n"
        "Devuelve Markdown con: ## Preguntas probables (8), ## Respuestas STAR sugeridas (3), "
        "## Preguntas del candidato (5). No inventes experiencia del candidato."
    )
    try:
        md = ai_cv._chat(  # type: ignore[attr-defined]
            "Eres coach de entrevistas en España/UE. Sé concreto y honesto.",
            prompt,
        ) if hasattr(ai_cv, "_chat") else None
    except Exception:
        md = None
    if not md:
        try:
            # reuse improve path internals
            from app.services.ai_cv import _chat_openai_compat

            md = _chat_openai_compat(
                "Eres coach de entrevistas en España/UE. Sé concreto y honesto. Responde en Markdown.",
                prompt,
            )
        except Exception:
            md = None
    if md:
        base["mode"] = "ai"
        base["markdown"] = md
    else:
        base["markdown"] = (
            "## Preguntas probables\n"
            + "\n".join(f"- {q}" for q in base["questions"])
            + "\n\n## STAR\n"
            + "\n".join(f"- {s}" for s in base["star_framework"])
        )
    base["wow"] = "Interview Copilot anclado a ESTA oferta + tu perfil — no plantillas genéricas."
    return base


def company_brief(job: Job) -> dict[str, Any]:
    text = f"{job.company} {job.title} {job.description or ''}"
    emails = re.findall(r"[\w.+-]+@[\w-]+\.[\w.-]+", text)
    stack_hints = []
    for token in [
        "python",
        "java",
        "react",
        "aws",
        "azure",
        "sap",
        "salesforce",
        "enfermer",
        "comercial",
        "contabilidad",
        "docker",
        "kubernetes",
    ]:
        if token in text.lower():
            stack_hints.append(token)
    return {
        "company": job.company or "Empresa no indicada",
        "signals": {
            "remote": job.remote,
            "english_fluent_required": job.requires_english_fluent,
            "hire_from_spain_ok": job.hire_from_spain_ok,
            "source": job.source,
            "location": job.location,
            "published_salary": bool(job.salary_min),
        },
        "stack_or_domain_hints": stack_hints[:10],
        "contacts_found_in_text": list(dict.fromkeys(emails))[:5],
        "angle": (
            "Enfatiza disponibilidad horaria EU y entregables remotos."
            if job.remote
            else "Enfatiza cercanía geográfica y disponibilidad presencial/híbrida."
        ),
        "wow": "Company Brief en 1 clic desde la ficha — acelera decide/apply.",
    }


def match_alerts(db: Session, user: User, alert) -> list[dict[str, Any]]:
    q = db.query(Job)
    if alert.query:
        like = f"%{alert.query}%"
        q = q.filter(or_(Job.title.ilike(like), Job.description.ilike(like), Job.company.ilike(like)))
    if alert.location:
        q = q.filter(Job.location.ilike(f"%{alert.location}%"))
    if alert.remote_only:
        q = q.filter(Job.remote.is_(True))
    if alert.min_salary is not None:
        q = q.filter(Job.salary_min.isnot(None), Job.salary_min >= alert.min_salary)
    jobs = q.order_by(Job.scraped_at.desc()).limit(200).all()
    score_map = {
        s.job_id: s.match_score
        for s in db.query(JobScore).filter(JobScore.user_id == user.id).all()
    }
    out = []
    for j in jobs:
        sc = score_map.get(j.id)
        if sc is None:
            continue
        if sc < float(alert.min_score or 0):
            continue
        out.append(
            {
                "id": j.id,
                "title": j.title,
                "company": j.company,
                "location": j.location,
                "match_score": sc,
                "salary_min": j.salary_min,
                "source": j.source,
            }
        )
    out.sort(key=lambda x: x["match_score"], reverse=True)
    return out[:30]


def dashboard_payload(db: Session, user: User) -> dict[str, Any]:
    from app.models import Application, CV, JobAlert
    from app.services.entitlements import get_entitlements, get_usage

    apps = db.query(Application).filter(Application.user_id == user.id).all()
    by_status: dict[str, int] = defaultdict(int)
    for a in apps:
        by_status[a.status] += 1
    top = (
        db.query(JobScore)
        .filter(JobScore.user_id == user.id)
        .order_by(JobScore.match_score.desc())
        .limit(5)
        .all()
    )
    top_jobs = []
    for s in top:
        j = db.get(Job, s.job_id)
        if j:
            top_jobs.append({"id": j.id, "title": j.title, "score": s.match_score, "company": j.company})
    ents = get_entitlements(db, user)
    usage = get_usage(db, user)
    cvs = db.query(CV).filter(CV.user_id == user.id).count()
    alerts = db.query(JobAlert).filter(JobAlert.user_id == user.id, JobAlert.enabled.is_(True)).count()
    next_actions = []
    if not user.onboarding_done:
        next_actions.append({"cta": "Completa el onboarding (2 min)", "to": "/onboarding", "priority": 1})
    if cvs == 0:
        next_actions.append({"cta": "Sube tu CV base", "to": "/cv", "priority": 2})
    if not top_jobs:
        next_actions.append({"cta": "Sincroniza fuentes y rankea ofertas", "to": "/sources", "priority": 3})
    elif by_status.get("applied", 0) == 0:
        next_actions.append({"cta": "Adapta y aprueba CV para tu top match", "to": f"/jobs/{top_jobs[0]['id']}", "priority": 3})
    if alerts == 0:
        next_actions.append({"cta": "Crea una alerta inteligente", "to": "/alerts", "priority": 4})
    scrape_left = max(0, int(ents.get("scrapes_per_day", 0)) - int(usage.get("scrapes_today", 0)))
    if scrape_left <= 1 and not user.is_admin:
        next_actions.append({"cta": "Te quedan pocos scrapes — mejora a Profesional", "to": "/pricing", "priority": 0})
    next_actions.sort(key=lambda x: x["priority"])
    funnel = {
        "saved": by_status.get("saved", 0),
        "interested": by_status.get("interested", 0),
        "applying": by_status.get("applying", 0),
        "applied": by_status.get("applied", 0),
        "rejected": by_status.get("rejected", 0),
    }
    conversion = 0.0
    if len(apps):
        conversion = round(100 * funnel["applied"] / max(1, len(apps)), 1)
    from app.services.wow_tier_d import mission_today

    return {
        "onboarding_done": bool(user.onboarding_done),
        "applications": dict(by_status),
        "applications_total": len(apps),
        "funnel": funnel,
        "funnel_conversion_pct": conversion,
        "cvs": cvs,
        "alerts": alerts,
        "top_matches": top_jobs,
        "usage": usage,
        "entitlements": {
            "scrapes_per_day": ents.get("scrapes_per_day"),
            "ai_adapts_per_month": ents.get("ai_adapts_per_month"),
            "plan_power": "max" if ents.get("all_sources") else "limited",
        },
        "next_actions": next_actions[:5],
        "conversion_hook": (
            "Hoy: aplica a las de menos de 24 h, esquiva fantasmas y sigue a quien no contestó."
        ),
        "mission": mission_today(db, user),
    }


def readiness_score(db: Session, user: User) -> dict[str, Any]:
    """Score 0-100 de preparación de búsqueda — wow metric."""
    from app.models import Application, CV, CVVersion, JobAlert

    profile = user.profile
    parts: list[tuple[str, int, str]] = []
    parts.append(("onboarding", 10 if user.onboarding_done else 0, "Onboarding completado"))
    has_roles = bool(profile and (profile.desired_roles or profile.keywords))
    parts.append(("perfil_roles", 15 if has_roles else 0, "Roles/keywords definidos"))
    has_loc = bool(profile and profile.location)
    parts.append(("ubicacion", 10 if has_loc else 0, "Ubicación configurada"))
    cvs = db.query(CV).filter(CV.user_id == user.id).count()
    parts.append(("cv_base", 20 if cvs else 0, "CV base cargado"))
    approved = (
        db.query(CVVersion)
        .join(CV)
        .filter(CV.user_id == user.id, CVVersion.review_status == "approved")
        .count()
    )
    parts.append(("cv_aprobado", 15 if approved else 0, "Al menos 1 CV aprobado para envío"))
    scores_n = db.query(JobScore).filter(JobScore.user_id == user.id).count()
    parts.append(("matching", 10 if scores_n else 0, "Ofertas rankeadas"))
    apps = db.query(Application).filter(Application.user_id == user.id).count()
    parts.append(("pipeline", 10 if apps else 0, "Pipeline de candidaturas activo"))
    alerts = db.query(JobAlert).filter(JobAlert.user_id == user.id, JobAlert.enabled.is_(True)).count()
    parts.append(("alertas", 10 if alerts else 0, "Alertas inteligentes"))
    total = sum(p[1] for p in parts)
    maxes = {
        "onboarding": 10,
        "perfil_roles": 15,
        "ubicacion": 10,
        "cv_base": 20,
        "cv_aprobado": 15,
        "matching": 10,
        "pipeline": 10,
        "alertas": 10,
    }
    level = "Crítico"
    if total >= 85:
        level = "Listo para Carrera Max"
    elif total >= 65:
        level = "Fuerte — ya puedes convertir"
    elif total >= 40:
        level = "En marcha"
    elif total >= 20:
        level = "Básico"
    return {
        "score": total,
        "level": level,
        "breakdown": [
            {"key": k, "points": v, "label": lab, "max": maxes[k]} for k, v, lab in parts
        ],
        "wow": "Readiness Score único: mide si tu búsqueda está lista para ganar — no solo contar clicks.",
        "cta": "/dashboard" if total >= 40 else "/onboarding",
    }


def daily_digest(db: Session, user: User) -> dict[str, Any]:
    since = datetime.now(timezone.utc) - timedelta(days=2)
    recent = db.query(Job).filter(Job.scraped_at >= since).order_by(Job.scraped_at.desc()).limit(40).all()
    score_map = {
        s.job_id: s
        for s in db.query(JobScore).filter(JobScore.user_id == user.id).all()
    }
    picks = []
    for j in recent:
        sc = score_map.get(j.id)
        if not sc or sc.match_score < 45:
            continue
        picks.append(
            {
                "id": j.id,
                "title": j.title,
                "company": j.company,
                "score": sc.match_score,
                "location": j.location,
                "remote": j.remote,
                "salary_min": j.salary_min,
                "reasons": (sc.reasons or [])[:2],
            }
        )
    picks.sort(key=lambda x: x["score"], reverse=True)
    picks = picks[:8]
    # Fallback: top scores globales si el digest reciente está vacío (demos / DB fría)
    if len(picks) < 3:
        for s in (
            db.query(JobScore)
            .filter(JobScore.user_id == user.id, JobScore.match_score >= 45)
            .order_by(JobScore.match_score.desc())
            .limit(8)
            .all()
        ):
            if any(p["id"] == s.job_id for p in picks):
                continue
            j = db.get(Job, s.job_id)
            if not j:
                continue
            picks.append(
                {
                    "id": j.id,
                    "title": j.title,
                    "company": j.company,
                    "score": s.match_score,
                    "location": j.location,
                    "remote": j.remote,
                    "salary_min": j.salary_min,
                    "reasons": (s.reasons or [])[:2],
                }
            )
            if len(picks) >= 8:
                break
        picks.sort(key=lambda x: x["score"], reverse=True)
    profile = user.profile
    headline = (
        f"Buenos días{', ' + user.name.split()[0] if user.name else ''}. "
        f"Hay {len(picks)} oportunidades calientes alineadas a tu perfil"
        + (f" en {profile.location}." if profile and profile.location else ".")
    )
    return {
        "headline": headline,
        "picks": picks,
        "focus": [
            "Abre el #1 y genera Interview Copilot (5 min).",
            "Adapta CV y aprueba secciones antes de enviar.",
            "Si el score > 70 y hay contacto, escribe en las primeras 24h.",
        ],
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "wow": "Daily Digest: tu briefing ejecutivo diario — como un headhunter en el bolsillo.",
    }


def career_path(db: Session, user: User) -> dict[str, Any]:
    profile = user.profile
    roles = list(getattr(profile, "desired_roles", None) or [])
    cats = list(getattr(profile, "job_categories", None) or ["general"])
    skills = list(getattr(profile, "skills", None) or [])
    # Market signal from titles
    titles = [j.title for j in db.query(Job).order_by(Job.scraped_at.desc()).limit(300).all()]
    blob = " ".join(titles).lower()
    suggestions = []
    catalog = [
        ("Tech lead / Staff", ["lead", "staff", "principal", "architect"], "tech"),
        ("Especialista cloud / DevOps", ["devops", "sre", "kubernetes", "cloud"], "tech"),
        ("Product / Project Manager", ["product", "project manager", "scrum"], "general"),
        ("Comercial / Account", ["comercial", "account", "sales", "business development"], "sales"),
        ("People / HRBP", ["hr", "people", "rrhh", "talent"], "general"),
        ("Salud clínica senior", ["enfermer", "médic", "supervisor", "planta"], "health"),
        ("Data / Analytics", ["data", "analyst", "bi ", "machine learning"], "tech"),
    ]
    for title, keys, cat in catalog:
        hits = sum(1 for k in keys if k in blob)
        fit = 20 + hits * 8
        if cat in cats:
            fit += 15
        if any(k in " ".join(skills).lower() for k in keys):
            fit += 10
        if any(k in " ".join(roles).lower() for k in keys):
            fit += 12
        suggestions.append(
            {
                "path": title,
                "fit": min(98, fit),
                "signal_hits": hits,
                "why": f"Señal de mercado en tu feed: {hits} menciones recientes; alineado a categorías {cats}.",
                "next_skills": keys[:3],
            }
        )
    suggestions.sort(key=lambda x: x["fit"], reverse=True)
    return {
        "current_roles": roles or ["(define roles en onboarding/perfil)"],
        "paths": suggestions[:5],
        "wow": "Career Path Engine: combina tu perfil con la demanda REAL de tu agregador — no quizzes genéricos.",
    }
