import {
  AVISO_DE_DADOS,
  SITUACAO_DO_PEDIDO,
  SITUACAO_FINANCEIRA,
  UNIDADE,
  contagem,
  dinheiro,
  exigir,
  falha,
  hojeEmBrasilia,
  inicioDoDia,
  limparBusca,
  nomeDoCliente,
  recusar,
  somarDias,
  texto,
  um,
} from "./comum";
import { opcoesParaLer } from "./orcamento-itens";
import type { FerramentaDeLeitura } from "./tipos";

type ClienteEmbutido = { id?: string | null; name?: string | null; company_name?: string | null; whatsapp?: string | null; email?: string | null };
type OrcamentoEmbutido = { id?: string | null; number?: number | null; title?: string | null };

type PedidoDaLista = {
  id: string;
  number: number | null;
  status: string | null;
  total_amount: number | null;
  paid_amount: number | null;
  financial_status: string | null;
  created_at: string | null;
  delivery_date: string | null;
  clients?: ClienteEmbutido | ClienteEmbutido[] | null;
  quotes?: OrcamentoEmbutido | OrcamentoEmbutido[] | null;
};

const SITUACOES = ["aguardando_producao", "em_producao", "pronto", "entregue", "cancelado", "em_andamento", "todos"] as const;
const SITUACAO_NO_BANCO: Record<string, string> = {
  aguardando_producao: "pending_production",
  em_producao: "in_production",
  pronto: "finished",
  entregue: "delivered",
  cancelado: "canceled",
};
const PAGAMENTOS = ["pago", "em_aberto", "pago_em_parte", "todos"] as const;
const PAGAMENTO_NO_BANCO: Record<string, string> = { pago: "paid", em_aberto: "unpaid", pago_em_parte: "partially_paid" };

function linhaDoPedido(row: PedidoDaLista, hoje: string) {
  const cliente = um(row.clients);
  const orcamento = um(row.quotes);
  const total = dinheiro(row.total_amount);
  const pago = dinheiro(row.paid_amount);
  const encerrado = row.status === "delivered" || row.status === "canceled";
  return {
    id: row.id,
    numero: row.number,
    situacao: SITUACAO_DO_PEDIDO[row.status ?? ""] ?? row.status,
    cliente: nomeDoCliente(cliente),
    cliente_id: cliente?.id ?? null,
    titulo: orcamento?.title || null,
    total,
    pago,
    em_aberto: row.status === "canceled" ? 0 : dinheiro(Math.max(0, total - pago)),
    pagamento: SITUACAO_FINANCEIRA[row.financial_status ?? ""] ?? row.financial_status,
    criado_em: row.created_at,
    entrega: row.delivery_date,
    ...(row.delivery_date && !encerrado && row.delivery_date < hoje ? { atrasado: true } : {}),
  };
}

export const listarPedidos: FerramentaDeLeitura = {
  nome: "listar_pedidos",
  titulo: "Listar pedidos",
  descricao:
    "Lista pedidos, do mais novo para o mais antigo, com cliente, valor, situação de produção, pagamento e data de entrega. Filtra por situação, pagamento, cliente, período de criação, período de entrega ou só os atrasados. A situação em_andamento junta aguardando produção, em produção e pronto.",
  campos: {
    situacao: { tipo: "opcao", descricao: "Situação de produção. O padrão é todos.", opcoes: SITUACOES },
    pagamento: { tipo: "opcao", descricao: "Situação do pagamento. O padrão é todos.", opcoes: PAGAMENTOS },
    apenas_atrasados: { tipo: "booleano", descricao: "Só os pedidos com entrega vencida e ainda não entregues." },
    busca: { tipo: "texto", descricao: "Número do pedido ou nome do cliente.", max: 80 },
    cliente_id: { tipo: "id", descricao: "Só os pedidos deste cliente." },
    criado_de: { tipo: "data", descricao: "Criados a partir deste dia." },
    criado_ate: { tipo: "data", descricao: "Criados até este dia." },
    entrega_de: { tipo: "data", descricao: "Com entrega a partir deste dia." },
    entrega_ate: { tipo: "data", descricao: "Com entrega até este dia." },
    limite: { tipo: "inteiro", descricao: "Quantos devolver. O padrão é 20.", min: 1, max: 50 },
  },
  tipo: "leitura",
  permissoes: ["orders.read"],
  async executar(ctx, args) {
    exigir(ctx, "orders.read", "ver os pedidos");
    const hoje = hojeEmBrasilia();
    const limite = (args.limite as number | undefined) ?? 20;
    const situacao = (args.situacao as string | undefined) ?? "todos";
    const pagamento = (args.pagamento as string | undefined) ?? "todos";
    const atrasados = args.apenas_atrasados === true;
    const busca = limparBusca(args.busca);

    let consulta = ctx.supabase
      .from("orders")
      .select(
        "id,number,status,total_amount,paid_amount,financial_status,created_at,delivery_date,clients(id,name,company_name),quotes(id,number,title)",
        { count: "exact" }
      )
      .in("user_id", ctx.tenantIds)
      .limit(limite);

    /* Atrasado se lê do mais velho para o mais novo: o que venceu há mais tempo primeiro. */
    consulta = atrasados ? consulta.order("delivery_date", { ascending: true }) : consulta.order("created_at", { ascending: false });

    if (situacao === "em_andamento") consulta = consulta.in("status", ["pending_production", "in_production", "finished"]);
    else if (situacao !== "todos") consulta = consulta.eq("status", SITUACAO_NO_BANCO[situacao]);
    if (pagamento !== "todos") consulta = consulta.eq("financial_status", PAGAMENTO_NO_BANCO[pagamento]);
    if (atrasados) consulta = consulta.lt("delivery_date", hoje).not("status", "in", "(delivered,canceled)");
    if (args.cliente_id) consulta = consulta.eq("client_id", String(args.cliente_id));
    if (args.criado_de) consulta = consulta.gte("created_at", inicioDoDia(String(args.criado_de)));
    if (args.criado_ate) consulta = consulta.lt("created_at", inicioDoDia(somarDias(String(args.criado_ate), 1)));
    if (args.entrega_de) consulta = consulta.gte("delivery_date", String(args.entrega_de));
    if (args.entrega_ate) consulta = consulta.lte("delivery_date", String(args.entrega_ate));

    if (busca) {
      const soNumero = busca.replace(/^#/, "");
      if (/^\d{1,9}$/.test(soNumero)) {
        consulta = consulta.eq("number", Number(soNumero));
      } else {
        const { data: clientes } = await ctx.supabase
          .from("clients")
          .select("id")
          .in("user_id", ctx.tenantIds)
          .or(`name.ilike.%${busca}%,company_name.ilike.%${busca}%`)
          .limit(40);
        const ids = ((clientes ?? []) as Array<{ id: string }>).map((cliente) => cliente.id);
        if (!ids.length) {
          return { dados: { _observacao: AVISO_DE_DADOS, pedidos: [], total_encontrado: 0 }, resumo: `Buscou pedidos por "${busca}" (nenhum encontrado)` };
        }
        consulta = consulta.in("client_id", ids);
      }
    }

    const { data, error, count } = await consulta;
    if (error) falha("listar os pedidos", error);
    const pedidos = ((data ?? []) as unknown as PedidoDaLista[]).map((row) => linhaDoPedido(row, hoje));

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        pedidos,
        total_encontrado: count ?? pedidos.length,
        soma_dos_listados: dinheiro(pedidos.reduce((soma, pedido) => soma + pedido.total, 0)),
        em_aberto_nos_listados: dinheiro(pedidos.reduce((soma, pedido) => soma + pedido.em_aberto, 0)),
        ...(count && count > pedidos.length ? { tem_mais: true } : {}),
      },
      resumo: `Listou pedidos${atrasados ? " atrasados" : ""} (${contagem(count ?? pedidos.length, "encontrado", "encontrados")})`,
    };
  },
};

type ItemDoPedido = {
  title: string | null;
  description: string | null;
  quantity: number | null;
  unit_price: number | null;
  total_price: number | null;
  unit_type: string | null;
  width_m: number | null;
  height_m: number | null;
  length_m: number | null;
  product_id: string | null;
  id: string;
  selected_options: unknown;
};

export const verPedido: FerramentaDeLeitura = {
  nome: "ver_pedido",
  titulo: "Ver um pedido",
  descricao:
    "Mostra um pedido por inteiro: cliente, itens (com as opções escolhidas de cada atributo, medidas e preços), valores, quanto já foi pago, situação de produção, data de entrega, rastreio e observações. Informe o id ou o número. Para mudar prazo, observações, rastreio ou trazer as mudanças do orçamento, use alterar_pedido.",
  campos: {
    pedido_id: { tipo: "id", descricao: "Id do pedido, devolvido por listar_pedidos." },
    numero: { tipo: "inteiro", descricao: "Número do pedido, como aparece no painel.", min: 1 },
  },
  tipo: "leitura",
  permissoes: ["orders.read"],
  async executar(ctx, args) {
    exigir(ctx, "orders.read", "ver os pedidos");
    if (!args.pedido_id && !args.numero) recusar("Informe pedido_id ou numero.");
    const hoje = hojeEmBrasilia();

    let consulta = ctx.supabase
      .from("orders")
      .select(
        "id,number,status,total_amount,paid_amount,financial_status,created_at,updated_at,delivery_date,notes,internal_notes,art_status,shipping_tracking_codes,shipping_tracking_url,production_first_started_at,production_finished_at,clients(id,name,company_name,whatsapp,email),quotes(id,number,title)"
      )
      .in("user_id", ctx.tenantIds)
      .order("created_at", { ascending: false })
      .limit(1);
    consulta = args.pedido_id ? consulta.eq("id", String(args.pedido_id)) : consulta.eq("number", Number(args.numero));

    const { data, error } = await consulta;
    if (error) falha("ler o pedido", error);
    const pedido = ((data ?? []) as unknown as Array<PedidoDaLista & Record<string, unknown>>)[0];
    if (!pedido) recusar("Pedido não encontrado. Use listar_pedidos para achar o id ou o número certo.");

    const { data: itens, error: erroDosItens } = await ctx.supabase
      .from("order_items")
      .select("id,title,description,quantity,unit_price,total_price,unit_type,width_m,height_m,length_m,product_id,selected_options")
      .eq("order_id", pedido.id)
      .order("created_at");
    if (erroDosItens) falha("ler os itens do pedido", erroDosItens);

    const cliente = um(pedido.clients);
    const orcamento = um(pedido.quotes);
    const rastreios = Array.isArray(pedido.shipping_tracking_codes) ? (pedido.shipping_tracking_codes as string[]).filter(Boolean) : [];

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        pedido: {
          ...linhaDoPedido(pedido, hoje),
          cliente: cliente
            ? { id: cliente.id, nome: nomeDoCliente(cliente), whatsapp: cliente.whatsapp || null, email: cliente.email || null }
            : null,
          orcamento: orcamento ? { id: orcamento.id, numero: orcamento.number } : null,
          itens: ((itens ?? []) as ItemDoPedido[]).map((item) => ({
            item_id: item.id,
            titulo: item.title ?? "",
            descricao: texto(item.description, 400),
            quantidade: Number(item.quantity ?? 0),
            vendido_por: UNIDADE[item.unit_type || "unit"] ?? "unidade",
            medidas:
              item.unit_type === "square_meter" && item.width_m && item.height_m
                ? `${item.width_m} m x ${item.height_m} m`
                : (item.unit_type === "linear_meter" || item.unit_type === "meter") && item.length_m
                  ? `${item.length_m} m`
                  : null,
            /* As opções escolhidas de cada atributo (Papel, Cor...): é o que a produção faz. */
            opcoes: opcoesParaLer(item.selected_options),
            preco_unitario: dinheiro(item.unit_price),
            total: dinheiro(item.total_price),
            produto_id: item.product_id,
          })),
          arte: pedido.art_status === "approved" ? "aprovada pelo cliente" : null,
          producao_comecou_em: pedido.production_first_started_at ?? null,
          producao_terminou_em: pedido.production_finished_at ?? null,
          rastreio: rastreios.length ? { codigos: rastreios, link: pedido.shipping_tracking_url || null } : null,
          observacoes: texto(pedido.notes, 1200),
          observacoes_internas: texto(pedido.internal_notes, 600),
          atualizado_em: pedido.updated_at ?? null,
        },
      },
      resumo: `Consultou o pedido nº ${pedido.number}`,
    };
  },
};
