import {
  interfaceSettingsSchema,
  INTERFACE_COMPLETA,
  interfaceTemDestino,
  type InterfaceSettings,
} from "@/lib/navigation/interface";
import { randomUUID } from "node:crypto";
import { env } from "@/lib/env";
import { audit } from "@/lib/audit";
import { signInviteToken, INVITE_TTL_SECONDS } from "@/lib/auth/invite-token";
import { buildInviteEmail } from "@/lib/email/templates/invite";
import { idiomaDoEmailDaOrganizacao } from "@/lib/email/idioma-da-organizacao";
import { sendEmail, type EmailDeliveryError, type TransporteDeEmail } from "@/lib/email/roteador";
import { marcaDaSaida } from "@/lib/branding/saida";
import type { Idioma } from "@/lib/i18n/idiomas";

/** Link sempre existe, inclusive quando a instalação não configurou e-mail. */
export async function issueInvite(input: {
  interfaceSettings?: InterfaceSettings;
  email: string;
  role: "viewer" | "agent" | "manager" | "admin";
  organizationId: string;
  orgName: string;
  inviterId: string;
  inviterName: string;
  requestId: string;
  inviteId?: string;
  issuedAt?: number;
  dispatch?: boolean;
  /**
   * O idioma do convite, quando quem chama já o sabe. Ausente, é resolvido pela
   * organização (`organizations.locale`) → `APP_LOCALE` → `pt-BR` (#4302).
   */
  idioma?: Idioma;
}) {
  const interfaceSettings = interfaceSettingsSchema.parse(
    input.interfaceSettings ?? INTERFACE_COMPLETA,
  );
  if (!interfaceTemDestino(interfaceSettings, input.role))
    throw new Error("Selecione ao menos uma área permitida ao papel.");
  const email = input.email.trim().toLowerCase();
  const inviteId = input.inviteId ?? randomUUID();
  const iat = input.issuedAt ?? Math.floor(Date.now() / 1000);
  const exp = iat + INVITE_TTL_SECONDS;
  const token = signInviteToken({
    invite_id: inviteId,
    email,
    organization_id: input.organizationId,
    role: input.role,
    exp,
    iat,
    invited_by: input.inviterId,
    interface_settings: interfaceSettings,
  });
  const acceptUrl = `${env.NEXT_PUBLIC_APP_URL.replace(/\/$/, "")}/team/accept-invite/${token}`;
  let dispatched = false;
  /**
   * POR QUE O MOTIVO É DEVOLVIDO, e não só o `false`.
   *
   * `email_dispatched: false` manda quem convidou procurar o link na tela, e
   * isso já funcionava. O que faltava era dizer POR QUE não saiu: e-mail nunca
   * configurado, servidor recusando o remetente, ou limite de envio estourado
   * levam a três consertos diferentes, e o booleano sozinho os achatava num
   * "não deu". O `via` acompanha porque, com dois transportes possíveis, "o
   * envio falhou" não diz onde olhar.
   */
  let deliveryError: EmailDeliveryError | undefined;
  let via: TransporteDeEmail | undefined;
  // Falhas de infraestrutura não desfazem a organização já criada nem o link.
  if (input.dispatch !== false) {
    try {
      const marca = await marcaDaSaida(input.organizationId, { semMemo: true });
      // A pessoa convidada ainda não tem preferência salva: o idioma vem da
      // organização que a convidou. Nunca lança — sem banco, sai em pt-BR.
      const idioma = input.idioma ?? (await idiomaDoEmailDaOrganizacao(input.organizationId));
      const message = buildInviteEmail({
        inviterName: input.inviterName,
        orgName: input.orgName,
        acceptUrl,
        role: input.role,
        expiresAt: new Date(exp * 1000),
        marca,
        idioma,
      });
      const result = await sendEmail({
        to: email,
        ...message,
        fromName: marca.nome,
        tags: [
          { name: "kind", value: "team_invite" },
          { name: "org", value: input.organizationId },
        ],
      });
      dispatched = result.ok;
      deliveryError = result.ok ? undefined : result.error;
      via = result.via;
    } catch {
      /* A superfície de recuperação é o link devolvido abaixo. */
      deliveryError = "send_failed";
    }
    await audit({
      action: "member.invited",
      actorUserId: input.inviterId,
      organizationId: input.organizationId,
      resourceType: "membership",
      resourceId: inviteId,
      requestId: input.requestId,
      metadata: {
        email,
        role: input.role,
        email_dispatched: dispatched,
        email_error: deliveryError ?? null,
        email_via: via ?? null,
      },
    });
  }
  return {
    email,
    invite_id: inviteId,
    expires_at: new Date(exp * 1000).toISOString(),
    email_dispatched: dispatched,
    email_error: deliveryError,
    accept_url: acceptUrl,
  };
}
