/*
 * Formatos dos arquivos dos produtos digitais: o que entra, o que nunca entra
 * e como conferir o formato REAL pelos primeiros bytes (a extensão sozinha
 * mente fácil: "manual.pdf" pode ser um programa).
 */

export type Categoria = "video" | "pdf" | "imagem" | "compactado" | "audio" | "documento" | "outro";

const GRUPOS: Record<Exclude<Categoria, "pdf">, string[]> & { pdf: string[] } = {
  pdf: ["pdf"],
  /* vtt e srt: as legendas das aulas. */
  documento: ["epub", "mobi", "doc", "docx", "xls", "xlsx", "xlsm", "ppt", "pptx", "odt", "ods", "odp", "txt", "rtf", "csv", "vtt", "srt"],
  imagem: ["jpg", "jpeg", "png", "webp", "gif", "svg", "tif", "tiff", "heic", "psd", "psb", "ai", "eps", "cdr", "afdesign", "afphoto", "xcf"],
  audio: ["mp3", "wav", "m4a", "aac", "flac", "ogg"],
  video: ["mp4", "mov", "webm", "m4v"],
  compactado: ["zip"],
  /* Corte, bordado, 3D, fontes, presets: entram, contam como "outros". */
  outro: [
    "dxf", "studio", "studio3", "pes", "dst", "jef", "exp", "vp3", "hus", "xxx", "vip", "stl", "obj", "3mf",
    "ttf", "otf", "woff", "woff2", "dng", "xmp", "cube",
  ],
};

/* Nunca: programas e o que se instala ou roda sozinho. RAR e 7Z ficam fora
   por enquanto (não dá para conferir o que tem dentro): o pedido é usar ZIP. */
export const RECUSADOS = new Set([
  "exe", "msi", "msp", "com", "scr", "pif", "bat", "cmd", "vbs", "vbe", "wsf", "wsh", "hta", "lnk", "ps1", "psm1",
  "reg", "cpl", "jar", "apk", "aab", "ipa", "dmg", "pkg", "app", "deb", "rpm", "appimage", "msix", "appx", "iso",
  "img", "vhd", "rar", "7z",
]);

const CATEGORIA_POR_EXTENSAO = new Map<string, Categoria>(
  (Object.entries(GRUPOS) as Array<[Categoria, string[]]>).flatMap(([categoria, lista]) => lista.map((ext) => [ext, categoria] as const))
);

export const EXTENSOES_ACEITAS: readonly string[] = [...CATEGORIA_POR_EXTENSAO.keys()];

/* As legendas das aulas (o player mostra pelo botão CC). */
export const LEGENDAS_DE_AULA = new Set(["vtt", "srt"]);

/* Os que viram aula no player (o navegador toca sem converter). */
export const VIDEOS_DE_AULA = new Set(["mp4", "webm", "m4v", "mov"]);

export function categoriaDaExtensao(extensao: string): Categoria | null {
  return CATEGORIA_POR_EXTENSAO.get(extensao.toLowerCase()) ?? null;
}

export type ConferenciaDoNome = { ok: true; categoria: Categoria } | { ok: false; motivo: string };

export function conferirExtensao(extensao: string): ConferenciaDoNome {
  const ext = extensao.toLowerCase();
  if (!ext) return { ok: false, motivo: "O arquivo precisa ter extensão (.pdf, .zip, .mp4...)." };
  if (ext === "rar" || ext === "7z") return { ok: false, motivo: "Arquivos .rar e .7z não são aceitos. Compacte em .zip e envie de novo." };
  if (RECUSADOS.has(ext)) return { ok: false, motivo: "Programas e instaladores não são aceitos, por segurança de quem compra." };
  const categoria = categoriaDaExtensao(ext);
  if (!categoria) return { ok: false, motivo: `Arquivos .${ext} não são aceitos. Se precisar, coloque dentro de um .zip.` };
  return { ok: true, categoria };
}

const MIME: Record<string, string> = {
  pdf: "application/pdf",
  epub: "application/epub+zip",
  doc: "application/msword",
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  xls: "application/vnd.ms-excel",
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  ppt: "application/vnd.ms-powerpoint",
  pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  txt: "text/plain; charset=utf-8",
  vtt: "text/vtt; charset=utf-8",
  srt: "application/x-subrip; charset=utf-8",
  csv: "text/csv; charset=utf-8",
  rtf: "application/rtf",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  png: "image/png",
  webp: "image/webp",
  gif: "image/gif",
  svg: "image/svg+xml",
  tif: "image/tiff",
  tiff: "image/tiff",
  heic: "image/heic",
  mp3: "audio/mpeg",
  wav: "audio/wav",
  m4a: "audio/mp4",
  aac: "audio/aac",
  flac: "audio/flac",
  ogg: "audio/ogg",
  mp4: "video/mp4",
  m4v: "video/mp4",
  mov: "video/quicktime",
  webm: "video/webm",
  zip: "application/zip",
  ttf: "font/ttf",
  otf: "font/otf",
  woff: "font/woff",
  woff2: "font/woff2",
};

export function tipoDaExtensao(extensao: string): string {
  return MIME[extensao.toLowerCase()] ?? "application/octet-stream";
}

export type FormatoReal =
  | "pdf" | "zip" | "png" | "jpeg" | "gif" | "webp" | "mp4" | "webm" | "mp3" | "ogg" | "flac" | "wav"
  | "executavel" | "rar" | "7z" | "desconhecido";

function comeca(bytes: Uint8Array, assinatura: number[], deslocamento = 0): boolean {
  if (bytes.length < deslocamento + assinatura.length) return false;
  return assinatura.every((b, i) => bytes[deslocamento + i] === b);
}

/* O formato pelos primeiros bytes. "desconhecido" não é recusa: muitos
   formatos (txt, csv, svg, cdr, fontes) não têm assinatura confiável. */
export function formatoReal(bytes: Uint8Array): FormatoReal {
  if (comeca(bytes, [0x4d, 0x5a])) return "executavel"; // MZ (Windows)
  if (comeca(bytes, [0x7f, 0x45, 0x4c, 0x46])) return "executavel"; // ELF
  if (comeca(bytes, [0xfe, 0xed, 0xfa, 0xce]) || comeca(bytes, [0xfe, 0xed, 0xfa, 0xcf]) || comeca(bytes, [0xce, 0xfa, 0xed, 0xfe]) || comeca(bytes, [0xcf, 0xfa, 0xed, 0xfe])) {
    return "executavel"; // Mach-O
  }
  if (comeca(bytes, [0x25, 0x50, 0x44, 0x46])) return "pdf"; // %PDF
  if (comeca(bytes, [0x50, 0x4b, 0x03, 0x04]) || comeca(bytes, [0x50, 0x4b, 0x05, 0x06])) return "zip";
  if (comeca(bytes, [0x52, 0x61, 0x72, 0x21])) return "rar";
  if (comeca(bytes, [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c])) return "7z";
  if (comeca(bytes, [0x89, 0x50, 0x4e, 0x47])) return "png";
  if (comeca(bytes, [0xff, 0xd8, 0xff])) return "jpeg";
  if (comeca(bytes, [0x47, 0x49, 0x46, 0x38])) return "gif";
  if (comeca(bytes, [0x52, 0x49, 0x46, 0x46]) && comeca(bytes, [0x57, 0x45, 0x42, 0x50], 8)) return "webp";
  if (comeca(bytes, [0x52, 0x49, 0x46, 0x46]) && comeca(bytes, [0x57, 0x41, 0x56, 0x45], 8)) return "wav";
  if (comeca(bytes, [0x66, 0x74, 0x79, 0x70], 4)) return "mp4"; // ....ftyp (mp4, m4v, mov, m4a)
  if (comeca(bytes, [0x1a, 0x45, 0xdf, 0xa3])) return "webm";
  if (comeca(bytes, [0x49, 0x44, 0x33]) || comeca(bytes, [0xff, 0xfb]) || comeca(bytes, [0xff, 0xf3]) || comeca(bytes, [0xff, 0xf2])) return "mp3";
  if (comeca(bytes, [0x4f, 0x67, 0x67, 0x53])) return "ogg";
  if (comeca(bytes, [0x66, 0x4c, 0x61, 0x43])) return "flac";
  return "desconhecido";
}

/* Extensões cujo conteúdo TEM de bater com a assinatura. */
const ESPERADO: Record<string, FormatoReal[]> = {
  pdf: ["pdf"],
  zip: ["zip"],
  docx: ["zip"],
  xlsx: ["zip"],
  xlsm: ["zip"],
  pptx: ["zip"],
  epub: ["zip"],
  odt: ["zip"],
  ods: ["zip"],
  odp: ["zip"],
  png: ["png"],
  jpg: ["jpeg"],
  jpeg: ["jpeg"],
  gif: ["gif"],
  webp: ["webp"],
  wav: ["wav"],
  mp4: ["mp4"],
  m4v: ["mp4"],
  mov: ["mp4"],
  m4a: ["mp4"],
  webm: ["webm"],
  mp3: ["mp3"],
  ogg: ["ogg"],
  flac: ["flac"],
};

/* Confere o começo do arquivo contra a extensão. */
export function conferirConteudo(extensao: string, bytes: Uint8Array): { ok: true } | { ok: false; motivo: string } {
  const real = formatoReal(bytes);
  if (real === "executavel") return { ok: false, motivo: "O arquivo é um programa disfarçado. Programas não são aceitos." };
  if (real === "rar" || real === "7z") return { ok: false, motivo: "O arquivo é um .rar ou .7z com outro nome. Compacte em .zip." };
  const esperado = ESPERADO[extensao.toLowerCase()];
  if (esperado && !esperado.includes(real)) {
    return { ok: false, motivo: `O conteúdo não é de um .${extensao.toLowerCase()} de verdade. Exporte o arquivo de novo e envie.` };
  }
  return { ok: true };
}

/* Os formatos como a tela mostra. */
export const FORMATOS_NA_TELA = ["MP4", "PDF", "PNG", "JPG", "SVG", "ZIP", "CDR", "AI", "PSD", "MP3", "EPUB", "DOCX"];
export const RECUSADOS_NA_TELA = ["EXE", "RAR"];
