import { config } from "./config.js";
import {
  ensureSetting,
  generateSecret,
  listSettings,
  removeSetting,
  setSetting,
  SettingKey,
  StoredSettings
} from "./settingsStore.js";

export interface RuntimeSettings {
  paperlessBaseUrl: string | null;
  paperlessExternalUrl: string | null;
  paperlessToken: string | null;
  schedulerIntervalMinutes: number;
  alertDaysBefore: number;
  mailServer: string | null;
  mailPort: number | null;
  mailUsername: string | null;
  mailPassword: string | null;
  mailUseTls: boolean;
  mailFrom: string | null;
  mailTo: string | null;
  ntfyServerUrl: string | null;
  ntfyTopic: string | null;
  ntfyToken: string | null;
  ntfyPriority: string | null;
  authUsername: string | null;
  authPassword: string | null;
  icalSecret: string | null;
}

const numericKeys = new Set<SettingKey>(["schedulerIntervalMinutes", "alertDaysBefore", "mailPort"]);
const booleanKeys = new Set<SettingKey>(["mailUseTls"]);

function coerceNumber(value: unknown, fallback: number): number {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  if (typeof value === "string" && value.trim() !== "") {
    const parsed = Number(value);
    if (!Number.isNaN(parsed)) {
      return parsed;
    }
  }
  return fallback;
}

function coerceBoolean(value: unknown, fallback: boolean): boolean {
  if (typeof value === "boolean") {
    return value;
  }
  if (typeof value === "string") {
    return ["1", "true", "yes", "on"].includes(value.toLowerCase());
  }
  return fallback;
}

function coerceString(value: unknown, fallback: string | null): string | null {
  if (typeof value === "string") {
    const trimmed = value.trim();
    return trimmed.length === 0 ? null : trimmed;
  }
  return fallback;
}

export function getRuntimeSettings(): RuntimeSettings {
  const stored = listSettings();

  const schedulerIntervalMinutes = coerceNumber(
    stored.schedulerIntervalMinutes,
    config.schedulerIntervalMinutes
  );
  const alertDaysBefore = coerceNumber(stored.alertDaysBefore, config.alertDaysBefore);
  const mailPort = stored.mailPort !== undefined ? coerceNumber(stored.mailPort, config.mailPort) : config.mailPort;

  return {
    paperlessBaseUrl: coerceString(stored.paperlessBaseUrl, config.paperlessBaseUrl ?? null),
    paperlessExternalUrl: coerceString(stored.paperlessExternalUrl, config.paperlessExternalUrl ?? null),
    paperlessToken: coerceString(stored.paperlessToken, config.paperlessToken ?? null),
    schedulerIntervalMinutes,
    alertDaysBefore,
    mailServer: coerceString(stored.mailServer, config.mailServer ?? null),
    mailPort,
    mailUsername: coerceString(stored.mailUsername, config.mailUsername ?? null),
    mailPassword: coerceString(stored.mailPassword, config.mailPassword ?? null),
    mailUseTls:
      stored.mailUseTls !== undefined
        ? coerceBoolean(stored.mailUseTls, config.mailUseTls)
        : config.mailUseTls,
    mailFrom: coerceString(stored.mailFrom, config.mailFrom ?? null),
    mailTo: coerceString(stored.mailTo, config.mailTo ?? null),
    ntfyServerUrl: coerceString(stored.ntfyServerUrl, config.ntfyServerUrl ?? null),
    ntfyTopic: coerceString(stored.ntfyTopic, config.ntfyTopic ?? null),
    ntfyToken: coerceString(stored.ntfyToken, config.ntfyToken ?? null),
    ntfyPriority: coerceString(stored.ntfyPriority, config.ntfyPriority ?? null),
    authUsername: coerceString(stored.authUsername, config.authUsername ?? null),
    authPassword: coerceString(stored.authPassword, config.authPassword ?? null),
    icalSecret: coerceString(stored.icalSecret, config.icalSecret ?? null)
  };
}

export function updateRuntimeSettings(update: Partial<RuntimeSettings>): RuntimeSettings {
  const keys = Object.keys(update) as SettingKey[];
  for (const key of keys) {
    const value = update[key as keyof RuntimeSettings];
    if (value === undefined || value === null || value === "") {
      removeSetting(key);
      continue;
    }

    if (numericKeys.has(key)) {
      const numericValue = coerceNumber(value, 0);
      setSetting(key, numericValue);
    } else if (booleanKeys.has(key)) {
      const boolValue = coerceBoolean(value, false);
      setSetting(key, boolValue);
    } else {
      setSetting(key, value);
    }
  }

  return getRuntimeSettings();
}

export function ensureIcalSecret(): string {
  const value = ensureSetting("icalSecret", () => generateSecret(24));
  return String(value);
}

export function regenerateIcalSecret(): string {
  const secret = generateSecret(24);
  setSetting("icalSecret", secret);
  return secret;
}

export function getStoredSettings(): StoredSettings {
  return listSettings();
}
