import { escapeHtml, sanitizeUrl } from '@/utils/sanitize';
import { formatIntelBrief } from '@/utils/format-intel-brief';
import { collectBriefSources, renderBriefSourcesFooter, type BriefSource } from '@/utils/brief-sources';
import { t } from '@/services/i18n';
import { getCSSColor, showToast } from '@/utils';
import type { CountryScore } from '@/services/country-instability';
import type { NewsItem } from '@/types';
import type { PredictionMarket } from '@/services/prediction';
import type { AssetType } from '@/types';
import type { CountryBriefSignals } from '@/types';
import type { CountryBriefPanel, CountryIntelData, StockIndexData } from '@/components/CountryBriefPanel';
import { getNearbyInfrastructure, haversineDistanceKm } from '@/services/related-assets';
import { PORTS } from '@/config/ports';
import type { Port } from '@/types';
import { exportCountryBriefJSON, exportCountryBriefCSV, exportCountryEvidenceMarkdown } from '@/utils/export';
import type { CountryBriefExport, CountryEvidenceBundleInput } from '@/utils/export';
import { ME_STRIKE_BOUNDS } from '@/services/country-geometry';
import { toFlagEmoji } from '@/utils/country-flag';
import { setTrustedHtml, trustedHtml } from '@/utils/dom-utils';
import { getAuthState } from '@/services/auth-state';
import { hasPremiumAccess } from '@/services/panel-gating';
import { trackGateHit } from '@/services/analytics';


type BriefAssetType = AssetType | 'port';

export class CountryBriefPage implements CountryBriefPanel {
  private static BRIEF_BOUNDS: Record<string, { n: number; s: number; e: number; w: number }> = {
    ...ME_STRIKE_BOUNDS,
    CN: { n: 53.6, s: 18.2, e: 134.8, w: 73.5 }, TW: { n: 25.3, s: 21.9, e: 122, w: 120 },
    JP: { n: 45.5, s: 24.2, e: 153.9, w: 122.9 }, KR: { n: 38.6, s: 33.1, e: 131.9, w: 124.6 },
    KP: { n: 43.0, s: 37.7, e: 130.7, w: 124.2 }, IN: { n: 35.5, s: 6.7, e: 97.4, w: 68.2 },
    PK: { n: 37, s: 24, e: 77, w: 61 }, AF: { n: 38.5, s: 29.4, e: 74.9, w: 60.5 },
    UA: { n: 52.4, s: 44.4, e: 40.2, w: 22.1 }, RU: { n: 82, s: 41.2, e: 180, w: 19.6 },
    BY: { n: 56.2, s: 51.3, e: 32.8, w: 23.2 }, PL: { n: 54.8, s: 49, e: 24.1, w: 14.1 },
    EG: { n: 31.7, s: 22, e: 36.9, w: 25 }, LY: { n: 33, s: 19.5, e: 25, w: 9.4 },
    SD: { n: 22, s: 8.7, e: 38.6, w: 21.8 }, US: { n: 49, s: 24.5, e: -66.9, w: -125 },
    GB: { n: 58.7, s: 49.9, e: 1.8, w: -8.2 }, DE: { n: 55.1, s: 47.3, e: 15.0, w: 5.9 },
    FR: { n: 51.1, s: 41.3, e: 9.6, w: -5.1 }, TR: { n: 42.1, s: 36, e: 44.8, w: 26 },
  };

  private static INFRA_ICONS: Record<BriefAssetType, string> = {
    pipeline: '\u{1F50C}',
    cable: '\u{1F310}',
    datacenter: '\u{1F5A5}\uFE0F',
    base: '\u{1F3DB}\uFE0F',
    nuclear: '\u2622\uFE0F',
    port: '\u2693',
  };

  private static INFRA_LABELS: Record<BriefAssetType, string> = {
    pipeline: 'pipeline',
    cable: 'cable',
    datacenter: 'datacenter',
    base: 'base',
    nuclear: 'nuclear',
    port: 'port',
  };

  private overlay: HTMLElement;
  private currentCode: string | null = null;
  private currentName: string | null = null;
  private currentHeadlineCount = 0;
  private currentScore: CountryScore | null = null;
  private currentSignals: CountryBriefSignals | null = null;
  private currentBrief: string | null = null;
  private currentBriefGeneratedAt: string | number | null = null;
  private currentBriefCached: boolean | null = null;
  private currentHeadlines: NewsItem[] = [];
  private onCloseCallback?: () => void;
  private onShareStory?: (code: string, name: string) => void;
  private onExportImage?: (code: string, name: string) => void;
  private abortController: AbortController = new AbortController();

  constructor() {
    this.overlay = document.createElement('div');
    this.overlay.className = 'country-brief-overlay';
    document.body.appendChild(this.overlay);

    // Single delegated click handler for all interactive elements.
    // This prevents listener accumulation when show()/showLoading() replace innerHTML.
    this.overlay.addEventListener('click', (e) => {
      const target = e.target as HTMLElement;

      // Click on overlay background to close
      if (target.classList.contains('country-brief-overlay')) {
        this.hide();
        return;
      }

      // Close button
      if (target.closest('.cb-close')) {
        this.hide();
        return;
      }

      // Link share button (copy URL to clipboard)
      const linkShareBtn = target.closest('.cb-link-share-btn') as HTMLButtonElement | null;
      if (linkShareBtn) {
        if (!this.currentCode || !this.currentName) return;
        const url = `${window.location.origin}/?c=${this.currentCode}`;
        navigator.clipboard.writeText(url).then(() => {
          const orig = linkShareBtn.innerHTML;
          setTrustedHtml(linkShareBtn, trustedHtml('<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>', "legacy direct innerHTML migration"));
          setTimeout(() => { setTrustedHtml(linkShareBtn, trustedHtml(orig, "legacy direct innerHTML migration")); }, 1500);
        }).catch(() => {});
        return;
      }

      // Share button
      if (target.closest('.cb-share-btn')) {
        if (this.onShareStory && this.currentCode && this.currentName) {
          this.onShareStory(this.currentCode, this.currentName);
        }
        return;
      }

      // Print button
      if (target.closest('.cb-print-btn')) {
        window.print();
        return;
      }

      // Export button (toggle menu)
      if (target.closest('.cb-export-btn')) {
        e.stopPropagation();
        const exportMenu = this.overlay.querySelector('.cb-export-menu');
        exportMenu?.classList.toggle('hidden');
        return;
      }

      // Export option buttons
      const exportOption = target.closest('.cb-export-option') as HTMLElement | null;
      if (exportOption) {
        const format = exportOption.dataset.format;
        if (format === 'image') {
          if (this.onExportImage && this.currentCode && this.currentName) {
            this.onExportImage(this.currentCode, this.currentName);
          }
        } else if (format === 'pdf') {
          this.exportPdf();
        } else if (format === 'json' || format === 'csv' || format === 'evidence-md') {
          this.exportBrief(format);
        }
        const exportMenu = this.overlay.querySelector('.cb-export-menu');
        exportMenu?.classList.add('hidden');
        return;
      }

      // Citation links
      if (target.classList.contains('cb-citation')) {
        const href = target.getAttribute('href');
        if (href?.startsWith('#')) {
          e.preventDefault();
          const el = this.overlay.querySelector(href);
          el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
          el?.classList.add('cb-news-highlight');
          setTimeout(() => el?.classList.remove('cb-news-highlight'), 2000);
        }
        return;
      }

      // Clicking anywhere else closes the export menu if open
      const exportMenu = this.overlay.querySelector('.cb-export-menu');
      if (exportMenu && !exportMenu.classList.contains('hidden')) {
        exportMenu.classList.add('hidden');
      }
    });

    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape' && this.overlay.classList.contains('active')) this.hide();
    });
  }

  private countryFlag(code: string): string {
    return toFlagEmoji(code, '🌍');
  }

  private levelColor(level: string): string {
    const varMap: Record<string, string> = {
      critical: '--semantic-critical',
      high: '--semantic-high',
      elevated: '--semantic-elevated',
      normal: '--semantic-normal',
      low: '--semantic-low',
    };
    return getCSSColor(varMap[level] || '--text-dim');
  }

  private levelBadge(level: string): string {
    const color = this.levelColor(level);
    const levelKey = level as 'critical' | 'high' | 'elevated' | 'moderate' | 'normal' | 'low';
    const label = t(`countryBrief.levels.${levelKey}`);
    return `<span class="cb-badge" style="background:${color}20;color:${color};border:1px solid ${color}40">${label.toUpperCase()}</span>`;
  }

  private trendIndicator(trend: string): string {
    const arrow = trend === 'rising' ? '↗' : trend === 'falling' ? '↘' : '→';
    const cls = trend === 'rising' ? 'trend-up' : trend === 'falling' ? 'trend-down' : 'trend-stable';
    const trendKey = trend as 'rising' | 'falling' | 'stable';
    const trendLabel = t(`countryBrief.trends.${trendKey}`);
    return `<span class="cb-trend ${cls}">${arrow} ${trendLabel}</span>`;
  }

  private scoreRing(score: number, level: string): string {
    const color = this.levelColor(level);
    const pct = Math.min(100, Math.max(0, score));
    const circumference = 2 * Math.PI * 42;
    const dashOffset = circumference * (1 - pct / 100);
    return `
      <div class="cb-score-ring">
        <svg viewBox="0 0 100 100" width="120" height="120">
          <circle cx="50" cy="50" r="42" fill="none" stroke="rgba(255,255,255,0.06)" stroke-width="6"/>
          <circle cx="50" cy="50" r="42" fill="none" stroke="${color}" stroke-width="6"
            stroke-dasharray="${circumference}" stroke-dashoffset="${dashOffset}"
            stroke-linecap="round" transform="rotate(-90 50 50)"
            style="transition: stroke-dashoffset 0.8s ease"/>
        </svg>
        <div class="cb-score-value" style="color:${color}">${score}</div>
        <div class="cb-score-label">/ 100</div>
      </div>`;
  }

  private componentBars(components: CountryScore['components']): string {
    const items = [
      { label: t('modals.countryBrief.components.unrest'), value: components.unrest, icon: '📢' },
      { label: t('modals.countryBrief.components.conflict'), value: components.conflict, icon: '⚔' },
      { label: t('modals.countryBrief.components.security'), value: components.security, icon: '🛡️' },
      { label: t('modals.countryBrief.components.information'), value: components.information, icon: '📡' },
    ];
    return items.map(({ label, value, icon }) => {
      const pct = Math.min(100, Math.max(0, value));
      const color = pct >= 70 ? getCSSColor('--semantic-critical') : pct >= 50 ? getCSSColor('--semantic-high') : pct >= 30 ? getCSSColor('--semantic-elevated') : getCSSColor('--semantic-normal');
      return `
        <div class="cb-comp-row">
          <span class="cb-comp-icon">${icon}</span>
          <span class="cb-comp-label">${label}</span>
          <div class="cb-comp-bar"><div class="cb-comp-fill" style="width:${pct}%;background:${color}"></div></div>
          <span class="cb-comp-val">${Math.round(value)}</span>
        </div>`;
    }).join('');
  }

  private signalChips(signals: CountryBriefSignals): string {
    const chips: string[] = [];
    if (signals.criticalNews > 0) chips.push(`<span class="signal-chip conflict">🚨 ${signals.criticalNews} Critical News</span>`);
    if (signals.protests > 0) chips.push(`<span class="signal-chip protest">📢 ${signals.protests} ${t('modals.countryBrief.signals.protests')}</span>`);
    if (signals.militaryFlights > 0) {
      const tip = `${signals.militaryFlights} near · ${signals.militaryFlightsInCountry} inside borders`;
      chips.push(`<span class="signal-chip military" title="${tip}">✈️ ${signals.militaryFlights} ${t('modals.countryBrief.signals.militaryAir')}</span>`);
    }
    if (signals.militaryVessels > 0) {
      const tip = `${signals.militaryVessels} near · ${signals.militaryVesselsInCountry} inside borders`;
      chips.push(`<span class="signal-chip military" title="${tip}">⚓ ${signals.militaryVessels} ${t('modals.countryBrief.signals.militarySea')}</span>`);
    }
    if (signals.outages > 0) chips.push(`<span class="signal-chip outage">🌐 ${signals.outages} ${t('modals.countryBrief.signals.outages')}</span>`);
    if (signals.aisDisruptions > 0) chips.push(`<span class="signal-chip outage">🚢 ${signals.aisDisruptions} AIS Disruptions</span>`);
    if (signals.satelliteFires > 0) chips.push(`<span class="signal-chip climate">🔥 ${signals.satelliteFires} Satellite Fires</span>`);
    if (signals.radiationAnomalies > 0) chips.push(`<span class="signal-chip outage">☢️ ${signals.radiationAnomalies} Radiation Anomalies</span>`);
    if (signals.temporalAnomalies > 0) chips.push(`<span class="signal-chip outage">⏱️ ${signals.temporalAnomalies} Temporal Anomalies</span>`);
    if (signals.cyberThreats > 0) chips.push(`<span class="signal-chip conflict">🛡️ ${signals.cyberThreats} Cyber Threats</span>`);
    if (signals.earthquakes > 0) chips.push(`<span class="signal-chip quake">🌍 ${signals.earthquakes} ${t('modals.countryBrief.signals.earthquakes')}</span>`);
    if (signals.displacementOutflow > 0) {
      const fmt = signals.displacementOutflow >= 1_000_000
        ? `${(signals.displacementOutflow / 1_000_000).toFixed(1)}M`
        : `${(signals.displacementOutflow / 1000).toFixed(0)}K`;
      chips.push(`<span class="signal-chip displacement">🌊 ${fmt} ${t('modals.countryBrief.signals.displaced')}</span>`);
    }
    if (signals.climateStress > 0) chips.push(`<span class="signal-chip climate">🌡️ ${t('modals.countryBrief.signals.climate')}</span>`);
    if (signals.conflictEvents > 0) chips.push(`<span class="signal-chip conflict">⚔️ ${signals.conflictEvents} ${t('modals.countryBrief.signals.conflictEvents')}</span>`);
    if (signals.activeStrikes > 0) chips.push(`<span class="signal-chip conflict">\u{1F4A5} ${signals.activeStrikes} ${t('modals.countryBrief.signals.activeStrikes')}</span>`);
    if (signals.travelAdvisories > 0 && signals.travelAdvisoryMaxLevel) {
      const advisoryClass = signals.travelAdvisoryMaxLevel === 'do-not-travel' ? 'conflict'
        : signals.travelAdvisoryMaxLevel === 'reconsider' ? 'outage'
        : 'military';
      const advisoryLabel = signals.travelAdvisoryMaxLevel === 'do-not-travel' ? 'Do Not Travel'
        : signals.travelAdvisoryMaxLevel === 'reconsider' ? 'Reconsider Travel'
        : 'Exercise Caution';
      chips.push(`<span class="signal-chip ${advisoryClass}">\u26A0\uFE0F ${signals.travelAdvisories} Advisory: ${advisoryLabel}</span>`);
    }
    if (signals.orefSirens > 0) chips.push(`<span class="signal-chip conflict">\u{1F6A8} ${signals.orefSirens} Active Sirens</span>`);
    if (signals.orefHistory24h > 0) chips.push(`<span class="signal-chip conflict">\u{1F553} ${signals.orefHistory24h} Sirens / 24h</span>`);
    if (signals.aviationDisruptions > 0) chips.push(`<span class="signal-chip outage">\u{1F6AB} ${signals.aviationDisruptions} ${t('modals.countryBrief.signals.aviationDisruptions')}</span>`);
    if (signals.gpsJammingHexes > 0) chips.push(`<span class="signal-chip outage">\u{1F4E1} ${signals.gpsJammingHexes} ${t('modals.countryBrief.signals.gpsJammingZones')}</span>`);
    chips.push(`<span class="signal-chip stock-loading">📈 ${t('modals.countryBrief.loadingIndex')}</span>`);
    return chips.join('');
  }

  public setShareStoryHandler(handler: (code: string, name: string) => void): void {
    this.onShareStory = handler;
  }

  public setExportImageHandler(handler: (code: string, name: string) => void): void {
    this.onExportImage = handler;
  }

  public showLoading(): void {
    this.currentCode = '__loading__';
    setTrustedHtml(this.overlay, trustedHtml(`
      <div class="country-brief-page">
        <div class="cb-header">
          <div class="cb-header-left">
            <span class="cb-flag">🌍</span>
            <span class="cb-country-name">${t('modals.countryBrief.identifying')}</span>
          </div>
          <div class="cb-header-right">
            <button class="cb-close" aria-label="${t('components.newsPanel.close')}">×</button>
          </div>
        </div>
        <div class="cb-body">
          <div class="cb-loading-state">
            <div class="intel-skeleton"></div>
            <div class="intel-skeleton short"></div>
            <span class="intel-loading-text">${t('modals.countryBrief.locating')}</span>
          </div>
        </div>
      </div>`, "legacy direct innerHTML migration"));
    // Close button click is handled via event delegation on the overlay (set up in constructor)
    this.overlay.classList.add('active');
  }

  public showGeoError(onRetry: () => void): void {
    this.currentCode = '__error__';
    this.overlay.textContent = '';

    const page = document.createElement('div');
    page.className = 'country-brief-page';

    const header = document.createElement('div');
    header.className = 'cb-header';
    const headerLeft = document.createElement('div');
    headerLeft.className = 'cb-header-left';
    const flag = document.createElement('span');
    flag.className = 'cb-flag';
    flag.textContent = '\u26A0\uFE0F';
    const title = document.createElement('span');
    title.className = 'cb-country-name';
    title.textContent = t('countryBrief.geocodeFailed');
    headerLeft.append(flag, title);
    const headerRight = document.createElement('div');
    headerRight.className = 'cb-header-right';
    const closeX = document.createElement('button');
    closeX.className = 'cb-close';
    closeX.setAttribute('aria-label', t('components.newsPanel.close'));
    closeX.textContent = '\u00D7';
    headerRight.append(closeX);
    header.append(headerLeft, headerRight);

    const body = document.createElement('div');
    body.className = 'cb-body';
    const errorWrap = document.createElement('div');
    errorWrap.className = 'cb-geo-error';
    const actions = document.createElement('div');
    actions.className = 'cb-geo-error-actions';
    const retryBtn = document.createElement('button');
    retryBtn.className = 'cb-geo-retry-btn';
    retryBtn.textContent = t('countryBrief.retryBtn');
    retryBtn.addEventListener('click', () => onRetry(), { once: true });
    const closeBtn = document.createElement('button');
    closeBtn.className = 'cb-geo-close-btn';
    closeBtn.textContent = t('countryBrief.closeBtn');
    closeBtn.addEventListener('click', () => this.hide(), { once: true });
    actions.append(retryBtn, closeBtn);
    errorWrap.append(actions);
    body.append(errorWrap);

    page.append(header, body);
    this.overlay.append(page);
    this.overlay.classList.add('active');
  }

  public get signal(): AbortSignal {
    return this.abortController.signal;
  }

  public show(country: string, code: string, score: CountryScore | null, signals: CountryBriefSignals): void {
    this.abortController.abort();
    this.abortController = new AbortController();
    this.currentCode = code;
    this.currentName = country;
    this.currentScore = score;
    this.currentSignals = signals;
    this.currentBrief = null;
    this.currentBriefGeneratedAt = null;
    this.currentBriefCached = null;
    this.currentHeadlines = [];
    this.currentHeadlineCount = 0;
    const flag = this.countryFlag(code);

    const tierBadge = !signals.isTier1
      ? `<span class="cb-tier-badge">${t('modals.countryBrief.limitedCoverage')}</span>`
      : '';

    setTrustedHtml(this.overlay, trustedHtml(`
      <div class="country-brief-page">
        <div class="cb-header">
          <div class="cb-header-left">
            <span class="cb-flag">${flag}</span>
            <span class="cb-country-name">${escapeHtml(country)}</span>
            ${score ? this.levelBadge(score.level) : ''}
            ${score ? this.trendIndicator(score.trend) : ''}
            ${tierBadge}
          </div>
          <div class="cb-header-right">
            <button class="cb-link-share-btn" title="${t('components.countryBrief.shareLink')}">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg>
            </button>
            <button class="cb-share-btn" title="${t('components.countryBrief.shareStory')}">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v7a2 2 0 002 2h12a2 2 0 002-2v-7"/><polyline points="16 6 12 2 8 6"/><line x1="12" y1="2" x2="12" y2="15"/></svg>
            </button>
            <button class="cb-print-btn" title="${t('components.countryBrief.printPdf')}">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9V2h12v7"/><path d="M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
            </button>
            <div style="position:relative;display:inline-block">
              <button class="cb-export-btn" title="${t('components.countryBrief.exportData')}">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
              </button>
              <div class="cb-export-menu hidden">
                <button class="cb-export-option" data-format="image">${t('common.exportImage')}</button>
                <button class="cb-export-option" data-format="pdf">${t('common.exportPdf')}</button>
                <button class="cb-export-option" data-format="json">${t('common.exportJson')}</button>
                <button class="cb-export-option" data-format="csv">${t('common.exportCsv')}</button>
                <button class="cb-export-option" data-format="evidence-md">Evidence Markdown</button>
              </div>
            </div>
            <button class="cb-close" aria-label="${t('components.newsPanel.close')}">×</button>
          </div>
        </div>
        <div class="cb-body">
          <div class="cb-grid">
            <div class="cb-col-left">
              ${score ? `
                <section class="cb-section cb-risk-section">
                  <h3 class="cb-section-title">${t('modals.countryBrief.instabilityIndex')}</h3>
                  <div class="cb-risk-content">
                    ${this.scoreRing(score.score, score.level)}
                    <div class="cb-components">
                      ${this.componentBars(score.components)}
                    </div>
                  </div>
                </section>` : signals.isTier1 ? '' : `
                <section class="cb-section cb-risk-section">
                  <h3 class="cb-section-title">${t('modals.countryBrief.instabilityIndex')}</h3>
                  <div class="cb-not-tracked">
                    <span class="cb-not-tracked-icon">📊</span>
                    <span>${t('modals.countryBrief.notTracked', { country: escapeHtml(country) })}</span>
                  </div>
                </section>`}

              <section class="cb-section cb-brief-section">
                <h3 class="cb-section-title">${t('modals.countryBrief.intelBrief')}</h3>
                <div class="cb-brief-content">
                  <div class="intel-brief-loading">
                    <div class="intel-skeleton"></div>
                    <div class="intel-skeleton short"></div>
                    <div class="intel-skeleton"></div>
                    <div class="intel-skeleton short"></div>
                    <span class="intel-loading-text">${t('modals.countryBrief.generatingBrief')}</span>
                  </div>
                </div>
              </section>

              <section class="cb-section cb-news-section" style="display:none">
                <h3 class="cb-section-title">${t('modals.countryBrief.topNews')}</h3>
                <div class="cb-news-content"></div>
              </section>
            </div>

            <div class="cb-col-right">
              <section class="cb-section cb-signals-section">
                <h3 class="cb-section-title">${t('modals.countryBrief.activeSignals')}</h3>
                <div class="cb-signals-grid">
                  ${this.signalChips(signals)}
                </div>
              </section>

              <section class="cb-section cb-timeline-section">
                <h3 class="cb-section-title">${t('modals.countryBrief.timeline')}</h3>
                <div class="cb-timeline-mount"></div>
              </section>

              <section class="cb-section cb-markets-section">
                <h3 class="cb-section-title">${t('modals.countryBrief.predictionMarkets')}</h3>
                <div class="cb-markets-content">
                  <span class="intel-loading-text">${t('modals.countryBrief.loadingMarkets')}</span>
                </div>
              </section>

              <section class="cb-section cb-infra-section" style="display:none">
                <h3 class="cb-section-title">${t('modals.countryBrief.infrastructure')}</h3>
                <div class="cb-infra-content"></div>
              </section>

            </div>
          </div>
        </div>
      </div>`, "legacy direct innerHTML migration"));

    // All button click handlers (close, share, print, export, citation, link-share) are handled
    // via event delegation on the overlay (set up in constructor)

    this.overlay.classList.add('active');
  }

  public updateBrief(data: CountryIntelData): void {
    if (data.code !== this.currentCode) return;
    const section = this.overlay.querySelector('.cb-brief-content');
    if (!section) return;

    if (data.error || data.skipped || !data.brief) {
      const msg = data.error || data.reason || t('modals.countryBrief.briefUnavailable');
      setTrustedHtml(section, trustedHtml(`<div class="intel-error">${escapeHtml(msg)}</div>`, "legacy direct innerHTML migration"));
      return;
    }

    this.currentBrief = data.brief;
    this.currentBriefGeneratedAt = data.generatedAt ?? null;
    this.currentBriefCached = data.cached === true;
    const briefSources = collectBriefSources(data.sources ?? [], 6);
    const formatted = this.formatBrief(data.brief, briefSources, this.currentHeadlineCount);
    const sourcesFooter = renderBriefSourcesFooter(briefSources, { className: 'cb-brief-sources' });
    setTrustedHtml(section, trustedHtml(`
      <div class="cb-brief-text">${formatted}</div>
      ${sourcesFooter}
      <div class="cb-brief-footer">
        ${data.cached ? `<span class="intel-cached">📋 ${t('modals.countryBrief.cached')}</span>` : `<span class="intel-fresh">✨ ${t('modals.countryBrief.fresh')}</span>`}
        <span class="intel-timestamp">${data.generatedAt ? new Date(data.generatedAt).toLocaleTimeString() : ''}</span>
      </div>`, "legacy direct innerHTML migration"));
  }

  public updateMarkets(markets: PredictionMarket[]): void {
    const section = this.overlay.querySelector('.cb-markets-content');
    if (!section) return;

    if (markets.length === 0) {
      setTrustedHtml(section, trustedHtml(`<span class="cb-empty">${t('modals.countryBrief.noMarkets')}</span>`, "legacy direct innerHTML migration"));
      return;
    }

    setTrustedHtml(section, trustedHtml(markets.slice(0, 3).map(m => {
      const pct = Math.round(m.yesPrice);
      const noPct = 100 - pct;
      const vol = m.volume ? `$${(m.volume / 1000).toFixed(0)}k vol` : '';
      const safeUrl = sanitizeUrl(m.url || '');
      const link = safeUrl ? ` <a href="${safeUrl}" target="_blank" rel="noopener" class="cb-market-link">↗</a>` : '';
      return `
        <div class="cb-market-item">
          <div class="cb-market-title">${escapeHtml(m.title.slice(0, 100))}${link}</div>
          <div class="market-bar">
            <div class="market-yes" style="width:${pct}%">${pct}%</div>
            <div class="market-no" style="width:${noPct}%">${noPct > 15 ? noPct + '%' : ''}</div>
          </div>
          ${vol ? `<div class="market-vol">${vol}</div>` : ''}
        </div>`;
    }).join(''), "legacy direct innerHTML migration"));
  }

  public updateStock(data: StockIndexData): void {
    const el = this.overlay.querySelector('.stock-loading');
    if (!el) return;

    if (!data.available) {
      el.remove();
      return;
    }

    const pct = parseFloat(data.weekChangePercent);
    const sign = pct >= 0 ? '+' : '';
    const cls = pct >= 0 ? 'stock-up' : 'stock-down';
    const arrow = pct >= 0 ? '📈' : '📉';
    el.className = `signal-chip stock ${cls}`;
    setTrustedHtml(el, trustedHtml(`${arrow} ${escapeHtml(data.indexName)}: ${sign}${data.weekChangePercent}% (1W)`, "legacy direct innerHTML migration"));
  }

  public updateNews(headlines: NewsItem[]): void {
    const section = this.overlay.querySelector('.cb-news-section') as HTMLElement | null;
    const content = this.overlay.querySelector('.cb-news-content');
    if (!section || !content || headlines.length === 0) return;

    const items = headlines.slice(0, 8);
    this.currentHeadlineCount = items.length;
    this.currentHeadlines = items;
    section.style.display = '';

    setTrustedHtml(content, trustedHtml(items.map((item, i) => {
      const safeUrl = sanitizeUrl(item.link);
      const threatColor = item.threat?.level === 'critical' ? getCSSColor('--threat-critical')
        : item.threat?.level === 'high' ? getCSSColor('--threat-high')
        : item.threat?.level === 'medium' ? getCSSColor('--threat-medium')
        : getCSSColor('--threat-info');
      const timeAgo = this.timeAgo(item.pubDate);
      const cardBody = `
        <span class="cb-news-threat" style="background:${threatColor}"></span>
        <div class="cb-news-body">
          <div class="cb-news-title">${escapeHtml(item.title)}</div>
          <div class="cb-news-meta">${escapeHtml(item.source)} · ${timeAgo}</div>
        </div>`;
      if (safeUrl) {
        return `<a href="${safeUrl}" target="_blank" rel="noopener" class="cb-news-card" id="cb-news-${i + 1}">${cardBody}</a>`;
      }
      return `<div class="cb-news-card" id="cb-news-${i + 1}">${cardBody}</div>`;
    }).join(''), "legacy direct innerHTML migration"));
  }


  public updateInfrastructure(countryCode: string): void {
    const bounds = CountryBriefPage.BRIEF_BOUNDS[countryCode];
    if (!bounds) return;

    const centroidLat = (bounds.n + bounds.s) / 2;
    const centroidLon = (bounds.e + bounds.w) / 2;

    const assets = getNearbyInfrastructure(centroidLat, centroidLon, ['pipeline', 'cable', 'datacenter', 'base', 'nuclear']);

    const nearbyPorts = PORTS
      .map((p: Port) => ({ port: p, dist: haversineDistanceKm(centroidLat, centroidLon, p.lat, p.lon) }))
      .filter(({ dist }) => dist <= 600)
      .sort((a, b) => a.dist - b.dist)
      .slice(0, 5);

    const grouped = new Map<BriefAssetType, Array<{ name: string; distanceKm: number }>>();
    for (const a of assets) {
      const list = grouped.get(a.type) || [];
      list.push({ name: a.name, distanceKm: a.distanceKm });
      grouped.set(a.type, list);
    }
    if (nearbyPorts.length > 0) {
      grouped.set('port', nearbyPorts.map(({ port, dist }) => ({ name: port.name, distanceKm: dist })));
    }

    if (grouped.size === 0) return;

    const section = this.overlay.querySelector('.cb-infra-section') as HTMLElement | null;
    const content = this.overlay.querySelector('.cb-infra-content');
    if (!section || !content) return;

    const order: BriefAssetType[] = ['pipeline', 'cable', 'datacenter', 'base', 'nuclear', 'port'];
    let html = '';
    for (const type of order) {
      const items = grouped.get(type);
      if (!items || items.length === 0) continue;
      const icon = CountryBriefPage.INFRA_ICONS[type];
      const key = CountryBriefPage.INFRA_LABELS[type];
      const label = t(`modals.countryBrief.infra.${key}`);
      html += `<div class="cb-infra-group">`;
      html += `<div class="cb-infra-type">${icon} ${label}</div>`;
      for (const item of items) {
        html += `<div class="cb-infra-item"><span>${escapeHtml(item.name)}</span><span class="cb-infra-dist">${Math.round(item.distanceKm)} km</span></div>`;
      }
      html += `</div>`;
    }

    setTrustedHtml(content, trustedHtml(html, "legacy direct innerHTML migration"));
    section.style.display = '';
  }

  public getTimelineMount(): HTMLElement | null {
    return this.overlay.querySelector('.cb-timeline-mount');
  }

  public getCode(): string | null {
    return this.currentCode;
  }

  public getName(): string | null {
    return this.currentName;
  }

  private timeAgo(date: Date): string {
    const ms = Date.now() - new Date(date).getTime();
    const hours = Math.floor(ms / 3600000);
    if (hours < 1) return t('modals.countryBrief.timeAgo.m', { count: Math.floor(ms / 60000) });
    if (hours < 24) return t('modals.countryBrief.timeAgo.h', { count: hours });
    return t('modals.countryBrief.timeAgo.d', { count: Math.floor(hours / 24) });
  }

  private formatBrief(text: string, sources: BriefSource[] = [], headlineCount = 0): string {
    return formatIntelBrief(
      text,
      sources.length > 0
        ? { sources }
        : headlineCount > 0
          ? { count: headlineCount, hrefPrefix: '#cb-news-' }
          : undefined,
    );
  }

  private exportBrief(format: 'json' | 'csv' | 'evidence-md'): void {
    if (!this.currentCode || !this.currentName) return;
    if (format === 'evidence-md' && !this.canExportEvidenceBundle()) return;
    const exportedAt = new Date().toISOString();
    const data: CountryBriefExport & CountryEvidenceBundleInput = {
      country: this.currentName,
      code: this.currentCode,
      context: 'Country dossier',
      generatedAt: exportedAt,
      exportedAt,
    };
    if (this.currentScore) {
      data.score = this.currentScore.score;
      data.level = this.currentScore.level;
      data.trend = this.currentScore.trend;
      data.components = this.currentScore.components;
    }
    if (this.currentSignals) {
      data.signals = {
        criticalNews: this.currentSignals.criticalNews,
        protests: this.currentSignals.protests,
        militaryFlights: this.currentSignals.militaryFlights,
        militaryVessels: this.currentSignals.militaryVessels,
        outages: this.currentSignals.outages,
        aisDisruptions: this.currentSignals.aisDisruptions,
        satelliteFires: this.currentSignals.satelliteFires,
        radiationAnomalies: this.currentSignals.radiationAnomalies,
        temporalAnomalies: this.currentSignals.temporalAnomalies,
        cyberThreats: this.currentSignals.cyberThreats,
        earthquakes: this.currentSignals.earthquakes,
        displacementOutflow: this.currentSignals.displacementOutflow,
        climateStress: this.currentSignals.climateStress,
        conflictEvents: this.currentSignals.conflictEvents,
        activeStrikes: this.currentSignals.activeStrikes,
        orefSirens: this.currentSignals.orefSirens,
        orefHistory24h: this.currentSignals.orefHistory24h,
        aviationDisruptions: this.currentSignals.aviationDisruptions,
        travelAdvisories: this.currentSignals.travelAdvisories,
        travelAdvisoryMaxLevel: this.currentSignals.travelAdvisoryMaxLevel,
        gpsJammingHexes: this.currentSignals.gpsJammingHexes,
      };
    }
    if (this.currentBrief) data.brief = this.currentBrief;
    if (this.currentBriefGeneratedAt) data.briefGeneratedAt = new Date(this.currentBriefGeneratedAt).toISOString();
    if (this.currentBriefCached != null) data.briefCached = this.currentBriefCached;
    if (this.currentHeadlines.length > 0) {
      data.headlines = this.currentHeadlines.map(h => ({
        title: h.title,
        source: h.source,
        link: h.link,
        pubDate: h.pubDate ? new Date(h.pubDate).toISOString() : undefined,
      }));
    }
    if (format === 'evidence-md') exportCountryEvidenceMarkdown(data);
    else if (format === 'json') exportCountryBriefJSON(data);
    else exportCountryBriefCSV(data);
  }

  private canExportEvidenceBundle(): boolean {
    if (hasPremiumAccess(getAuthState())) return true;
    trackGateHit('evidence-export');
    showToast('Evidence export is available on Pro.');
    return false;
  }

  private exportPdf(): void {
    const content = this.overlay.querySelector('.cb-body');
    const header = this.overlay.querySelector('.cb-header');
    if (!content) return;

    const iframe = document.createElement('iframe');
    iframe.style.cssText = 'position:fixed;left:-9999px;width:0;height:0;border:none';
    document.body.appendChild(iframe);
    const doc = iframe.contentDocument || iframe.contentWindow?.document;
    if (!doc) { document.body.removeChild(iframe); return; }

    const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
      .map(el => el.outerHTML).join('\n');

    doc.open();
    doc.write(`<!DOCTYPE html><html><head><meta charset="utf-8">${styles}
      <style>
        @media print { body { margin: 0; padding: 16px; background: #fff; color: #111; }
          .cb-grid { display: block !important; }
          .cb-grid > * { break-inside: avoid; margin-bottom: 16px; }
          .cb-badge, .cb-trend { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
          canvas { max-width: 100% !important; }
        }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
        .country-brief-overlay { position: static !important; background: none !important; }
      </style>
    </head><body>${header ? header.outerHTML : ''}${content.outerHTML}</body></html>`);
    doc.close();

    if (iframe.contentWindow) {
      iframe.contentWindow.onafterprint = () => document.body.removeChild(iframe);
    }
    setTimeout(() => {
      if (iframe.contentWindow) {
        iframe.contentWindow.print();
      }
      setTimeout(() => { if (iframe.parentNode) document.body.removeChild(iframe); }, 5000);
    }, 300);
  }

  public hide(): void {
    this.abortController.abort();
    this.overlay.classList.remove('active');
    this.currentCode = null;
    this.currentName = null;
    this.onCloseCallback?.();
  }

  public onClose(cb: () => void): void {
    this.onCloseCallback = cb;
  }

  public isVisible(): boolean {
    return this.overlay.classList.contains('active');
  }
}
