import { getRequestAuditContext } from "@/lib/audit";

export type TransactionalNotificationInput = {
  to: string;
  subject: string;
  text: string;
  html?: string;
  metadata?: Record<string, unknown>;
};

export type TransactionalNotificationResult = {
  ok: boolean;
  provider?: string;
  error?: string;
};

type TransportName = "webhook" | "console" | "none";

function normalizeProvider(value: string | undefined) {
  const normalized = value?.trim().toLowerCase();
  if (!normalized) {
    return "auto";
  }

  if (normalized === "webhook") {
    return "webhook" as TransportName;
  }

  if (normalized === "console") {
    return "console" as TransportName;
  }

  return normalized as "auto" | TransportName;
}

function isProduction() {
  return process.env.NODE_ENV === "production";
}

function buildPayload(input: TransactionalNotificationInput, context?: { ipAddress?: string | null; userAgent?: string | null }) {
  return {
    to: input.to,
    subject: input.subject,
    text: input.text,
    html: input.html ?? null,
    metadata: input.metadata ?? null,
    type: "transactional",
    providerContext: {
      ipAddress: context?.ipAddress ?? null,
      userAgent: context?.userAgent ?? null
    }
  };
}

function getWebhookUrl() {
  return process.env.NOTIFICATION_WEBHOOK_URL?.trim();
}

function getWebhookToken() {
  return process.env.NOTIFICATION_WEBHOOK_TOKEN?.trim();
}

function getWebhookTimeoutMs() {
  const raw = process.env.NOTIFICATION_WEBHOOK_TIMEOUT_MS?.trim();
  const parsed = Number(raw);
  if (!Number.isFinite(parsed) || parsed <= 0) {
    return 10_000;
  }
  return Math.min(60_000, parsed);
}

async function sendViaWebhook(input: TransactionalNotificationInput, context?: { ipAddress?: string | null; userAgent?: string | null }) {
  const url = getWebhookUrl();
  if (!url) {
    return {
      ok: false,
      error: "NOTIFICATION_WEBHOOK_URL is not configured"
    } as const;
  }

  const headers: Record<string, string> = {
    "Content-Type": "application/json"
  };

  const token = getWebhookToken();
  if (token) {
    headers.Authorization = `Bearer ${token}`;
  }

  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), getWebhookTimeoutMs());

  try {
    const response = await fetch(url, {
      method: "POST",
      headers,
      body: JSON.stringify(buildPayload(input, context)),
      signal: controller.signal
    });

    clearTimeout(timeout);
    if (!response.ok) {
      const errorBody = await response.text();
      return {
        ok: false,
        error: `Webhook notification failed ${response.status}: ${errorBody}`.slice(0, 600)
      };
    }

    return { ok: true, provider: "webhook" };
  } catch (error) {
    clearTimeout(timeout);
    return {
      ok: false,
      error: error instanceof Error ? error.message : "Webhook notification error"
    };
  }
}

export async function sendTransactionalNotification(input: TransactionalNotificationInput): Promise<TransactionalNotificationResult> {
  if (!input.to || !input.subject || !input.text) {
    return { ok: false, error: "Notification payload is incomplete" };
  }

  const provider = normalizeProvider(process.env.NOTIFICATION_PROVIDER);
  const explicitWebhookUrl = getWebhookUrl();
  const requestContext = await getRequestAuditContext().catch(() => ({
    ipAddress: null,
    userAgent: null
  }));

  if (provider === "none") {
    return { ok: false, error: "Notification delivery disabled" };
  }

  if (provider === "console" || (!provider && !isProduction())) {
    // eslint-disable-next-line no-console
    console.log(`NOTIFICATION subject=${input.subject} to=${input.to}`);
    // eslint-disable-next-line no-console
    console.log(`NOTIFICATION_BODY ${input.text}`);
    return { ok: true, provider: "console" };
  }

  const result = await sendViaWebhook(input, requestContext);
  if (!result.ok && !provider && !isProduction() && !explicitWebhookUrl) {
    // eslint-disable-next-line no-console
    console.log(`NOTIFICATION_FALLBACK subject=${input.subject} to=${input.to}`);
    // eslint-disable-next-line no-console
    console.log(`NOTIFICATION_BODY ${input.text}`);
    return { ok: true, provider: "console" };
  }

  return result.ok ? { ok: true, provider: result.provider } : { ok: false, error: result.error };
}
