import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
import * as db from "./db";
import { ENV } from "./_core/env";
import { providerDefaults, resolveMailConfig, type MailEnvInput, type SupportedMailProvider } from "@shared/mailProviders";

export const MAIL_SETTINGS_KEY = "system.mail.settings";

type StoredMailSettings = {
  smtpProvider: SupportedMailProvider;
  smtpHost?: string;
  smtpPort?: string;
  smtpUser?: string;
  smtpFrom?: string;
  smtpSecure?: boolean;
  smtpRequireTls?: boolean;
  smtpPassEncrypted?: string;
  updatedByUserId?: number;
  updatedByUserName?: string | null;
};

export type MailSettingsFormInput = {
  smtpProvider: SupportedMailProvider;
  smtpHost?: string;
  smtpPort?: string;
  smtpUser?: string;
  smtpFrom?: string;
  smtpSecure?: boolean;
  smtpRequireTls?: boolean;
  smtpPass?: string;
};

export type AdminMailSettings = {
  smtpProvider: SupportedMailProvider;
  smtpHost: string;
  smtpPort: string;
  smtpUser: string;
  smtpFrom: string;
  smtpSecure: boolean;
  smtpRequireTls: boolean;
  smtpPassConfigured: boolean;
  ready: boolean;
  missing: string[];
  source: "environment" | "admin" | "admin+environment";
  hasSavedConfig: boolean;
  updatedAt: string | null;
  updatedByUserId: number | null;
  updatedByUserName: string | null;
};

function buildCipherKey() {
  return createHash("sha256")
    .update(ENV.cookieSecret || "local-dev-mail-settings")
    .digest();
}

function encryptSecret(value: string) {
  const iv = randomBytes(12);
  const key = buildCipherKey();
  const cipher = createCipheriv("aes-256-gcm", key, iv);
  const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
  const tag = cipher.getAuthTag();
  return `${iv.toString("base64")}.${tag.toString("base64")}.${encrypted.toString("base64")}`;
}

function decryptSecret(value?: string) {
  if (!value) return "";

  const [ivBase64, tagBase64, encryptedBase64] = value.split(".");
  if (!ivBase64 || !tagBase64 || !encryptedBase64) {
    return "";
  }

  try {
    const key = buildCipherKey();
    const decipher = createDecipheriv(
      "aes-256-gcm",
      key,
      Buffer.from(ivBase64, "base64")
    );
    decipher.setAuthTag(Buffer.from(tagBase64, "base64"));
    const decrypted = Buffer.concat([
      decipher.update(Buffer.from(encryptedBase64, "base64")),
      decipher.final(),
    ]);
    return decrypted.toString("utf8");
  } catch {
    return "";
  }
}

function trimOrEmpty(value?: string | null) {
  return String(value || "").trim();
}

function normalizeStoredSettings(input: Partial<StoredMailSettings> | null | undefined): StoredMailSettings | null {
  if (!input?.smtpProvider) return null;

  return {
    smtpProvider: input.smtpProvider,
    smtpHost: trimOrEmpty(input.smtpHost),
    smtpPort: trimOrEmpty(input.smtpPort),
    smtpUser: trimOrEmpty(input.smtpUser),
    smtpFrom: trimOrEmpty(input.smtpFrom),
    smtpSecure: Boolean(input.smtpSecure),
    smtpRequireTls: Boolean(input.smtpRequireTls),
    smtpPassEncrypted: trimOrEmpty(input.smtpPassEncrypted),
    updatedByUserId: input.updatedByUserId ? Number(input.updatedByUserId) : undefined,
    updatedByUserName: input.updatedByUserName?.trim() || null,
  };
}

async function getStoredMailSettingsRecord() {
  const setting = await db.getPortalSettingRecord(MAIL_SETTINGS_KEY);
  if (!setting?.valeur) {
    return { setting: null, payload: null as StoredMailSettings | null };
  }

  try {
    const parsed = JSON.parse(setting.valeur);
    return {
      setting,
      payload: normalizeStoredSettings(parsed),
    };
  } catch {
    return {
      setting,
      payload: null,
    };
  }
}

function mergeMailInput(stored: StoredMailSettings | null) {
  const decryptedPassword = decryptSecret(stored?.smtpPassEncrypted);
  const sourceEntries: Array<keyof MailEnvInput> = [
    "smtpProvider",
    "smtpHost",
    "smtpPort",
    "smtpUser",
    "smtpPass",
    "smtpFrom",
    "smtpSecure",
    "smtpRequireTls",
  ];

  const storedInput: MailEnvInput = {
    smtpProvider: stored?.smtpProvider,
    smtpHost: stored?.smtpHost,
    smtpPort: stored?.smtpPort,
    smtpUser: stored?.smtpUser,
    smtpPass: decryptedPassword,
    smtpFrom: stored?.smtpFrom,
    smtpSecure: stored ? String(Boolean(stored.smtpSecure)) : "",
    smtpRequireTls: stored ? String(Boolean(stored.smtpRequireTls)) : "",
  };

  const envInput: MailEnvInput = {
    smtpProvider: ENV.smtpProvider,
    smtpHost: ENV.smtpHost,
    smtpPort: ENV.smtpPort,
    smtpUser: ENV.smtpUser,
    smtpPass: ENV.smtpPass,
    smtpFrom: ENV.smtpFrom,
    smtpSecure: ENV.smtpSecure,
    smtpRequireTls: ENV.smtpRequireTls,
  };

  const merged: MailEnvInput = {};
  let usesEnvironmentFallback = false;
  for (const key of sourceEntries) {
    const storedValue = trimOrEmpty(storedInput[key]);
    const envValue = trimOrEmpty(envInput[key]);
    if (storedValue) {
      merged[key] = storedInput[key];
    } else if (envValue) {
      merged[key] = envInput[key];
      if (stored) {
        usesEnvironmentFallback = true;
      }
    }
  }

  const source: AdminMailSettings["source"] = stored
    ? usesEnvironmentFallback
      ? "admin+environment"
      : "admin"
    : "environment";

  return { merged, source };
}

export async function getResolvedMailConfiguration() {
  const { setting, payload } = await getStoredMailSettingsRecord();
  const { merged, source } = mergeMailInput(payload);
  return {
    config: resolveMailConfig(merged),
    source,
    stored: payload,
    updatedAt: setting?.updatedAt?.toISOString?.() ?? null,
  };
}

export async function getAdminMailSettings(): Promise<AdminMailSettings> {
  const { config, source, stored, updatedAt } = await getResolvedMailConfiguration();
  const defaults = stored?.smtpProvider && stored.smtpProvider !== "custom"
    ? providerDefaults[stored.smtpProvider]
    : null;

  return {
    smtpProvider: config.provider,
    smtpHost: stored?.smtpHost || config.host || defaults?.host || "",
    smtpPort: stored?.smtpPort || (config.port ? String(config.port) : defaults?.port ? String(defaults.port) : ""),
    smtpUser: stored?.smtpUser || config.user || "",
    smtpFrom: stored?.smtpFrom || config.from || "",
    smtpSecure: stored ? Boolean(stored.smtpSecure) : config.secure,
    smtpRequireTls: stored ? Boolean(stored.smtpRequireTls) : config.requireTLS,
    smtpPassConfigured: Boolean(stored?.smtpPassEncrypted || ENV.smtpPass),
    ready: config.ready,
    missing: config.missing,
    source,
    hasSavedConfig: Boolean(stored),
    updatedAt,
    updatedByUserId: stored?.updatedByUserId ?? null,
    updatedByUserName: stored?.updatedByUserName ?? null,
  };
}

export async function saveAdminMailSettings(
  input: MailSettingsFormInput,
  actor?: { id: number; name?: string | null }
) {
  const existing = await getStoredMailSettingsRecord();
  const nextPassword = trimOrEmpty(input.smtpPass)
    ? encryptSecret(trimOrEmpty(input.smtpPass))
    : existing.payload?.smtpPassEncrypted || "";

  const payload: StoredMailSettings = {
    smtpProvider: input.smtpProvider,
    smtpHost: trimOrEmpty(input.smtpHost),
    smtpPort: trimOrEmpty(input.smtpPort),
    smtpUser: trimOrEmpty(input.smtpUser),
    smtpFrom: trimOrEmpty(input.smtpFrom),
    smtpSecure: Boolean(input.smtpSecure),
    smtpRequireTls: Boolean(input.smtpRequireTls),
    smtpPassEncrypted: nextPassword,
    updatedByUserId: actor?.id,
    updatedByUserName: actor?.name?.trim() || null,
  };

  await db.setPortalSetting(
    MAIL_SETTINGS_KEY,
    JSON.stringify(payload),
    "Configuration SMTP et messagerie du portail"
  );

  return getAdminMailSettings();
}
