/**
 * <ModalEditarPlano> — editar um plano comercial.
 *
 * O ecrã Planos era uma matriz de comparação só de leitura. Editar dentro de
 * uma matriz de colunas seria ilegível, por isso a edição é de um plano de
 * cada vez, num formulário.
 *
 * ## O que está trancado e porquê
 *
 * Quando o plano já tem contratos assinados, os limites ficam bloqueados —
 * alguém comprou aquilo por escrito, e só o plano o guarda. O preço não: o
 * contrato guarda a mensalidade acordada, e mudar o do plano só vale para quem
 * assinar a seguir (aprovado pelo FC a 22 Set). O formulário mostra-os na
 * mesma, desactivados e com o motivo à vista, em vez de os esconder: quem abre
 * isto quer saber o que **não** pode mudar tanto como o que pode.
 *
 * O servidor recusa na mesma; isto é o ecrã a não oferecer.
 */

/** Os campos, na ordem e com o vocabulário da matriz de comparação. */
const CAMPOS_PLANO = [
  { grupo: 'Identificação', campos: [
    { chave: 'name', rotulo: 'Nome', tipo: 'texto' },
    { chave: 'is_public', rotulo: 'Preço público', tipo: 'booleano',
      ajuda: 'Desligado, o plano aparece como "sob consulta"' },
  ] },
  { grupo: 'Preço', campos: [
    { chave: 'monthly_price_eur', rotulo: 'Mensalidade (€)', tipo: 'numero',
      // Num plano "sob consulta" a lista devolve `null`, porque o preço do
      // plano não significa nada aí — o valor de cada cliente está no contrato.
      // Deixar o campo vazio é honesto; o que não se pode é reenviá-lo vazio.
      ajuda: 'Só para planos de preço público. Sob consulta, o valor vive no contrato de cada cliente.' },
  ] },
  { grupo: 'Limites', contratado: true, campos: [
    { chave: 'max_users', rotulo: 'Utilizadores', tipo: 'numero', ilimitavel: true },
    { chave: 'max_lojas', rotulo: 'Lojas', tipo: 'numero', ilimitavel: true },
    { chave: 'max_campanhas_active', rotulo: 'Campanhas activas', tipo: 'numero', ilimitavel: true },
  ] },
  { grupo: 'Inclui', campos: [
    // Não há campo para "painéis DOOH": o direito a spots na rede Brand
    // Digital é a quota em Recursos, e o painel do cliente vem do equipamento
    // atribuído. Havia aqui um `has_dooh` que misturava as duas coisas.
    { chave: 'has_pdv', rotulo: 'Agente no ponto de venda', tipo: 'booleano' },
    { chave: 'has_api', rotulo: 'API', tipo: 'booleano' },
    { chave: 'max_social_networks', rotulo: 'Redes sociais', tipo: 'numero' },
  ] },
  { grupo: 'Recursos', campos: [
    { chave: 'ai_credits_eur_month', rotulo: 'Créditos de IA (€/mês)', tipo: 'numero', contratado: true },
    { chave: 'storage_gb', rotulo: 'Armazenamento (GB)', tipo: 'numero', contratado: true },
    { chave: 'branddigital_spots_month', rotulo: 'Spots Branddigital (/mês)', tipo: 'numero' },
    { chave: 'support_level', rotulo: 'Suporte', tipo: 'suporte' },
  ] },
];

const SUPORTE_OPCOES = [
  { valor: 'base', rotulo: 'Base' },
  { valor: 'priority', rotulo: 'Prioritário' },
  { valor: 'dedicated', rotulo: 'Dedicado' },
];

/** Do que o `/admin/plans` devolve para os nomes de coluna que o PATCH aceita. */
function comoFormulario(p) {
  return {
    name: p.nome,
    is_public: p.publico,
    monthly_price_eur: p.mensalidade,
    max_users: p.limites.utilizadores,
    max_lojas: p.limites.lojas,
    max_campanhas_active: p.limites.campanhas_activas,
    max_social_networks: p.limites.redes_sociais,
    has_pdv: p.funcionalidades.pdv,
    has_api: p.funcionalidades.api,
    ai_credits_eur_month: p.creditos_ia,
    storage_gb: p.armazenamento_gb,
    branddigital_spots_month: p.spots_mensais,
    support_level: p.suporte,
  };
}

function ModalEditarPlano({ aberto, plano, onFechar, onConcluido }) {
  const [f, setF] = React.useState({});
  const [motivo, setMotivo] = React.useState('');
  const [erro, setErro] = React.useState(null);
  const [aGuardar, setAGuardar] = React.useState(false);

  React.useEffect(() => {
    if (!aberto || !plano) return;
    setF(comoFormulario(plano));
    setMotivo('');
    setErro(null);
    setAGuardar(false);
  }, [aberto, plano]);

  if (!plano) return null;

  const trancado = plano.contas_com_contrato > 0;
  const alterar = (chave, valor) => setF((s) => ({ ...s, [chave]: valor }));

  /** Só o que mudou. O servidor recusa um PATCH que não muda nada. */
  function mudancas() {
    const inicial = comoFormulario(plano);
    const fora = new Set();
    if (trancado) {
      for (const g of CAMPOS_PLANO) {
        for (const c of g.campos) if (g.contratado || c.contratado) fora.add(c.chave);
      }
    }
    const saida = {};
    for (const [k, v] of Object.entries(f)) {
      if (fora.has(k)) continue;
      const antes = inicial[k];
      const igual = antes === null && v === null ? true : antes === v;
      if (!igual) saida[k] = v;
    }
    return saida;
  }

  const alteradas = mudancas();
  const nAlteradas = Object.keys(alteradas).length;

  async function guardar() {
    setErro(null);
    setAGuardar(true);
    try {
      await api.patch(`/api/admin/plans/${plano.codigo}`, { ...alteradas, motivo: motivo.trim() });
      onConcluido(`${f.name}: ${nAlteradas} ${nAlteradas === 1 ? 'campo alterado' : 'campos alterados'}.`);
    } catch (err) {
      setErro(err.message);
      setAGuardar(false);
    }
  }

  function campo(c, bloqueado) {
    const valor = f[c.chave];
    const id = `plano-${c.chave}`;
    const comum = 'w-full border border-border bg-background px-3 py-2 text-sm text-foreground outline-none focus-visible:border-accent disabled:opacity-40';

    if (c.tipo === 'booleano') {
      return (
        <label htmlFor={id} className="flex items-baseline gap-2">
          <input
            id={id}
            type="checkbox"
            checked={Boolean(valor)}
            disabled={bloqueado}
            onChange={(e) => alterar(c.chave, e.target.checked)}
            className="mt-0.5 accent-primary disabled:opacity-40"
          />
          <span className="min-w-0">
            <span className={`block text-sm ${bloqueado ? 'text-muted-foreground' : 'text-foreground'}`}>
              {c.rotulo}
            </span>
            {c.ajuda && <span className="mt-0.5 block text-xs text-muted-foreground">{c.ajuda}</span>}
          </span>
        </label>
      );
    }

    if (c.tipo === 'suporte') {
      return (
        <div>
          <label htmlFor={id} className="label-eyebrow block">{c.rotulo}</label>
          <select
            id={id}
            value={valor || 'base'}
            disabled={bloqueado}
            onChange={(e) => alterar(c.chave, e.target.value)}
            className={`mt-1 ${comum}`}
          >
            {SUPORTE_OPCOES.map((o) => (
              <option key={o.valor} value={o.valor} className="bg-background">{o.rotulo}</option>
            ))}
          </select>
        </div>
      );
    }

    if (c.tipo === 'texto') {
      return (
        <div>
          <label htmlFor={id} className="label-eyebrow block">{c.rotulo}</label>
          <input
            id={id}
            type="text"
            value={valor ?? ''}
            disabled={bloqueado}
            onChange={(e) => alterar(c.chave, e.target.value)}
            className={`mt-1 ${comum}`}
          />
        </div>
      );
    }

    // Numérico. `null` é ilimitado nos campos que o permitem, e é diferente de
    // zero — a matriz já faz essa distinção, e o campo tem de a deixar exprimir.
    const ilimitado = c.ilimitavel && valor === null;
    return (
      <div>
        <label htmlFor={id} className="label-eyebrow block">{c.rotulo}</label>
        <div className="mt-1 flex items-center gap-3">
          <input
            id={id}
            type="number"
            min="0"
            value={ilimitado ? '' : (valor ?? '')}
            disabled={bloqueado || ilimitado}
            onChange={(e) => alterar(c.chave, e.target.value === '' ? null : Number(e.target.value))}
            className={`${comum} ${c.ilimitavel ? 'grow' : ''}`}
          />
          {c.ilimitavel && (
            <label className="flex shrink-0 items-center gap-2 text-xs text-muted-foreground">
              <input
                type="checkbox"
                checked={ilimitado}
                disabled={bloqueado}
                onChange={(e) => alterar(c.chave, e.target.checked ? null : 0)}
                className="accent-primary disabled:opacity-40"
              />
              sem limite
            </label>
          )}
        </div>
      </div>
    );
  }

  return (
    <Modal
      aberto={aberto}
      onFechar={onFechar}
      titulo={`Editar ${plano.nome}`}
      acoes={
        <>
          <button
            type="button"
            onClick={onFechar}
            className="border border-border px-4 py-2 text-sm uppercase tracking-wider text-muted-foreground outline-none hover:text-foreground focus-visible:ring-1 focus-visible:ring-accent"
          >
            Cancelar
          </button>
          <button
            type="button"
            disabled={!nAlteradas || !motivo.trim() || aGuardar}
            onClick={guardar}
            className="bg-primary px-4 py-2 text-sm uppercase tracking-wider text-primary-foreground outline-none hover:bg-primary/90 focus-visible:ring-1 focus-visible:ring-accent disabled:cursor-not-allowed disabled:opacity-40"
          >
            {aGuardar ? 'A guardar…' : nAlteradas ? `Guardar ${nAlteradas}` : 'Guardar'}
          </button>
        </>
      }
    >
      {trancado && (
        <p className="border-l-2 border-primary bg-muted px-3 py-2 text-xs text-foreground">
          Este plano tem {plano.contas_com_contrato}{' '}
          {plano.contas_com_contrato === 1 ? 'contrato assinado' : 'contratos assinados'}. Os limites já
          foram acordados por escrito e não se mudam aqui — para os mudar, cria um plano novo e move as
          contas. O preço muda-se: vale para quem assinar a seguir, e quem já assinou fica com a
          mensalidade do contrato.
        </p>
      )}

      <div className={trancado ? 'mt-4 space-y-5' : 'space-y-5'}>
        {CAMPOS_PLANO.map((g) => (
          <fieldset key={g.grupo}>
            <legend className="label-eyebrow">{g.grupo}</legend>
            <div className="mt-2 space-y-3">
              {g.campos.map((c) => (
                <div key={c.chave}>{campo(c, trancado && (g.contratado || c.contratado))}</div>
              ))}
            </div>
          </fieldset>
        ))}

        <div>
          <label htmlFor="motivo-plano" className="label-eyebrow block">Porquê</label>
          <textarea
            id="motivo-plano"
            rows={2}
            value={motivo}
            onChange={(e) => setMotivo(e.target.value)}
            placeholder="Ex.: revisão de preços para 2027"
            className="mt-1 w-full resize-none border border-border bg-background px-3 py-2 text-sm text-foreground outline-none placeholder:text-muted-foreground focus-visible:border-accent"
          />
          <p className="mt-1 text-xs text-muted-foreground">
            Fica no registo de auditoria, com o teu nome e a data.
          </p>
        </div>

        {erro && (
          <p role="alert" className="border-l-2 border-primary bg-muted px-3 py-2 text-sm text-foreground">
            {erro}
          </p>
        )}
      </div>
    </Modal>
  );
}
