/*
 * Calendário de 2027 para a gráfica (out/2026): os 12 meses em blocos de
 * 5 × 5 cm, colados um no outro (4 colunas × 3 linhas = 20 × 15 cm) no meio
 * de uma folha A4 deitada, com as linhas finas de corte entre eles. Cada
 * bloco: a faixa vermelha com o mês e o ano, os dias da semana em bolinhas
 * (domingo vermelho), os dias (domingo e feriado em vermelho), a linha das
 * datas do mês e as fases da lua.
 *
 * As contas do calendário saem da data (o dia 1 de cada mês cai no dia da
 * semana certo); feriados, datas e luas são os do calendário de referência
 * da gráfica. Mês que pede 6 semanas dobra a última no mesmo quadrinho
 * ("24/31", como nos calendários de bolso). Os dias de cada coluna são um
 * texto só (os números alinham e a arte fica leve no editor); cada mês é um
 * grupo (um clique pega o bloco inteiro, dois cliques entram na peça).
 */
import { novoId, solida, type DocumentoDoBanner, type Peca, type PecaDeForma, type PecaDeIcone, type PecaDeTexto } from "./documento";
import { documento, fundoDoBanner, texto, type CamposDoTexto, type PedidoDoModelo } from "./modelos-base";
import { encaixar } from "./modelos-colecao";

/* A4 deitada em 300 ppp; o bloco de 5 cm tem 590,55 px. */
const DPI = 300;
export const LARGURA_DA_A4_DEITADA = 3508;
export const ALTURA_DA_A4_DEITADA = 2480;
const B = (50 / 25.4) * DPI;
const X0 = (LARGURA_DA_A4_DEITADA - 4 * B) / 2;
const Y0 = (ALTURA_DA_A4_DEITADA - 3 * B) / 2;
/* Medidas dentro do bloco em centésimos do lado (u = 0,5 mm). */
const u = B / 100;

const VERMELHO = "#E3262E";
const ESCURO = "#1C1C1C";
const BOLINHA = "#3A3A3A";
const LINHA_DE_CORTE = "#B9B9B9";
/* Os dias em letra estreita e forte; a faixa, a semana, as datas e as luas em letra larga (como no calendário de referência). */
const FONTE = "barlow-condensed";
const FONTE_LARGA = "archivo";
const FONTE_DA_FAIXA = "archivo-black";

const MESES = ["JANEIRO", "FEVEREIRO", "MARÇO", "ABRIL", "MAIO", "JUNHO", "JULHO", "AGOSTO", "SETEMBRO", "OUTUBRO", "NOVEMBRO", "DEZEMBRO"];
const SEMANA = ["D", "S", "T", "Q", "Q", "S", "S"];
const NOMES_DA_SEMANA = ["domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado"];

type Fase = "nova" | "crescente" | "cheia" | "minguante";
const ROTULO_DA_FASE: Record<Fase, string> = { nova: "NOVA", crescente: "CRESC.", cheia: "CHEIA", minguante: "MING." };

/* O que muda de mês a mês em 2027: feriados (em vermelho), a linha das datas e as luas (dia de cada fase). */
type DadosDoMes = { feriados: number[]; datas: string; luas: Array<[Fase, number]>; noCanto?: string };
const DADOS_DE_2027: DadosDoMes[] = [
  { feriados: [1], datas: "1 - CONFRATERNIZAÇÃO UNIVERSAL", luas: [["nova", 7], ["crescente", 15], ["cheia", 22], ["minguante", 29]] },
  { feriados: [9], datas: "9 - CARNAVAL / 10 - QUARTA-FEIRA DE CINZAS", luas: [["nova", 6], ["crescente", 14], ["cheia", 20], ["minguante", 28]] },
  { feriados: [26], datas: "8 - DIA DA MULHER / 26 - PAIXÃO DE CRISTO / 28 - PÁSCOA", luas: [["nova", 8], ["crescente", 15], ["cheia", 22], ["minguante", 29]] },
  { feriados: [21], datas: "21 - TIRADENTES / 22 - DESCOBRIMENTO DO BRASIL", luas: [["nova", 6], ["crescente", 13], ["cheia", 20], ["minguante", 28]] },
  { feriados: [1, 27], datas: "1 - DIA DO TRABALHO / 9 - DIA DAS MÃES / 27 - CORPUS CHRISTI", luas: [["nova", 6], ["crescente", 13], ["cheia", 20], ["minguante", 28]] },
  { feriados: [], datas: "12 - DIA DOS NAMORADOS / 24 - SÃO JOÃO", luas: [["nova", 4], ["crescente", 11], ["cheia", 18], ["minguante", 27]] },
  { feriados: [], datas: "20 - DIA DO AMIGO", luas: [["nova", 4], ["crescente", 10], ["cheia", 18], ["minguante", 26]] },
  { feriados: [], datas: "8 - DIA DOS PAIS", luas: [["nova", 2], ["crescente", 9], ["cheia", 17], ["minguante", 24], ["nova", 31]] },
  { feriados: [7], datas: "7 - DIA DA INDEPENDÊNCIA DO BRASIL", luas: [["crescente", 7], ["cheia", 15], ["minguante", 23], ["nova", 29]] },
  { feriados: [12], datas: "12 - DIA DE NOSSA SENHORA APARECIDA / DIA DAS CRIANÇAS", luas: [["crescente", 7], ["cheia", 15], ["minguante", 22], ["nova", 29]] },
  { feriados: [2, 15, 20], datas: "15 - PROC. DA REPÚBLICA / 20 - DIA DA CONSCIÊNCIA NEGRA", luas: [["crescente", 6], ["cheia", 14], ["minguante", 20], ["nova", 28]], noCanto: "2 - DIA DE FINADOS" },
  { feriados: [25], datas: "25 - NATAL", luas: [["crescente", 6], ["cheia", 13], ["minguante", 20], ["nova", 27]] },
];

/**
 * As semanas do mês (linhas × 7 colunas, domingo primeiro; 0 = vazio). Com
 * 6 semanas, a sexta dobra na quinta: o quadrinho fica com os dois dias
 * ([24, 31]), como nos calendários de bolso.
 */
export function semanasDoMes(ano: number, mes: number): Array<Array<number | [number, number]>> {
  const primeiro = new Date(Date.UTC(ano, mes, 1)).getUTCDay();
  const dias = new Date(Date.UTC(ano, mes + 1, 0)).getUTCDate();
  const linhas: number[][] = [];
  for (let d = 1, i = primeiro; d <= dias; d += 1, i += 1) {
    const l = Math.floor(i / 7);
    (linhas[l] ??= Array(7).fill(0))[i % 7] = d;
  }
  const semanas: Array<Array<number | [number, number]>> = linhas.slice(0, 5).map((l) => [...l]);
  if (linhas[5]) linhas[5].forEach((d, c) => d && (semanas[4][c] = [semanas[4][c] as number, d]));
  return semanas;
}

/* --------------------------------------------------------------- peças */

type Grupo = { grupo: string; nome: string };
const t = (g: Grupo, nome: string, x: number, y: number, w: number, h: number, campos: CamposDoTexto): PecaDeTexto => texto({ nome: `${g.nome} · ${nome}`, grupo: g.grupo, x, y, w, h, fonte: FONTE, ...campos });

function f(g: Grupo | null, nome: string, forma: PecaDeForma["forma"], x: number, y: number, w: number, h: number, cor: string, extra: Partial<PecaDeForma> = {}): PecaDeForma {
  return { id: novoId("f"), tipo: "forma", nome: g ? `${g.nome} · ${nome}` : nome, ...(g ? { grupo: g.grupo } : {}), x, y, w, h, rot: 0, opacidade: 1, forma, pintura: solida(cor), raio: 0, borda: null, sombra: null, pontas: 5, profundidade: 0.2, ...extra };
}

/*
 * A lua crescente e a minguante: o arco de fora e o de dentro (24 × 24,
 * preenchidas), da altura da lua cheia e grossas como na referência, com a
 * parte que aparece no meio do desenho.
 */
const DESENHO_DA_LUA: Record<"crescente" | "minguante", string> = {
  crescente: `<path d="M4.25 1a11 11 0 0 1 0 22a4.5 11 0 0 0 0-22Z"/>`,
  minguante: `<path d="M19.75 1a11 11 0 0 0 0 22a4.5 11 0 0 1 0-22Z"/>`,
};

function iconeDaLua(g: Grupo, fase: "crescente" | "minguante", cx: number, cy: number, lado: number): PecaDeIcone {
  return { id: novoId("c"), tipo: "icone", nome: `${g.nome} · Lua ${fase}`, grupo: g.grupo, icone: `lua-${fase}`, desenho: DESENHO_DA_LUA[fase], x: cx - lado / 2, y: cy - lado / 2, w: lado, h: lado, rot: 0, opacidade: 1, pintura: solida(ESCURO), espessura: 0.5, preenchido: true, sombra: null };
}

const trecho = (conteudo: string, cor: string) => `<span style="color:${cor}">${conteudo}</span>`;

/* Os 7 centros das colunas e o topo de cada semana (5 linhas de 11u). */
const centroDaColuna = (c: number) => (10.7 + 13.1 * c) * u;
const ALTURA_DA_SEMANA = 11 * u;
const topoDaSemana = (l: number) => (24 + 11 * l) * u;
const LETRA_DO_DIA = 10.2 * u;

function blocoDoMes(ano: number, mes: number, bx: number, by: number): Peca[] {
  const dados = DADOS_DE_2027[mes];
  const g: Grupo = { grupo: `calendario-${ano}-${String(mes + 1).padStart(2, "0")}`, nome: `${MESES[mes][0]}${MESES[mes].slice(1).toLowerCase()}` };
  const vermelho = (dia: number, coluna: number) => coluna === 0 || dados.feriados.includes(dia);
  const pecas: Peca[] = [];

  /* A faixa do mês e do ano. */
  pecas.push(f(g, "Faixa", "retangulo", bx + 3.5 * u, by + 3.8 * u, 93 * u, 11.8 * u, VERMELHO, { raio: 1.2 * u }));
  pecas.push(t(g, "Mês", bx + 5.4 * u, by + 3.8 * u, 66 * u, 11.8 * u, { texto: MESES[mes], fonte: FONTE_DA_FAIXA, tamanho: 8.4 * u, peso: 400, cor: "#FFFFFF", vertical: "meio", linha: 1 }));
  pecas.push(t(g, "Ano", bx + 64 * u, by + 3.8 * u, 30.8 * u, 11.8 * u, { texto: String(ano), fonte: FONTE_DA_FAIXA, tamanho: 8.4 * u, peso: 400, cor: "#FFFFFF", alinhamento: "direita", vertical: "meio", linha: 1 }));

  /* Os dias da semana em bolinhas (domingo vermelho). */
  SEMANA.forEach((letra, c) => {
    const cx = bx + centroDaColuna(c);
    const cy = by + 20.4 * u;
    const d = 7 * u;
    /* A bolinha com o anel mais claro em volta. */
    pecas.push(f(g, `Bolinha de ${NOMES_DA_SEMANA[c]}`, "circulo", cx - d / 2, cy - d / 2, d, d, c === 0 ? VERMELHO : BOLINHA, { borda: { cor: c === 0 ? "#F4A3A6" : "#9A9A9A", largura: 0.45 * u } }));
    pecas.push(t(g, `Letra de ${NOMES_DA_SEMANA[c]}`, cx - d / 2, cy - d / 2, d, d, { texto: letra, fonte: FONTE_LARGA, tamanho: 4.6 * u, peso: 700, cor: "#FFFFFF", alinhamento: "centro", vertical: "meio", linha: 1 }));
  });

  /* Os dias: um texto por coluna (o domingo inteiro em vermelho, o feriado em vermelho no meio da coluna). */
  const semanas = semanasDoMes(ano, mes);
  for (let c = 0; c < 7; c += 1) {
    const linhas = semanas.map((s, l) => ({ l, dia: s[c] })).filter((x) => typeof x.dia === "number" && x.dia > 0) as Array<{ l: number; dia: number }>;
    if (!linhas.length) continue;
    const primeira = linhas[0].l;
    const cx = bx + centroDaColuna(c);
    const corDaColuna = c === 0 ? VERMELHO : ESCURO;
    const comFeriado = c !== 0 && linhas.some((x) => vermelho(x.dia, c));
    pecas.push(
      t(g, `Dias de ${NOMES_DA_SEMANA[c]}`, cx - 6.4 * u, by + topoDaSemana(primeira), 12.8 * u, linhas.length * ALTURA_DA_SEMANA, {
        texto: linhas.map((x) => String(x.dia)).join("\n"),
        ...(comFeriado ? { html: linhas.map((x) => (vermelho(x.dia, c) ? trecho(String(x.dia), VERMELHO) : String(x.dia))).join("<br>") } : {}),
        tamanho: LETRA_DO_DIA,
        peso: 800,
        cor: corDaColuna,
        alinhamento: "centro",
        vertical: "topo",
        linha: ALTURA_DA_SEMANA / LETRA_DO_DIA,
      }),
    );
  }
  /* A semana dobrada: "24/31" no mesmo quadrinho (o de cima menor no alto, o de baixo embaixo, a barra no meio). */
  semanas[4]?.forEach((dia, c) => {
    if (!Array.isArray(dia)) return;
    const [a, b] = dia;
    const cor = vermelho(a, c) ? VERMELHO : ESCURO;
    const cx = bx + centroDaColuna(c);
    const topo = by + topoDaSemana(4);
    pecas.push(t(g, `Dia ${a}`, cx - 6.6 * u, topo + 0.4 * u, 6.2 * u, 5.4 * u, { texto: String(a), tamanho: 5.6 * u, peso: 800, cor, alinhamento: "direita", vertical: "meio", linha: 1 }));
    pecas.push(t(g, `Barra ${a}/${b}`, cx - 2.6 * u, topo + 1 * u, 5.2 * u, 9 * u, { texto: "/", tamanho: 9 * u, peso: 600, cor, alinhamento: "centro", vertical: "meio", linha: 1, opacidade: 0.75 }));
    pecas.push(t(g, `Dia ${b}`, cx + 0.4 * u, topo + 5.4 * u, 6.2 * u, 5.4 * u, { texto: String(b), tamanho: 5.6 * u, peso: 800, cor, alinhamento: "esquerda", vertical: "meio", linha: 1 }));
  });
  /* A data que cabe no canto vazio da última semana (novembro: finados). */
  if (dados.noCanto) {
    const usadas = semanas[4].map((d, c) => (d ? c : -1)).filter((c) => c >= 0);
    const de = Math.max(...usadas) + 1;
    pecas.push(t(g, "Data no canto", bx + centroDaColuna(de) - 6 * u, by + topoDaSemana(4), (7 - de) * 13.1 * u, ALTURA_DA_SEMANA, { texto: dados.noCanto, fonte: FONTE_LARGA, tamanho: 2.5 * u, peso: 700, cor: ESCURO, alinhamento: "centro", vertical: "meio", linha: 1 }));
  }

  /* As datas do mês. */
  pecas.push(t(g, "Datas", bx + 3 * u, by + 80.4 * u, 94 * u, 4 * u, { texto: dados.datas, fonte: FONTE_LARGA, tamanho: 2.5 * u, peso: 700, cor: "#333333", alinhamento: "centro", vertical: "meio", linha: 1 }));

  /* As fases da lua: o desenho e, do lado, o dia em cima do nome da fase. */
  /* Quatro luas espaçadas como na referência; agosto (cinco) mais juntas. */
  const cinco = dados.luas.length > 4;
  const largura = cinco ? 19.2 : 24.5;
  dados.luas.forEach(([fase, dia], k) => {
    const lado = (cinco ? 6.6 : 7.6) * u;
    const cx = bx + (cinco ? 7.6 : 9.2) * u + k * largura * u;
    const x = cx - lado / 2;
    const cy = by + 90.4 * u;
    if (fase === "nova") pecas.push(f(g, "Lua nova", "circulo", cx - lado / 2, cy - lado / 2, lado, lado, ESCURO));
    else if (fase === "cheia") pecas.push(f(g, "Lua cheia", "circulo", cx - lado / 2 + 0.3 * u, cy - lado / 2 + 0.3 * u, lado - 0.6 * u, lado - 0.6 * u, "#FFFFFF", { borda: { cor: ESCURO, largura: 0.4 * u } }));
    else pecas.push(iconeDaLua(g, fase, cx, cy, lado * 1.18));
    const numero = String(dia).padStart(2, "0");
    const inicioDoTexto = fase === "crescente" || fase === "minguante" ? cx + lado * 0.36 : x + lado + 0.4 * u;
    pecas.push(
      /* Na crescente e na minguante a parte desenhada é estreita: o número encosta nela. */
      t(g, `Lua ${ROTULO_DA_FASE[fase]} ${numero}`, inicioDoTexto, cy - 5.4 * u, x + largura * u - inicioDoTexto, 10.8 * u, {
        texto: `${numero}\n${ROTULO_DA_FASE[fase]}`,
        html: `<span style="font-size:${Math.round((cinco ? 5.4 : 6.4) * u * 10) / 10}px">${numero}</span><br>${ROTULO_DA_FASE[fase]}`,
        fonte: FONTE_LARGA,
        tamanho: (cinco ? 2.7 : 3.3) * u,
        peso: 400,
        cor: "#3A3A3A",
        alinhamento: "esquerda",
        vertical: "meio",
        linha: 0.92,
      }),
    );
  });
  return pecas;
}

/** O calendário de 2027 inteiro na A4 deitada (300 ppp): 12 blocos de 5 × 5 cm e as linhas de corte. */
function calendario2027(): DocumentoDoBanner {
  const ano = 2027;
  const pecas: Peca[] = [];
  for (let mes = 0; mes < 12; mes += 1) pecas.push(...blocoDoMes(ano, mes, X0 + (mes % 4) * B, Y0 + Math.floor(mes / 4) * B));
  /* As linhas de corte (finas, cinza, travadas no lugar): a volta e as divisas dos blocos. */
  const fina = 2;
  for (let k = 0; k <= 4; k += 1) pecas.push(f(null, "Linha de corte", "retangulo", X0 + k * B - fina / 2, Y0 - fina / 2, fina, 3 * B + fina, LINHA_DE_CORTE, { bloqueada: true }));
  for (let k = 0; k <= 3; k += 1) pecas.push(f(null, "Linha de corte", "retangulo", X0 - fina / 2, Y0 + k * B - fina / 2, 4 * B + fina, fina, LINHA_DE_CORTE, { bloqueada: true }));
  return { ...documento(LARGURA_DA_A4_DEITADA, ALTURA_DA_A4_DEITADA, fundoDoBanner({ cor: "#FFFFFF", textura: "nenhuma" }), pecas), impressao: { dpi: DPI } };
}

/* Noutra A4 deitada (outra resolução), encaixa inteiro. */
function emQualquerA4(montar: () => DocumentoDoBanner) {
  return (p: PedidoDoModelo): DocumentoDoBanner => {
    const doc = montar();
    return p.largura === doc.largura && p.altura === doc.altura ? doc : { ...encaixar(doc, p.largura, p.altura), impressao: null };
  };
}

export const MODELOS_DE_CALENDARIO = [{ chave: "calendario-2027-5x5", nome: "Calendário 2027", descricao: "12 meses de 5 × 5 cm na A4 deitada · feriados e luas", montar: emQualquerA4(calendario2027) }].map((m) => ({ ...m, ramo: "grafica" as const, feitoPara: "a4-deitada" as const }));
