import { getEffectiveCompanyRole, type CompanyPermission } from "@/lib/company-context";
import { avisarLojaEEsperar } from "@/lib/loja/avisar-loja";

import { contatosNovos, recusaDeContatos } from "../contatos";
import type { ContextoDoAssistente } from "../contexto";
import {
  conferirMensagem,
  descreverValor,
  exemploDaMensagem,
  lerMensagem,
  lerObservacao,
  mesmoValor,
  MENSAGENS,
  observacaoPedida,
  QUAIS_MENSAGENS,
  qualMensagemPeloNome,
  TAMANHO_DA_MENSAGEM,
  TAMANHO_DA_OBSERVACAO,
  textoEfetivo,
  type Observacao,
  type QualMensagem,
} from "../personalizacoes/catalogo";
import {
  aplicarDesfazer,
  gravarValor,
  lerPerfil,
  listarPersonalizacoes,
  MENSAGEM_DO_TRAVADO,
  prepararDesfazer,
  registrarPersonalizacao,
} from "../personalizacoes/servidor";
import { AVISO_DE_DADOS, exigir, falha, pode, recusar } from "./comum";
import type { FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/**
 * Personalizar a conta pelo assistente: as mensagens do WhatsApp (as do
 * orçamento e do pedido, e as três da loja virtual) e a observação padrão do
 * orçamento.
 *
 * Toda mudança fica registrada com o valor de antes, e por isso pode ser
 * desfeita: pelo painel, com um clique, ou pedindo ao próprio assistente.
 */

const COMO_DESFAZER =
  "Fica registrado com o texto de antes: dá para desfazer no painel (Configurações, Integrações, Assistentes e Apps) ou pedindo ao assistente.";

function verTodas(ctx: ContextoDoAssistente) {
  const papel = getEffectiveCompanyRole(ctx.empresa, ctx.user.id);
  return papel === "owner" || papel === "admin";
}

function quem(ctx: ContextoDoAssistente) {
  return {
    companyId: ctx.chave.companyId,
    usuarioId: ctx.user.id,
    verTodas: verTodas(ctx),
    pode: (permissao: CompanyPermission) => pode(ctx, permissao),
  };
}

async function perfil(ctx: ContextoDoAssistente) {
  try {
    return await lerPerfil(ctx.supabase, ctx.donoDosDados);
  } catch (erro) {
    falha("ler as configurações da empresa", erro as { message?: string });
  }
}

async function registrar(ctx: ContextoDoAssistente, tipo: "mensagem_do_whatsapp" | "observacao_do_orcamento", alvo: string | null, antes: unknown, depois: unknown) {
  return registrarPersonalizacao(ctx.admin, {
    companyId: ctx.chave.companyId,
    donoId: ctx.donoDosDados,
    chaveId: ctx.chave.id,
    usuarioId: ctx.user.id,
    tipo,
    alvo,
    antes,
    depois,
  });
}

/* ------------------------------------------------------------------- ver */

export const verPersonalizacoes: FerramentaDeLeitura = {
  nome: "ver_personalizacoes",
  titulo: "Ver as personalizações",
  descricao:
    "Mostra o que dá para personalizar pelo assistente e como está hoje: o texto das mensagens do WhatsApp (a do orçamento, a do andamento do pedido e as três da loja virtual: dúvida sobre produto, cabeçalho e rodapé do pedido), com os marcadores que cada uma aceita, e a observação padrão que aparece em todo orçamento. Mostra também as últimas personalizações feitas pelos assistentes, com o id de cada uma para desfazer.",
  campos: {},
  tipo: "leitura",
  permissoes: ["whatsapp.manage", "quotes.write"],
  async executar(ctx) {
    const podeMensagens = pode(ctx, "whatsapp.manage");
    const podeObservacao = pode(ctx, "quotes.write");
    if (!podeMensagens && !podeObservacao) recusar("Esta pessoa não tem permissão para ver as personalizações.");

    const atual = await perfil(ctx);
    let historico: Awaited<ReturnType<typeof listarPersonalizacoes>> = [];
    try {
      historico = await listarPersonalizacoes(ctx.admin, quem(ctx), 10);
    } catch (erro) {
      falha("ler as personalizações feitas", erro as { message?: string });
    }

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        ...(podeMensagens
          ? {
              mensagens_do_whatsapp: QUAIS_MENSAGENS.map((qual) => {
                const salvo = lerMensagem(atual.whatsapp_templates, qual);
                const texto = textoEfetivo(qual, salvo);
                return {
                  qual: MENSAGENS[qual].nome,
                  texto,
                  e_o_texto_do_sistema: salvo === null,
                  marcadores: MENSAGENS[qual].marcadores.map((marcador) => ({ marcador: marcador.codigo, o_que_entra: marcador.descricao })),
                  como_o_cliente_le: exemploDaMensagem(qual, texto),
                  ...(MENSAGENS[qual].observacao ? { onde_vale: MENSAGENS[qual].observacao } : {}),
                };
              }),
            }
          : {}),
        ...(podeObservacao
          ? {
              observacao_padrao_do_orcamento: {
                ...lerObservacao(atual),
                onde_aparece: "Em todo orçamento: na tela, no PDF e no link que o cliente abre, acima da observação de cada orçamento.",
              },
            }
          : {}),
        personalizacoes_feitas: historico.map((linha) => ({
          id: linha.id,
          o_que: linha.titulo,
          quando: linha.quando,
          antes: linha.antes,
          depois: linha.depois,
          desfeita: Boolean(linha.desfeitaEm),
        })),
      },
      resumo: "Consultou as personalizações",
    };
  },
};

/* ------------------------------------------------------- mensagem do zap */

type MudancaDeMensagem = { qual: QualMensagem; antes: string | null; depois: string | null };

export const alterarMensagemDoWhatsapp: FerramentaDeEscrita<MudancaDeMensagem> = {
  nome: "alterar_mensagem_do_whatsapp",
  titulo: "Alterar uma mensagem do WhatsApp",
  descricao: `Troca o texto de uma mensagem do WhatsApp, ou volta ao texto do sistema: a que vai com o orçamento, a do andamento do pedido, e as três da loja virtual (a dúvida sobre produto, que o cliente manda da página do produto, e o cabeçalho e o rodapé do pedido que o carrinho manda). Use os marcadores entre chaves duplas para os dados que mudam a cada envio (veja ver_personalizacoes): eles são trocados pelo número, total, link etc. na hora de enviar. *texto* sai em negrito no WhatsApp. Até ${TAMANHO_DA_MENSAGEM} letras. A primeira chamada devolve uma prévia com o antes, o depois e como o cliente vai ler; depois de a pessoa aprovar, chame de novo com os mesmos dados e o código em \`confirmacao\`.`,
  campos: {
    qual: { tipo: "opcao", descricao: "Qual mensagem.", obrigatorio: true, opcoes: QUAIS_MENSAGENS.map((qual) => MENSAGENS[qual].nome) },
    texto: { tipo: "texto", descricao: "O texto novo da mensagem, com os marcadores.", max: TAMANHO_DA_MENSAGEM },
    voltar_ao_padrao: { tipo: "booleano", descricao: "Verdadeiro para voltar ao texto do sistema. Não use junto com texto." },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["whatsapp.manage"],
  async preparar(ctx, args) {
    exigir(ctx, "whatsapp.manage", "mudar as mensagens do WhatsApp");
    const qual = qualMensagemPeloNome(args.qual);
    if (!qual) recusar(`Mensagem desconhecida. Use uma destas: ${QUAIS_MENSAGENS.map((chave) => MENSAGENS[chave].nome).join(", ")}.`);
    const voltar = args.voltar_ao_padrao === true;
    if (voltar && args.texto !== undefined) recusar("Escolha um dos dois: o texto novo, ou voltar ao padrão.");
    if (!voltar && args.texto === undefined) recusar("Mande o texto novo da mensagem, ou voltar_ao_padrao.");

    const avisos: string[] = [];
    const antes = lerMensagem((await perfil(ctx)).whatsapp_templates, qual);
    const depois = voltar ? null : String(args.texto).trim();
    if (depois !== null) {
      const conferido = conferirMensagem(qual, depois);
      if (!conferido.ok) recusar(conferido.erro);
      avisos.push(...conferido.avisos);
      const contatos = contatosNovos(textoEfetivo(qual, antes), depois);
      if (contatos.length) recusar(recusaDeContatos(contatos, "no menu WhatsApp"));
    }

    if (mesmoValor(antes, depois) || textoEfetivo(qual, antes) === textoEfetivo(qual, depois)) recusar("Nada muda: a mensagem já está com este texto.");

    const definicao = MENSAGENS[qual];
    return {
      previa: {
        o_que_vai_acontecer: `A ${definicao.titulo.charAt(0).toLowerCase()}${definicao.titulo.slice(1)} vai mudar.`,
        hoje: `${textoEfetivo(qual, antes)}${antes === null ? " (texto do sistema)" : ""}`,
        passa_a_ser: `${textoEfetivo(qual, depois)}${depois === null ? " (texto do sistema)" : ""}`,
        como_o_cliente_vai_ler: exemploDaMensagem(qual, textoEfetivo(qual, depois)),
        desfazer: COMO_DESFAZER,
      },
      resumo: `Prévia da troca da mensagem do ${definicao.nome} no WhatsApp`,
      avisos,
      carga: { qual, antes, depois },
    };
  },
  async gravar(ctx, carga) {
    const definicao = MENSAGENS[carga.qual];
    let gravado: "ok" | "travado";
    try {
      gravado = await gravarValor(ctx.supabase, ctx.donoDosDados, "mensagem_do_whatsapp", definicao.chave, carga.depois);
    } catch (erro) {
      falha("gravar a mensagem", erro as { message?: string });
    }
    if (gravado === "travado") recusar(MENSAGEM_DO_TRAVADO);
    const id = await registrar(ctx, "mensagem_do_whatsapp", definicao.chave, carga.antes, carga.depois);
    /* As da loja: a loja guarda o perfil por dois minutos (a rota do painel não avisa). */
    const lojaOuviu = definicao.daLoja ? await avisarLojaEEsperar(ctx.donoDosDados) : true;

    return {
      dados: {
        alterado: true,
        mensagem: definicao.titulo,
        texto: textoEfetivo(carga.qual, carga.depois),
        ...(lojaOuviu ? {} : { observacao: "A loja pode levar até dois minutos para usar o texto novo." }),
        ...(id ? { personalizacao_id: id, desfazer: COMO_DESFAZER } : { atencao: "A mensagem mudou, mas não foi possível guardar o texto de antes para desfazer." }),
      },
      resumo: carga.depois === null ? `Voltou ao texto do sistema a mensagem do ${definicao.nome} no WhatsApp` : `Alterou a mensagem do ${definicao.nome} no WhatsApp`,
    };
  },
};

/* -------------------------------------------------------------- observação */

type MudancaDeObservacao = { antes: Observacao; depois: Observacao };

export const alterarObservacaoPadrao: FerramentaDeEscrita<MudancaDeObservacao> = {
  nome: "alterar_observacao_padrao",
  titulo: "Alterar a observação padrão do orçamento",
  descricao: `Troca, liga ou desliga a observação padrão: o texto que aparece em todo orçamento (na tela, no PDF e no link do cliente), acima da observação de cada um. Bom para prazo de produção, aprovação de arte, garantia, frete. Até ${TAMANHO_DA_OBSERVACAO} letras; quebras de linha são mantidas. A primeira chamada devolve uma prévia; depois de a pessoa aprovar, chame de novo com os mesmos dados e o código em \`confirmacao\`.`,
  campos: {
    texto: { tipo: "texto", descricao: "O texto novo da observação. Sem dizer `ligada`, ela já passa a aparecer.", max: TAMANHO_DA_OBSERVACAO },
    ligada: { tipo: "booleano", descricao: "Falso para parar de mostrar a observação (o texto fica guardado). Verdadeiro para voltar a mostrar." },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["quotes.write"],
  async preparar(ctx, args) {
    exigir(ctx, "quotes.write", "mudar a observação padrão dos orçamentos");
    if (args.texto === undefined && args.ligada === undefined) recusar("Mande o texto novo, ou `ligada` para ligar ou desligar a observação.");

    const antes = lerObservacao(await perfil(ctx));
    const pedida = observacaoPedida(antes, { texto: args.texto as string | undefined, ligada: args.ligada as boolean | undefined });
    if (!pedida.ok) recusar(pedida.erro);
    const depois = pedida.observacao;
    if (mesmoValor(antes, depois)) recusar("Nada muda: a observação padrão já está assim.");
    const contatos = contatosNovos(antes.texto, depois.texto);
    if (contatos.length) recusar(recusaDeContatos(contatos, "em Orçamentos, na configuração das observações"));

    return {
      previa: {
        o_que_vai_acontecer: !depois.ligada
          ? "A observação padrão deixa de aparecer nos orçamentos (o texto fica guardado)."
          : "A observação padrão vai aparecer em todo orçamento: na tela, no PDF e no link do cliente.",
        hoje: descreverValor("observacao_do_orcamento", antes, 2000),
        passa_a_ser: descreverValor("observacao_do_orcamento", depois, 2000),
        desfazer: COMO_DESFAZER,
      },
      resumo: "Prévia da troca da observação padrão do orçamento",
      avisos: [],
      carga: { antes, depois },
    };
  },
  async gravar(ctx, carga) {
    let gravado: "ok" | "travado";
    try {
      gravado = await gravarValor(ctx.supabase, ctx.donoDosDados, "observacao_do_orcamento", null, carga.depois);
    } catch (erro) {
      falha("gravar a observação padrão", erro as { message?: string });
    }
    if (gravado === "travado") recusar(MENSAGEM_DO_TRAVADO);
    const id = await registrar(ctx, "observacao_do_orcamento", null, carga.antes, carga.depois);

    return {
      dados: {
        alterado: true,
        observacao_padrao: carga.depois,
        ...(id ? { personalizacao_id: id, desfazer: COMO_DESFAZER } : { atencao: "A observação mudou, mas não foi possível guardar a de antes para desfazer." }),
      },
      resumo: carga.depois.ligada ? "Alterou a observação padrão do orçamento" : "Desligou a observação padrão do orçamento",
    };
  },
};

/* ---------------------------------------------------------------- desfazer */

type Desfazer = { id: number; titulo: string };

export const desfazerPersonalizacao: FerramentaDeEscrita<Desfazer> = {
  nome: "desfazer_personalizacao",
  titulo: "Desfazer uma personalização",
  descricao:
    "Volta uma configuração ao que era antes de uma personalização feita por assistente. Pegue o id em ver_personalizacoes. Só desfaz se ninguém mexeu na configuração depois; havendo várias, desfaça da mais nova para a mais antiga. A primeira chamada devolve uma prévia; depois de a pessoa aprovar, chame de novo com os mesmos dados e o código em `confirmacao`.",
  campos: {
    personalizacao_id: { tipo: "inteiro", descricao: "O id da personalização, devolvido por ver_personalizacoes.", obrigatorio: true, min: 1, max: 2_147_483_647 },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["whatsapp.manage", "quotes.write"],
  async preparar(ctx, args) {
    let preparo: Awaited<ReturnType<typeof prepararDesfazer>>;
    try {
      preparo = await prepararDesfazer(ctx.admin, ctx.supabase, { id: Number(args.personalizacao_id), ...quem(ctx) });
    } catch (erro) {
      falha("ler a personalização", erro as { message?: string });
    }
    if (!preparo.ok) recusar(preparo.mensagem);
    const { linha, atual } = preparo;
    return {
      previa: {
        o_que_vai_acontecer: `${linha.titulo} volta a ser como era antes da personalização.`,
        hoje: descreverValor(linha.tipo, atual, 2000),
        volta_a_ser: descreverValor(linha.tipo, linha.antes, 2000),
      },
      resumo: `Prévia de desfazer: ${linha.titulo}`,
      avisos: [],
      carga: { id: linha.id, titulo: linha.titulo },
    };
  },
  async gravar(ctx, carga) {
    /* De novo, do zero: entre a prévia e agora a configuração pode ter mudado. */
    let preparo: Awaited<ReturnType<typeof prepararDesfazer>>;
    try {
      preparo = await prepararDesfazer(ctx.admin, ctx.supabase, { id: carga.id, ...quem(ctx) });
    } catch (erro) {
      falha("ler a personalização", erro as { message?: string });
    }
    if (!preparo.ok) recusar(preparo.mensagem);

    let resultado: Awaited<ReturnType<typeof aplicarDesfazer>>;
    try {
      resultado = await aplicarDesfazer(ctx.admin, ctx.supabase, preparo, { usuarioId: ctx.user.id });
    } catch (erro) {
      falha("desfazer a personalização", erro as { message?: string });
    }
    if (!resultado.ok) recusar(resultado.mensagem);
    /* Desfez uma mensagem que a loja lê: a loja esquece a antiga na hora. */
    const daLoja = preparo.linha.tipo === "mensagem_do_whatsapp" && QUAIS_MENSAGENS.some((qual) => MENSAGENS[qual].daLoja && MENSAGENS[qual].chave === preparo.linha.alvo);
    if (daLoja) await avisarLojaEEsperar(ctx.donoDosDados);
    return {
      dados: { desfeito: true, o_que: carga.titulo },
      resumo: `Desfez: ${carga.titulo}`,
    };
  },
};
