#!/usr/bin/env bun
/**
 * Generate a standalone HTML featurette report from destination_featurette data.
 * Usage: bun run generate-report.ts
 */

import { resolve } from 'path';
import { readFileSync } from 'fs';

const DATA_DIR = import.meta.dir;

interface ScoresData {
  overall: { geo_score: number; visibility_tier: string };
  decision_moments: Record<string, any>;
  platform_scores: Record<string, any>;
  zero_mention_queries: Array<{ query_id: string; query_text: string }>;
  competitor_analysis: Record<string, any>;
}

interface QueryInput {
  metadata: { clusters: Record<string, string>; total_queries: number; featurette: string; purpose: string };
  queries: { discovery: Array<{ text: string; pattern: string }> };
}

interface MentionedQuery {
  id: string; text: string; cluster: string; chatgpt: boolean; claude: boolean; perplexity: boolean; gemini: boolean; total: number;
}

// ── Load data ──
const scores: ScoresData = JSON.parse(readFileSync(resolve(DATA_DIR, 'scores.json'), 'utf-8'));
const queriesRaw: QueryInput = JSON.parse(readFileSync(resolve(DATA_DIR, '..', 'queries_destination_featurette.json'), 'utf-8'));
const analysis = JSON.parse(readFileSync(resolve(DATA_DIR, 'analysis.json'), 'utf-8'));

// ── Parse CSV to get per-query mentions ──
const csvRaw = readFileSync(resolve(DATA_DIR, 'responses.csv'), 'utf-8');
const csvLines = csvRaw.trim().split('\n').slice(1);
const mentionedQueries: MentionedQuery[] = [];
const zeroMentionQueries: MentionedQuery[] = [];

const queryPatternMap = new Map<string, string>();
queriesRaw.queries.discovery.forEach((q, i) => {
  queryPatternMap.set(`Q${i + 1}`, q.pattern);
});

csvLines.forEach(line => {
  const cols = line.split(',');
  const id = cols[0];
  const text = cols[3];
  const chatgpt = cols[4] === 'Yes';
  const claude = cols[5] === 'Yes';
  const perplexity = cols[6] === 'Yes';
  const gemini = cols[7] === 'Yes';
  const total = parseInt(cols[8], 10);
  const cluster = queryPatternMap.get(id) ?? 'unknown';

  const record: MentionedQuery = { id, text, cluster, chatgpt, claude, perplexity, gemini, total };
  if (total > 0) mentionedQueries.push(record);
  else zeroMentionQueries.push(record);
});

const clusterNames: Record<string, string> = {
  houston_origin: 'Houston Origin',
  austin_origin: 'Austin Origin',
  san_antonio_origin: 'San Antonio Origin',
  taste_profile: 'Taste Profile',
  competitive_substitution: 'Competitive Substitution',
};

// Per-cluster aggregation
const clusterStats: Record<string, { total: number; mentions: number; queries: MentionedQuery[] }> = {};
csvLines.forEach(line => {
  const cols = line.split(',');
  const id = cols[0];
  const total = parseInt(cols[8], 10);
  const cluster = queryPatternMap.get(id) ?? 'unknown';
  if (!clusterStats[cluster]) clusterStats[cluster] = { total: 0, mentions: 0, queries: [] };
  clusterStats[cluster].total += total;
  if (total > 0) clusterStats[cluster].mentions += total;
});

const geoScore = scores.overall.geo_score;
const tier = scores.overall.visibility_tier;
const discoveryScore = scores.decision_moments.discovery.score;
const platforms = ['chatgpt', 'claude', 'perplexity', 'gemini'] as const;
const platformLabels: Record<string, string> = { chatgpt: 'ChatGPT', claude: 'Claude', perplexity: 'Perplexity', gemini: 'Gemini' };

// ── Build HTML ──
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hotel Lucine — Galveston Destination Featurette Report</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
  :root {
    --bg: #0a0a0a;
    --surface: #141414;
    --surface2: #1a1a1a;
    --border: #2a2a2a;
    --text: #e0e0e0;
    --text-muted: #888;
    --accent: #d4a853;
    --accent-dim: rgba(212, 168, 83, 0.15);
    --red: #e05555;
    --red-dim: rgba(224, 85, 85, 0.12);
    --green: #4caf50;
    --green-dim: rgba(76, 175, 80, 0.12);
    --blue: #5b9bd5;
    --blue-dim: rgba(91, 155, 213, 0.12);
  }
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    font-family: 'Space Grotesk', system-ui, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
  }
  .container { max-width: 960px; margin: 0 auto; padding: 0 24px; }

  /* ── Cover ── */
  .cover {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 80px 24px;
    border-bottom: 1px solid var(--border);
  }
  .cover .brand { font-size: 14px; color: var(--accent); letter-spacing: 3px; text-transform: uppercase; margin-bottom: 12px; }
  .cover h1 { font-size: 48px; font-weight: 700; line-height: 1.1; margin-bottom: 8px; }
  .cover .subtitle { font-size: 20px; color: var(--text-muted); margin-bottom: 48px; }
  .cover .score-ring {
    width: 180px; height: 180px;
    border-radius: 50%;
    border: 3px solid var(--red);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    margin-bottom: 24px;
  }
  .cover .score-ring .score-num { font-size: 64px; font-weight: 700; color: var(--red); line-height: 1; }
  .cover .score-ring .score-label { font-size: 13px; color: var(--text-muted); letter-spacing: 2px; text-transform: uppercase; }
  .cover .meta {
    display: flex;
    gap: 32px;
    font-size: 13px;
    color: var(--text-muted);
    margin-top: 32px;
  }
  .cover .meta span { display: block; }
  .cover .meta .val { color: var(--text); font-weight: 500; }

  /* ── Sections ── */
  section { padding: 80px 0; border-bottom: 1px solid var(--border); }
  section:last-child { border-bottom: none; }
  h2 { font-size: 28px; font-weight: 600; margin-bottom: 12px; }
  h3 { font-size: 18px; font-weight: 500; margin-bottom: 16px; color: var(--accent); }
  .section-intro { color: var(--text-muted); font-size: 15px; margin-bottom: 40px; max-width: 680px; }

  /* ── Cards ── */
  .card-grid { display: grid; gap: 20px; margin-bottom: 40px; }
  .card-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
  .card-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
  .card-grid.cols-5 { grid-template-columns: repeat(5, 1fr); }
  @media (max-width: 768px) {
    .card-grid.cols-2, .card-grid.cols-4, .card-grid.cols-5 { grid-template-columns: 1fr; }
  }
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 24px;
  }
  .card .card-label { font-size: 11px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--text-muted); margin-bottom: 8px; }
  .card .card-value { font-size: 36px; font-weight: 700; }
  .card .card-sub { font-size: 13px; color: var(--text-muted); margin-top: 4px; }

  /* ── Severity colors ── */
  .severity-critical { color: var(--red); }
  .severity-warning { color: var(--accent); }
  .severity-ok { color: var(--green); }

  /* ── Progress bar ── */
  .bar-track { height: 8px; background: var(--surface2); border-radius: 4px; margin-top: 8px; overflow: hidden; }
  .bar-fill { height: 100%; border-radius: 4px; transition: width 0.3s; }
  .bar-fill.red { background: var(--red); }
  .bar-fill.accent { background: var(--accent); }
  .bar-fill.green { background: var(--green); }
  .bar-fill.blue { background: var(--blue); }

  /* ── Tables ── */
  table { width: 100%; border-collapse: collapse; font-size: 14px; }
  thead th {
    text-align: left;
    padding: 12px 16px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border);
  }
  tbody td { padding: 12px 16px; border-bottom: 1px solid var(--border); }
  tbody tr:hover { background: var(--surface2); }

  /* ── Check/X marks ── */
  .check { color: var(--green); }
  .cross { color: var(--red); }

  /* ── Cluster tags ── */
  .cluster-tag {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.5px;
  }
  .tag-houston { background: var(--blue-dim); color: var(--blue); }
  .tag-austin { background: rgba(155, 89, 182, 0.15); color: #a67cc5; }
  .tag-sanantonio { background: rgba(241, 196, 15, 0.12); color: #f1c40f; }
  .tag-taste { background: var(--accent-dim); color: var(--accent); }
  .tag-comp { background: var(--red-dim); color: var(--red); }

  /* ── Finding box ── */
  .finding { background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0; padding: 24px; margin: 24px 0; }
  .finding .f-label { font-size: 11px; text-transform: uppercase; letter-spacing: 1.5px; color: var(--accent); margin-bottom: 8px; }
  .finding p { font-size: 15px; color: var(--text); line-height: 1.7; }

  /* ── Mention dot ── */
  .mention-dots { display: flex; gap: 6px; }
  .mention-dot { width: 8px; height: 8px; border-radius: 50%; }
  .mention-dot.on { background: var(--green); }
  .mention-dot.off { background: var(--border); }

  /* ── Footer ── */
  footer {
    padding: 60px 24px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
  }
</style>
</head>
<body>

<!-- ══ COVER ══ -->
<header class="cover">
  <div class="brand">Hotel Lucine</div>
  <h1>Galveston Destination<br>Featurette Report</h1>
  <p class="subtitle">Measuring AI visibility in destination-level queries from key drive markets</p>
  <div class="score-ring">
    <span class="score-num">${geoScore}</span>
    <span class="score-label">GEO Score</span>
  </div>
  <p style="font-size: 18px; font-weight: 500; color: var(--red); margin-bottom: 8px;">${tier}</p>
  <p style="font-size: 14px; color: var(--text-muted); max-width: 480px;">Your property is effectively invisible to AI-powered hotel discovery — travelers using AI assistants for destination-level queries will not find your brand.</p>
  <div class="meta">
    <div><span class="val">${queriesRaw.metadata.total_queries}</span><span>Queries</span></div>
    <div><span class="val">4</span><span>Platforms</span></div>
    <div><span class="val">${analysis.metadata.collection_count}</span><span>Runs</span></div>
    <div><span class="val">5</span><span>Clusters</span></div>
    <div><span class="val">${analysis.metadata.analysis_date}</span><span>Date</span></div>
  </div>
</header>

<!-- ══ EXECUTIVE SUMMARY ══ -->
<section>
  <div class="container">
    <h2>Executive Summary</h2>
    <p class="section-intro">This featurette tests whether Hotel Lucine appears when travelers ask hotel-centric destination questions — queries where the "right" AI answer includes Galveston as a destination. It does not affect the main GEO Visibility Score (${scores.overall.geo_score}).</p>

    <div class="card-grid cols-2">
      <div class="card">
        <div class="card-label">Discovery Score</div>
        <div class="card-value severity-critical">${discoveryScore}</div>
        <div class="card-sub">out of 100 — critical weakness</div>
        <div class="bar-track"><div class="bar-fill red" style="width: ${discoveryScore}%"></div></div>
      </div>
      <div class="card">
        <div class="card-label">Queries With Any Mention</div>
        <div class="card-value" style="color: var(--accent)">${mentionedQueries.length} / ${queriesRaw.metadata.total_queries}</div>
        <div class="card-sub">only ${mentionedQueries.length} of ${queriesRaw.metadata.total_queries} queries had Lucine mentioned on any platform</div>
        <div class="bar-track"><div class="bar-fill accent" style="width: ${(mentionedQueries.length / queriesRaw.metadata.total_queries * 100).toFixed(0)}%"></div></div>
      </div>
    </div>

    <div class="finding">
      <div class="f-label">Core Finding</div>
      <p>Hotel Lucine is <strong>absent from destination-level AI queries</strong>. Travelers asking where to go for a weekend from Houston, Austin, or San Antonio do not see Lucine in AI responses. The one bright spot: Q14 ("Cool boutique hotel in Texas with music events and beachfront location") surfaced Lucine on all 4 platforms, proving the property <em>can</em> appear when the query language aligns precisely with its differentiators. The gap is not about Lucine's quality — it's about the property not being encoded in AI training data as a "Galveston answer" for origin-drive-time queries.</p>
    </div>
  </div>
</section>

<!-- ══ CLUSTER ANALYSIS ══ -->
<section>
  <div class="container">
    <h2>Cluster Performance</h2>
    <p class="section-intro">The 20 queries are organized into 5 strategic clusters. Performance varies significantly by cluster.</p>

    <div class="card-grid cols-5">
      ${['houston_origin', 'austin_origin', 'san_antonio_origin', 'taste_profile', 'competitive_substitution'].map(cluster => {
        const stats = clusterStats[cluster] ?? { total: 0, mentions: 0 };
        const maxMentions = cluster === 'houston_origin' ? 16 : cluster === 'austin_origin' ? 16 : cluster === 'san_antonio_origin' ? 12 : cluster === 'taste_profile' ? 20 : 16;
        const pct = stats.total > 0 ? (stats.mentions / maxMentions * 100).toFixed(0) : 0;
        const color = Number(pct) === 0 ? 'var(--red)' : Number(pct) < 15 ? 'var(--accent)' : 'var(--green)';
        const tagClass = cluster === 'houston_origin' ? 'tag-houston' : cluster === 'austin_origin' ? 'tag-austin' : cluster === 'san_antonio_origin' ? 'tag-sanantonio' : cluster === 'taste_profile' ? 'tag-taste' : 'tag-comp';
        return `
        <div class="card">
          <div class="card-label"><span class="cluster-tag ${tagClass}">${clusterNames[cluster] ?? cluster}</span></div>
          <div class="card-value" style="color: ${color}; font-size: 28px;">${stats.mentions}</div>
          <div class="card-sub">mentions / ${maxMentions} possible</div>
          <div class="bar-track"><div class="bar-fill ${Number(pct) === 0 ? 'red' : Number(pct) < 15 ? 'accent' : 'green'}" style="width: ${pct}%"></div></div>
        </div>`;
      }).join('')}
    </div>

    <div class="finding">
      <div class="f-label">Cluster Insight</div>
      <p><strong>Taste Profile</strong> is the only cluster with meaningful visibility (Q14: 4-platform mention). The origin-based clusters (Houston, Austin, San Antonio) are essentially dead zones — only 5 scattered mentions across 48 total query-platform opportunities. <strong>Competitive Substitution</strong> (queries explicitly seeking Austin alternatives) has just 2 Perplexity mentions — Lucine is not being surfaced as the "skip Austin" answer.</p>
    </div>
  </div>
</section>

<!-- ══ PLATFORM ANALYSIS ══ -->
<section>
  <div class="container">
    <h2>Platform Analysis</h2>
    <p class="section-intro">Where Hotel Lucine was mentioned across the 4 AI platforms (aggregated across ${analysis.metadata.collection_count} runs).</p>

    <div class="card-grid cols-4">
      ${platforms.map(p => {
        const ps = scores.platform_scores[p];
        const color = ps.score === 0 ? 'var(--red)' : ps.score < 10 ? 'var(--accent)' : 'var(--green)';
        return `
        <div class="card">
          <div class="card-label">${platformLabels[p]}</div>
          <div class="card-value" style="color: ${color}; font-size: 28px;">${ps.score}</div>
          <div class="card-sub">${ps.mentions} mention${ps.mentions !== 1 ? 's' : ''} / ${ps.total} queries</div>
          <div class="bar-track"><div class="bar-fill ${ps.score === 0 ? 'red' : ps.score < 10 ? 'accent' : 'green'}" style="width: ${ps.score}%"></div></div>
        </div>`;
      }).join('')}
    </div>

    <div class="finding">
      <div class="f-label">Platform Finding</div>
      <p><strong>Gemini and Perplexity</strong> are the only platforms showing Lucine at all, and barely. <strong>ChatGPT mentioned Lucine on only 1 query</strong> across all runs. <strong>Claude mentioned on 2 queries</strong>. There are no sentiment data points because mentions were too few to analyze sentiment meaningfully. This is a training-data gap, not a content-quality gap — the platforms don't associate Lucine with "Galveston beach weekend" or "Texas drive destination" queries.</p>
    </div>
  </div>
</section>

<!-- ══ FULL QUERY TABLE ══ -->
<section>
  <div class="container">
    <h2>Query-Level Detail</h2>
    <p class="section-intro">All 20 queries with per-platform mention status. 12 queries had zero mentions across all platforms and all ${analysis.metadata.collection_count} runs.</p>

    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Query</th>
          <th>Cluster</th>
          <th>ChatGPT</th>
          <th>Claude</th>
          <th>Perplexity</th>
          <th>Gemini</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        ${[...mentionedQueries, ...zeroMentionQueries].sort((a, b) => parseInt(a.id.slice(1)) - parseInt(b.id.slice(1))).map(q => {
          const tagClass = q.cluster === 'houston_origin' ? 'tag-houston' : q.cluster === 'austin_origin' ? 'tag-austin' : q.cluster === 'san_antonio_origin' ? 'tag-sanantonio' : q.cluster === 'taste_profile' ? 'tag-taste' : 'tag-comp';
          const rowStyle = q.total === 0 ? 'opacity: 0.5;' : '';
          return `
          <tr style="${rowStyle}">
            <td style="font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--text-muted);">${q.id}</td>
            <td>${q.text}</td>
            <td><span class="cluster-tag ${tagClass}">${clusterNames[q.cluster] ?? q.cluster}</span></td>
            <td>${q.chatgpt ? '<span class="check">●</span>' : '<span class="cross">—</span>'}</td>
            <td>${q.claude ? '<span class="check">●</span>' : '<span class="cross">—</span>'}</td>
            <td>${q.perplexity ? '<span class="check">●</span>' : '<span class="cross">—</span>'}</td>
            <td>${q.gemini ? '<span class="check">●</span>' : '<span class="cross">—</span>'}</td>
            <td style="font-weight: 600; ${q.total > 0 ? 'color: var(--accent);' : 'color: var(--text-muted);'}">${q.total}</td>
          </tr>`;
        }).join('')}
      </tbody>
    </table>
  </div>
</section>

<!-- ══ COMPETITOR BRIEF ══ -->
<section>
  <div class="container">
    <h2>Who Shows Up Instead</h2>
    <p class="section-intro">Competitor mentions detected in Lucine's zero-mention queries. When Lucine isn't surfacing, these hotels are.</p>

    <div class="card-grid cols-5">
      ${Object.entries(scores.competitor_analysis).map(([name, data]: [string, any]) => {
        const color = data.total_mentions === 0 ? 'var(--text-muted)' : data.total_mentions >= 2 ? 'var(--red)' : 'var(--accent)';
        return `
        <div class="card">
          <div class="card-label">${name}</div>
          <div class="card-value" style="color: ${color}; font-size: 28px;">${data.total_mentions}</div>
          <div class="card-sub">total mentions · ${data.in_zero_mentions} in zero-mention queries</div>
        </div>`;
      }).join('')}
    </div>

    <div class="finding">
      <div class="f-label">Competitive Note</div>
      <p><strong>The Tremont House</strong> and <strong>Grand Galvez</strong> lead with 3 and 2 mentions respectively — both are historic Galveston hotels with stronger AI training-data presence. <strong>The Mansard House</strong> (a direct boutique competitor) has zero mentions, suggesting this is a Galveston-wide AI visibility problem, not just a Lucine problem.</p>
    </div>
  </div>
</section>

<!-- ══ METHODOLOGY ══ -->
<section>
  <div class="container">
    <h2>Methodology</h2>
    <p class="section-intro">How this featurette was collected and scored.</p>

    <div class="card-grid cols-2">
      <div class="card">
        <div class="card-label">Collection</div>
        <p style="font-size: 14px; margin-top: 8px; color: var(--text-muted);">${analysis.metadata.collection_count} independent runs across 4 AI platforms (ChatGPT, Claude, Perplexity, Gemini). Each run: 20 queries × 4 platforms = 80 API calls. Total: ${analysis.metadata.collection_count * 80} API calls aggregated into a single GEO score.</p>
      </div>
      <div class="card">
        <div class="card-label">Scoring</div>
        <p style="font-size: 14px; margin-top: 8px; color: var(--text-muted);">Mentions are counted per-platform per-query. The GEO Score is a weighted average across decision moments (discovery: 20%), with actual mention count mapped to a 0-100 scale per moment. Multi-run aggregation uses mean scores with trend direction analysis.</p>
      </div>
    </div>

    <div class="finding">
      <div class="f-label">Purpose</div>
      <p>${queriesRaw.metadata.purpose}</p>
    </div>
  </div>
</section>

<footer>
  <p>Hotel Lucine · Galveston Destination Featurette · ${analysis.metadata.analysis_date}</p>
  <p style="margin-top: 8px;">Generated by Watermelon Ghost · GEO Machine v1.b</p>
</footer>

</body>
</html>`;

const outPath = resolve(DATA_DIR, 'report.html');
Bun.write(outPath, html);
console.log(`Report written to ${outPath}`);
console.log(`GEO Score: ${geoScore} (${tier})`);
console.log(`Mentioned queries: ${mentionedQueries.length}/${queriesRaw.metadata.total_queries}`);
console.log(`Zero-mention queries: ${zeroMentionQueries.length}/${queriesRaw.metadata.total_queries}`);
