"""Persona Radar Chart — browser page for visualizing persona trait profiles.

Serves at :4548. Displays interactive radar charts for the 12 persona archetypes
with overlay comparison, user profile, and trait details.

Data loaded from config/personas/ YAML files generated in Wave 7.
"""

from __future__ import annotations

import json
import logging
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path

import yaml

logger = logging.getLogger(__name__)

_PROJECT_ROOT = Path(__file__).resolve().parents[3]


def _load_yaml(path: Path) -> list | dict | None:
    if not path.exists():
        return None
    try:
        return yaml.safe_load(path.read_text(encoding="utf-8"))
    except Exception:
        return None


def _load_persona_data() -> dict:
    """Load all persona data from config/personas/ into a single JSON-serializable dict.

    Wave 6: Includes Ojas traits, virtues, dimensions when present.
    """
    from vastian.storage.data_root import get_config_dir

    pdir = get_config_dir(_PROJECT_ROOT) / "personas"

    # Traits
    traits_path = pdir / "persona_traits.yaml"
    traits = _load_yaml(traits_path) if traits_path.exists() else []
    if not isinstance(traits, list):
        traits = []

    # Archetypes
    archetypes = []
    arcdir = pdir / "archetypes"
    if arcdir.exists():
        for f in sorted(arcdir.glob("*.yaml")):
            data = _load_yaml(f)
            if data and "persona_id" in data:
                archetypes.append(data)
    archetypes.sort(key=lambda a: a.get("persona_id", 0))

    # Radar weights
    rw_path = pdir / "radar_weights.yaml"
    radar = _load_yaml(rw_path) if rw_path.exists() else {}
    if not isinstance(radar, dict):
        radar = {}

    # Insights
    ins_path = pdir / "insights_lookup.yaml"
    insights = _load_yaml(ins_path) if ins_path.exists() else []
    if not isinstance(insights, list):
        insights = []

    # Wave 6: Ojas traits, virtues, dimensions
    ojas_dir = pdir / "ojas"
    ojas_data = {}
    if ojas_dir.exists():
        for name in ("traits", "virtues", "dimensions"):
            data = _load_yaml(ojas_dir / f"{name}.yaml")
            if data is not None:
                ojas_data[name] = data

    # Sample user profile (default — updated during onboarding)
    user_profile = {
        "name": "User",
        "dominant_persona": "The Light",
        "secondary_persona": "The Manifestor",
        "weakest_persona": "The Core Integrator",
        "strongest_trait": "Discipline",
        "weakest_trait": "Humor",
        "trait_scores": {
            "Discipline": 0.65,
            "Creativity": 0.60,
            "Humor": 0.35,
            "Compassion": 0.55,
            "Wisdom": 0.70,
            "Visionary Insight": 0.75,
            "Adaptability": 0.60,
            "Manifestation": 0.72,
            "Spiritual Vision": 0.80,
            "Courage": 0.58,
            "Compassionate Leadership": 0.62,
            "Patience": 0.50,
        },
    }

    result = {
        "traits": traits,
        "archetypes": archetypes,
        "radar_weights": radar,
        "insights": insights,
        "user_profile": user_profile,
    }
    if ojas_data:
        result["ojas"] = ojas_data
    return result


CHART_HTML = r"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Persona Radar Chart</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    background: #0d1117; color: #c9d1d9;
  }
  .top-bar {
    background: #161b22; border-bottom: 1px solid #30363d;
    padding: 16px 24px; display: flex; justify-content: space-between; align-items: center;
    position: sticky; top: 0; z-index: 100;
  }
  .top-bar h1 { color: #58a6ff; font-size: 1.3em; }
  .top-bar .subtitle { color: #8b949e; font-size: 0.82em; }

  .main { display: flex; gap: 20px; padding: 20px 24px; }
  .chart-area { flex: 1; min-width: 0; }
  .sidebar { width: 320px; flex-shrink: 0; }

  canvas { background: #161b22; border-radius: 12px; border: 1px solid #30363d; display: block; }

  .controls {
    display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; align-items: center;
  }
  .controls label { color: #8b949e; font-size: 0.82em; }
  .controls select, .controls button {
    background: #161b22; color: #c9d1d9; border: 1px solid #30363d;
    border-radius: 6px; padding: 6px 12px; font-size: 0.85em; font-family: inherit; cursor: pointer;
  }
  .controls button:hover { background: #21262d; border-color: #58a6ff; }
  .controls button.active { background: #1f6feb; color: #fff; border-color: #1f6feb; }

  /* Persona cards */
  .persona-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 16px;
  }
  .p-card {
    background: #161b22; border: 1px solid #30363d; border-radius: 8px;
    padding: 10px; cursor: pointer; transition: all 0.15s; font-size: 0.8em;
  }
  .p-card:hover { border-color: #58a6ff; }
  .p-card.selected { border-color: #58a6ff; background: #1f6feb22; }
  .p-card .p-name { font-weight: 600; color: #f0f6fc; margin-bottom: 2px; }
  .p-card .p-detail { color: #8b949e; font-size: 0.85em; }

  /* Trait panel */
  .trait-panel {
    background: #161b22; border: 1px solid #30363d; border-radius: 8px;
    padding: 14px; margin-bottom: 12px;
  }
  .trait-panel h3 { color: #58a6ff; font-size: 0.95em; margin-bottom: 8px; }
  .trait-row {
    display: flex; justify-content: space-between; align-items: center;
    padding: 4px 0; font-size: 0.82em; border-bottom: 1px solid #21262d;
  }
  .trait-row .t-name { color: #c9d1d9; }
  .trait-row .t-bar { width: 100px; height: 6px; background: #21262d; border-radius: 3px; margin: 0 8px; flex-shrink: 0; }
  .trait-row .t-fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
  .trait-row .t-val { color: #8b949e; min-width: 36px; text-align: right; }

  /* User profile card */
  .user-card {
    background: #132a13; border: 1px solid #238636; border-radius: 8px;
    padding: 14px; margin-bottom: 12px;
  }
  .user-card h3 { color: #3fb950; font-size: 0.95em; margin-bottom: 8px; }
  .user-card .u-line { color: #8b949e; font-size: 0.82em; padding: 2px 0; }
  .user-card .u-label { color: #6e7681; }

  /* Legend */
  .legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 10px; font-size: 0.78em; }
  .legend-item { display: flex; align-items: center; gap: 4px; }
  .legend-dot { width: 10px; height: 10px; border-radius: 50%; }

  @media (max-width: 900px) {
    .main { flex-direction: column; }
    .sidebar { width: 100%; }
  }
</style>
</head>
<body>

<div class="top-bar">
  <div>
    <h1>Persona Radar Chart</h1>
    <div class="subtitle">12 archetypes x 12 traits — select personas to compare</div>
  </div>
</div>

<div class="main">
  <div class="chart-area">
    <div class="controls">
      <label>Compare:</label>
      <button id="btnUser" class="active" onclick="toggleUser()">Your Profile</button>
      <button id="btnAll" onclick="showAll()">All Personas</button>
      <button onclick="clearSelection()">Clear</button>
      <label style="margin-left:12px">View:</label>
      <select id="viewMode" onchange="draw()">
        <option value="raw">Raw Scores</option>
        <option value="weighted">Weighted Scores</option>
      </select>
    </div>
    <canvas id="radar" width="700" height="700"></canvas>
    <div class="legend" id="legend"></div>
  </div>

  <div class="sidebar">
    <div class="user-card" id="userCard"></div>
    <div class="trait-panel" id="traitPanel">
      <h3>Trait Breakdown</h3>
      <div id="traitRows">Select a persona to see trait details</div>
    </div>
    <div class="persona-grid" id="personaGrid"></div>
  </div>
</div>

<script>
// Data injected by server
const DATA = __PERSONA_DATA__;

const COLORS = [
  '#58a6ff','#f85149','#3fb950','#d29922','#a371f7','#f778ba',
  '#79c0ff','#ffa657','#7ee787','#d2a8ff','#ff7b72','#56d364',
];
const USER_COLOR = '#3fb950';

const traitNames = DATA.traits.map(t => t.name);
const personas = DATA.archetypes;
const radarWeights = DATA.radar_weights;
const userProfile = DATA.user_profile;

let selectedIds = new Set();
let showUser = true;

// Build persona grid
function buildGrid() {
  const grid = document.getElementById('personaGrid');
  grid.innerHTML = personas.map(p => {
    const id = p.persona_id;
    return `<div class="p-card" id="pcard-${id}" onclick="togglePersona(${id})">
      <div class="p-name">${esc(p.name)}</div>
      <div class="p-detail">${esc(p.dominant_trait)} dominant</div>
    </div>`;
  }).join('');
}

// Build user profile card
function buildUserCard() {
  const u = userProfile;
  document.getElementById('userCard').innerHTML = `
    <h3>${esc(u.name)}'s Profile</h3>
    <div class="u-line"><span class="u-label">Dominant:</span> ${esc(u.dominant_persona)}</div>
    <div class="u-line"><span class="u-label">Secondary:</span> ${esc(u.secondary_persona)}</div>
    <div class="u-line"><span class="u-label">Weakest:</span> ${esc(u.weakest_persona)}</div>
    <div class="u-line"><span class="u-label">Strongest Trait:</span> ${esc(u.strongest_trait)}</div>
    <div class="u-line"><span class="u-label">Weakest Trait:</span> ${esc(u.weakest_trait)}</div>
  `;
}

function togglePersona(id) {
  if (selectedIds.has(id)) selectedIds.delete(id);
  else selectedIds.add(id);
  updateSelection();
}

function toggleUser() {
  showUser = !showUser;
  document.getElementById('btnUser').classList.toggle('active', showUser);
  draw();
}

function showAll() {
  selectedIds = new Set(personas.map(p => p.persona_id));
  updateSelection();
}

function clearSelection() {
  selectedIds.clear();
  updateSelection();
}

function updateSelection() {
  personas.forEach(p => {
    const el = document.getElementById('pcard-' + p.persona_id);
    if (el) el.classList.toggle('selected', selectedIds.has(p.persona_id));
  });
  // Update trait panel for first selected
  if (selectedIds.size > 0) {
    const firstId = [...selectedIds][selectedIds.size - 1];
    const p = personas.find(x => x.persona_id === firstId);
    if (p) showTraitBreakdown(p);
  }
  draw();
}

function showTraitBreakdown(persona) {
  const scores = persona.default_scores || {};
  const rows = DATA.traits.map((t, i) => {
    const val = scores[t.trait_id] || scores[String(t.trait_id)] || 0;
    const pct = Math.round(val * 100);
    const color = val > 0.7 ? '#3fb950' : val > 0.4 ? '#d29922' : '#f85149';
    return `<div class="trait-row">
      <span class="t-name">${esc(t.name)}</span>
      <div class="t-bar"><div class="t-fill" style="width:${pct}%;background:${color}"></div></div>
      <span class="t-val">${pct}%</span>
    </div>`;
  }).join('');
  document.getElementById('traitPanel').innerHTML = `<h3>${esc(persona.name)}</h3>${rows}`;
}

// Radar chart drawing
function draw() {
  const canvas = document.getElementById('radar');
  const ctx = canvas.getContext('2d');
  const W = canvas.width, H = canvas.height;
  const cx = W / 2, cy = H / 2;
  const R = Math.min(cx, cy) - 60;
  const n = traitNames.length;
  const mode = document.getElementById('viewMode').value;

  ctx.clearRect(0, 0, W, H);

  // Background rings
  for (let ring = 1; ring <= 5; ring++) {
    const r = R * ring / 5;
    ctx.beginPath();
    for (let i = 0; i <= n; i++) {
      const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
      const x = cx + r * Math.cos(angle);
      const y = cy + r * Math.sin(angle);
      i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    }
    ctx.strokeStyle = '#21262d';
    ctx.lineWidth = 1;
    ctx.stroke();
    // Ring label
    ctx.fillStyle = '#484f58';
    ctx.font = '10px sans-serif';
    ctx.fillText((ring * 20) + '%', cx + 4, cy - r + 12);
  }

  // Axis lines and labels
  for (let i = 0; i < n; i++) {
    const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
    const x = cx + R * Math.cos(angle);
    const y = cy + R * Math.sin(angle);
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.lineTo(x, y);
    ctx.strokeStyle = '#21262d';
    ctx.lineWidth = 1;
    ctx.stroke();
    // Label
    const lx = cx + (R + 28) * Math.cos(angle);
    const ly = cy + (R + 28) * Math.sin(angle);
    ctx.fillStyle = '#8b949e';
    ctx.font = '11px sans-serif';
    ctx.textAlign = angle > Math.PI / 2 - 0.1 && angle < Math.PI * 1.5 + 0.1 ? 'right' : 'left';
    if (Math.abs(angle + Math.PI / 2) < 0.2) ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(traitNames[i], lx, ly);
  }

  // Draw function for a single dataset
  function drawDataset(values, color, alpha, lineWidth) {
    ctx.beginPath();
    for (let i = 0; i <= n; i++) {
      const idx = i % n;
      const val = Math.min(values[idx] || 0, 1);
      const angle = (Math.PI * 2 * idx / n) - Math.PI / 2;
      const r = R * val;
      const x = cx + r * Math.cos(angle);
      const y = cy + r * Math.sin(angle);
      i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    }
    ctx.closePath();
    ctx.fillStyle = color + Math.round(alpha * 255).toString(16).padStart(2, '0');
    ctx.fill();
    ctx.strokeStyle = color;
    ctx.lineWidth = lineWidth;
    ctx.stroke();

    // Draw dots
    for (let i = 0; i < n; i++) {
      const val = Math.min(values[i] || 0, 1);
      const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
      const r = R * val;
      ctx.beginPath();
      ctx.arc(cx + r * Math.cos(angle), cy + r * Math.sin(angle), 3, 0, Math.PI * 2);
      ctx.fillStyle = color;
      ctx.fill();
    }
  }

  // Draw selected personas
  let legendHtml = '';
  let colorIdx = 0;
  for (const pid of selectedIds) {
    const p = personas.find(x => x.persona_id === pid);
    if (!p) continue;
    const pname = p.name;
    const color = COLORS[colorIdx % COLORS.length];
    colorIdx++;

    let values;
    if (mode === 'weighted' && radarWeights[pname]) {
      values = traitNames.map(t => {
        const entry = radarWeights[pname][t];
        return entry ? entry.weighted * 10 : 0; // Scale weighted to 0-1 range (weighted are ~0.02-0.1)
      });
    } else {
      values = traitNames.map(t => {
        const scores = p.default_scores || {};
        const tobj = DATA.traits.find(tr => tr.name === t);
        if (!tobj) return 0;
        return scores[tobj.trait_id] || scores[String(tobj.trait_id)] || 0;
      });
    }

    const alpha = selectedIds.size > 3 ? 0.08 : 0.15;
    drawDataset(values, color, alpha, 2);
    legendHtml += `<div class="legend-item"><div class="legend-dot" style="background:${color}"></div>${esc(pname)}</div>`;
  }

  // Draw user profile
  if (showUser) {
    const uValues = traitNames.map(t => userProfile.trait_scores[t] || 0);
    drawDataset(uValues, USER_COLOR, 0.12, 3);
    legendHtml = `<div class="legend-item"><div class="legend-dot" style="background:${USER_COLOR}"></div>${esc(userProfile.name)} (You)</div>` + legendHtml;
  }

  document.getElementById('legend').innerHTML = legendHtml;
}

function esc(s) { const d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }

// Init
buildGrid();
buildUserCard();
draw();
</script>
</body>
</html>"""


class PersonaChartPage:
    """Serves the persona radar chart browser page."""

    def __init__(self, port: int = 4548) -> None:
        self.port = port
        self._server: HTTPServer | None = None
        self._thread: threading.Thread | None = None
        self._data: dict | None = None

    def _get_data(self) -> dict:
        if self._data is None:
            self._data = _load_persona_data()
        return self._data

    def start(self) -> str:
        chart = self

        class Handler(BaseHTTPRequestHandler):
            def do_GET(self) -> None:  # noqa: N802
                try:
                    if self.path == "/api/persona-data":
                        data = chart._get_data()
                        body = json.dumps(data, default=str).encode("utf-8")
                        self.send_response(200)
                        self.send_header("Content-Type", "application/json")
                        self.send_header("Content-Length", str(len(body)))
                        self.end_headers()
                        self.wfile.write(body)
                    else:
                        html = chart._render_html()
                        body = html.encode("utf-8")
                        self.send_response(200)
                        self.send_header("Content-Type", "text/html; charset=utf-8")
                        self.send_header("Content-Length", str(len(body)))
                        self.send_header("Cache-Control", "no-store")
                        self.end_headers()
                        self.wfile.write(body)
                except (ConnectionAbortedError, ConnectionResetError, BrokenPipeError):
                    pass

            def log_message(self, *args: object) -> None:
                pass

        class ReusableHTTPServer(HTTPServer):
            allow_reuse_address = True
            allow_reuse_port = True

        try:
            self._server = ReusableHTTPServer(("127.0.0.1", self.port), Handler)
        except OSError:
            self.port += 10
            self._server = ReusableHTTPServer(("127.0.0.1", self.port), Handler)

        self._thread = threading.Thread(
            target=self._server.serve_forever,
            daemon=True,
            name="persona-chart",
        )
        self._thread.start()
        url = f"http://127.0.0.1:{self.port}"
        logger.info("Persona chart started at %s", url)
        return url

    def stop(self) -> None:
        if self._server:
            self._server.shutdown()
            self._server = None

    def _render_html(self) -> str:
        data = self._get_data()
        data_json = json.dumps(data, default=str)
        return CHART_HTML.replace("__PERSONA_DATA__", data_json)
