/**
 * Parser de roteiros estruturados (Sprint L slice 1).
 *
 * Descoberta Dra. Fernanda 20/05: roteiros markdown puros são difíceis de
 * aproveitar — não dá pra cortar 60→30s automático, não dá pra validar hook.
 * Estrutura com blocos nomeados Hook/Corpo/Virada/Fechamento/CTA/Card habilita:
 *  - Auto-validação (hook < 5s, CTA < 10s do fim, total <= 60s)
 *  - Variações (slice 2 — cortar 60s → 30s mantendo Hook + Card)
 *  - Export para Premiere/CapCut com chapter markers
 *
 * Estratégia: a IA gera `reel.scenes` (legado). Esse parser CLASSIFICA cada
 * cena num bloco semântico baseado em:
 *  - posição temporal (primeiros 5s = Hook; últimos 5s = Card)
 *  - heurística de conteúdo (CTA keywords, hook keywords)
 *  - on_screen_text padrões (logo, CRM = Card final)
 *
 * Não modifica a fonte — gera uma "view" estruturada sobreposta.
 */

export type BlocoRoteiro = 'hook' | 'corpo' | 'virada' | 'fechamento' | 'cta' | 'card';

export interface SceneInput {
  time?: string;       // "0-5s", "0:05", "5-15s" etc
  action?: string;
  speech?: string;
  on_screen_text?: string;
  b_roll_suggestion?: string;
  bloco?: BlocoRoteiro; // Sprint L slice 3 — IA agora retorna bloco direto
}

export interface SceneClassified extends SceneInput {
  bloco: BlocoRoteiro;
  startSec: number;
  endSec: number;
  ordem: number;
}

export interface RoteiroParsed {
  duracaoTotalSec: number;
  blocos: SceneClassified[];
  hook: SceneClassified[];
  corpo: SceneClassified[];
  virada: SceneClassified[];
  fechamento: SceneClassified[];
  cta: SceneClassified[];
  card: SceneClassified[];
}

export type ValidacaoSeveridade = 'ok' | 'aviso' | 'critico';

export interface ValidacaoFinding {
  campo: string;
  severidade: ValidacaoSeveridade;
  mensagem: string;
  acao?: string;
}

export interface ValidacaoResult {
  ok: boolean;
  findings: ValidacaoFinding[];
  metricas: {
    duracaoTotalSec: number;
    hookSec: number;
    cardSec: number;
    temCta: boolean;
    temCard: boolean;
  };
}

// ─── Parser ────────────────────────────────────────────────────────────────

const HOOK_KEYWORDS = [
  'você sabia', 'imagina', 'já parou', 'descobri', 'atenção', 'pare', 'cuidado',
  'verdade', 'segredo', 'ninguém te conta', 'novidade', '⚠', 'olha isso',
];

const CTA_KEYWORDS = [
  'agende', 'clique', 'link na bio', 'whatsapp', 'comente', 'comenta', 'compartilha',
  'salva', 'salve', 'avalição', 'avaliação', 'consulta', 'cta', 'chama', 'manda dm',
];

const CARD_KEYWORDS = [
  'crm', 'rqe', 'cnae', 'resultados podem variar', 'consulte seu médico', 'cfm',
  'six aesthetics', '@sixaesthetic', 'logo', 'disclaimer',
];

const VIRADA_KEYWORDS = [
  'mas', 'porém', 'no entanto', 'então', 'a verdade é', 'na real',
  'o problema é', 'a solução', 'aqui está',
];

/** Parser de "0-5s", "0:05", "5s", "00:05" etc → segundos (start, end). */
export function parseTime(t: string | undefined): [number, number] {
  if (!t) return [0, 0];
  const clean = t.trim().toLowerCase().replace(/s$/, '').replace(/\s+/g, '');

  // formato "0-5" ou "0-5s"
  const range = clean.match(/^(\d+(?::\d+)?)-(\d+(?::\d+)?)$/);
  if (range) {
    return [toSec(range[1]), toSec(range[2])];
  }
  // formato "0:05" sozinho → ponto único
  const single = clean.match(/^(\d+(?::\d+)?)$/);
  if (single) {
    const v = toSec(single[1]);
    return [v, v];
  }
  return [0, 0];
}

function toSec(s: string): number {
  if (s.includes(':')) {
    const [m, sec] = s.split(':').map((x) => parseInt(x, 10) || 0);
    return m * 60 + sec;
  }
  return parseInt(s, 10) || 0;
}

const BLOCOS_VALIDOS: BlocoRoteiro[] = ['hook', 'corpo', 'virada', 'fechamento', 'cta', 'card'];
function isValidBloco(b: unknown): b is BlocoRoteiro {
  return typeof b === 'string' && BLOCOS_VALIDOS.includes(b as BlocoRoteiro);
}

function classify(scene: SceneInput, startSec: number, _endSec: number, totalSec: number, isLast: boolean): BlocoRoteiro {
  const text = `${scene.speech || ''} ${scene.on_screen_text || ''} ${scene.action || ''}`.toLowerCase();
  const last5 = totalSec > 0 && startSec >= totalSec - 5;
  const first5 = startSec < 5;

  // Card: última cena com elementos de assinatura (CRM, logo, "resultados podem variar")
  if (
    (isLast || last5) &&
    CARD_KEYWORDS.some((kw) => text.includes(kw))
  ) {
    return 'card';
  }

  // CTA: keywords + posição final
  if (CTA_KEYWORDS.some((kw) => text.includes(kw))) {
    return last5 ? 'cta' : 'cta';
  }

  // Hook: primeiros 5s OU keywords de gancho
  if (first5 || HOOK_KEYWORDS.some((kw) => text.includes(kw))) {
    return 'hook';
  }

  // Virada: middle + keywords
  if (VIRADA_KEYWORDS.some((kw) => text.startsWith(kw) || text.includes(` ${kw} `))) {
    return 'virada';
  }

  // Fechamento: últimos 10s sem CTA explícita
  if (totalSec > 0 && startSec >= totalSec - 10) {
    return 'fechamento';
  }

  // Default: corpo
  return 'corpo';
}

export function parseRoteiro(scenes: SceneInput[] | undefined, durationHint?: string): RoteiroParsed | null {
  if (!scenes || scenes.length === 0) return null;

  // Calcular duração total
  let maxEnd = 0;
  const parsed = scenes.map((s, i) => {
    const [start, end] = parseTime(s.time);
    if (end > maxEnd) maxEnd = end;
    return { scene: s, start, end, ordem: i };
  });

  // Fallback duração via hint "30s", "60s"
  if (maxEnd === 0 && durationHint) {
    const m = durationHint.match(/(\d+)/);
    if (m) maxEnd = parseInt(m[1], 10);
  }
  if (maxEnd === 0) maxEnd = scenes.length * 5; // chute conservador

  const classified: SceneClassified[] = parsed.map(({ scene, start, end, ordem }) => ({
    ...scene,
    startSec: start,
    endSec: end || start + 5,
    ordem,
    // Sprint L slice 3: usa scene.bloco se IA forneceu, senão heurística (fallback)
    bloco: isValidBloco(scene.bloco)
      ? (scene.bloco as BlocoRoteiro)
      : classify(scene, start, end || start, maxEnd, ordem === scenes.length - 1),
  }));

  return {
    duracaoTotalSec: maxEnd,
    blocos: classified,
    hook: classified.filter((c) => c.bloco === 'hook'),
    corpo: classified.filter((c) => c.bloco === 'corpo'),
    virada: classified.filter((c) => c.bloco === 'virada'),
    fechamento: classified.filter((c) => c.bloco === 'fechamento'),
    cta: classified.filter((c) => c.bloco === 'cta'),
    card: classified.filter((c) => c.bloco === 'card'),
  };
}

// ─── Validação ─────────────────────────────────────────────────────────────

const REEL_MAX_SEC = 60;
const HOOK_IDEAL_SEC = 5;
const CTA_IDEAL_DELAY_SEC = 10;

export function validateRoteiro(parsed: RoteiroParsed | null): ValidacaoResult {
  const findings: ValidacaoFinding[] = [];
  if (!parsed) {
    return {
      ok: false,
      findings: [{ campo: 'roteiro', severidade: 'critico', mensagem: 'Sem cenas pra analisar.' }],
      metricas: { duracaoTotalSec: 0, hookSec: 0, cardSec: 0, temCta: false, temCard: false },
    };
  }

  const hookSec = parsed.hook.length > 0 ? Math.max(...parsed.hook.map((h) => h.endSec)) - Math.min(...parsed.hook.map((h) => h.startSec)) : 0;
  const cardSec = parsed.card.length > 0 ? parsed.card.reduce((s, c) => s + (c.endSec - c.startSec), 0) : 0;
  const temCta = parsed.cta.length > 0;
  const temCard = parsed.card.length > 0;

  // Duração total
  if (parsed.duracaoTotalSec > REEL_MAX_SEC) {
    findings.push({
      campo: 'duracaoTotal',
      severidade: 'aviso',
      mensagem: `Duração ${parsed.duracaoTotalSec}s ultrapassa ${REEL_MAX_SEC}s ideais pra Reel.`,
      acao: 'Considerar cortar pra 60s ou gerar variação curta.',
    });
  }

  // Hook ideal < 5s
  if (parsed.hook.length === 0) {
    findings.push({
      campo: 'hook',
      severidade: 'critico',
      mensagem: 'Nenhuma cena classificada como Hook. Hook é crítico nos primeiros 3-5s.',
      acao: 'Adicionar cena com gancho explícito nos primeiros 5s.',
    });
  } else if (hookSec > HOOK_IDEAL_SEC) {
    findings.push({
      campo: 'hookDuracao',
      severidade: 'aviso',
      mensagem: `Hook tem ${hookSec}s — ideal ≤ ${HOOK_IDEAL_SEC}s pra reter scroll.`,
      acao: 'Comprimir o hook pros primeiros 3-5s.',
    });
  }

  // CTA presente + posicionado no fim
  if (!temCta) {
    findings.push({
      campo: 'cta',
      severidade: 'critico',
      mensagem: 'Sem CTA explícita. Reel sem CTA tem ROI muito menor.',
      acao: 'Adicionar cena com "agende", "link na bio", "manda DM" ou similar.',
    });
  } else {
    const ctaStart = Math.min(...parsed.cta.map((c) => c.startSec));
    if (parsed.duracaoTotalSec > 0 && parsed.duracaoTotalSec - ctaStart > CTA_IDEAL_DELAY_SEC) {
      findings.push({
        campo: 'ctaPosicao',
        severidade: 'aviso',
        mensagem: `CTA começa em ${ctaStart}s mas vídeo termina em ${parsed.duracaoTotalSec}s — ideal CTA nos últimos ${CTA_IDEAL_DELAY_SEC}s.`,
      });
    }
  }

  // Card final (CRM, logo, disclaimer)
  if (!temCard) {
    findings.push({
      campo: 'card',
      severidade: 'aviso',
      mensagem: 'Sem card final identificado (logo + CRM + disclaimer).',
      acao: 'Card final reforça autoridade + compliance CFM.',
    });
  }

  return {
    ok: findings.filter((f) => f.severidade === 'critico').length === 0,
    findings,
    metricas: {
      duracaoTotalSec: parsed.duracaoTotalSec,
      hookSec,
      cardSec,
      temCta,
      temCard,
    },
  };
}

// ─── Variações (Sprint L slice 2) ──────────────────────────────────────────

/**
 * Score que indica relevância de uma cena pra manter quando cortar.
 * Maior = mais essencial.
 */
function scoreCena(s: SceneClassified): number {
  const text = `${s.speech || ''} ${s.on_screen_text || ''}`.toLowerCase();
  let score = 0;
  // Densidade de info: cena com fala + texto vale mais
  if (s.speech) score += 2;
  if (s.on_screen_text) score += 1;
  // Cena com b-roll é descritiva — vale menos cortar
  if (s.b_roll_suggestion && !s.speech) score -= 1;
  // Palavras-chave de venda/educação
  const hot = ['ciência', 'estudo', 'resultado', 'antes', 'depois', 'segredo', 'novidade', 'única', 'exclusivo', 'protocolo'];
  hot.forEach((kw) => {
    if (text.includes(kw)) score += 1;
  });
  return score;
}

export interface VariacaoResult {
  scenes: SceneClassified[];
  duracaoEstimadaSec: number;
  cenaOriginais: number;
  cenasMantidas: number;
  cenasRemovidas: number;
  preservados: Record<BlocoRoteiro, number>;
  warnings: string[];
}

/**
 * Gera versão curta com alvo `targetSec`. Preserva Hook + CTA + Card (essenciais),
 * adiciona melhores cenas de Corpo+Virada+Fechamento por score até atingir target.
 *
 * Estratégia:
 *  1. Hook 1 (sempre mantém o primeiro)
 *  2. Card 1 (sempre mantém o último - assinatura)
 *  3. CTA 1 (essencial pra conversão)
 *  4. Preenche o restante com Corpo/Virada por score até estourar target
 */
export function gerarVariacaoCurta(parsed: RoteiroParsed | null, targetSec: number): VariacaoResult {
  if (!parsed || parsed.blocos.length === 0) {
    return {
      scenes: [],
      duracaoEstimadaSec: 0,
      cenaOriginais: 0,
      cenasMantidas: 0,
      cenasRemovidas: 0,
      preservados: { hook: 0, corpo: 0, virada: 0, fechamento: 0, cta: 0, card: 0 },
      warnings: ['Sem roteiro pra processar.'],
    };
  }

  const warnings: string[] = [];
  const mantidas = new Set<number>(); // ordens
  let duracaoEstimada = 0;

  const addCena = (s: SceneClassified | undefined): boolean => {
    if (!s) return false;
    if (mantidas.has(s.ordem)) return true;
    const cenaDuracao = Math.max(1, s.endSec - s.startSec);
    if (duracaoEstimada + cenaDuracao > targetSec && mantidas.size > 0) return false;
    mantidas.add(s.ordem);
    duracaoEstimada += cenaDuracao;
    return true;
  };

  // 1. Hook obrigatório (primeira cena de hook)
  if (parsed.hook.length > 0) {
    addCena(parsed.hook[0]);
  } else {
    warnings.push('Roteiro sem Hook — variação corta começa fraca.');
    if (parsed.blocos[0]) addCena(parsed.blocos[0]);
  }

  // 2. Card final obrigatório (assinatura/CRM)
  if (parsed.card.length > 0) {
    addCena(parsed.card[0]);
  }

  // 3. CTA obrigatória
  if (parsed.cta.length > 0) {
    addCena(parsed.cta[0]);
  } else {
    warnings.push('Roteiro sem CTA — variação curta perde conversão.');
  }

  // 4. Preenche restante com Corpo + Virada + Fechamento por score
  const candidatos = [...parsed.corpo, ...parsed.virada, ...parsed.fechamento]
    .filter((c) => !mantidas.has(c.ordem))
    .sort((a, b) => scoreCena(b) - scoreCena(a));

  for (const c of candidatos) {
    if (!addCena(c)) break;
  }

  // Ordenar mantidas pela ordem original
  const scenes = parsed.blocos
    .filter((b) => mantidas.has(b.ordem))
    .sort((a, b) => a.ordem - b.ordem);

  const preservados: Record<BlocoRoteiro, number> = { hook: 0, corpo: 0, virada: 0, fechamento: 0, cta: 0, card: 0 };
  scenes.forEach((s) => {
    preservados[s.bloco]++;
  });

  if (duracaoEstimada > targetSec) {
    warnings.push(`Duração estimada ${duracaoEstimada}s excede alvo ${targetSec}s — considere reduzir o Corpo manualmente.`);
  }

  return {
    scenes,
    duracaoEstimadaSec: duracaoEstimada,
    cenaOriginais: parsed.blocos.length,
    cenasMantidas: scenes.length,
    cenasRemovidas: parsed.blocos.length - scenes.length,
    preservados,
    warnings,
  };
}

// ─── UI helpers ────────────────────────────────────────────────────────────

export const BLOCO_LABELS: Record<BlocoRoteiro, { label: string; icon: string; bg: string; fg: string; descricao: string }> = {
  hook: {
    label: 'Hook',
    icon: '⚡',
    bg: '#fef3c7',
    fg: '#78350f',
    descricao: 'Primeiros 3-5s. Para o scroll.',
  },
  corpo: {
    label: 'Corpo',
    icon: '◧',
    bg: '#dbeafe',
    fg: '#1e40af',
    descricao: 'Desenvolve o assunto. Dá densidade.',
  },
  virada: {
    label: 'Virada',
    icon: '↻',
    bg: '#ede9fe',
    fg: '#5b21b6',
    descricao: 'Quebra de expectativa ou solução.',
  },
  fechamento: {
    label: 'Fechamento',
    icon: '◯',
    bg: '#e0f2fe',
    fg: '#075985',
    descricao: 'Amarra a mensagem antes do CTA.',
  },
  cta: {
    label: 'CTA',
    icon: '→',
    bg: '#d1fae5',
    fg: '#065f46',
    descricao: 'Chamada de ação clara nos últimos 10s.',
  },
  card: {
    label: 'Card final',
    icon: '◉',
    bg: '#ffedd5',
    fg: '#9a3412',
    descricao: 'Assinatura: logo + CRM + disclaimer.',
  },
};
