import * as db from "./db";
import {
  materialEventInventory,
  materialEventItems,
  materialEventReplacementValues,
  type MaterialEventItemKey,
} from "@shared/materialEvent";

export const LOGISTICS_SETTINGS_KEY = "system.logistics.settings";

export type LogisticsSettings = {
  materialReturnLeadDays: number;
  materialReturnGraceDays: number;
  inventory: Record<MaterialEventItemKey, number | null>;
  replacementValues: Record<MaterialEventItemKey, number | null>;
  updatedAt: string | null;
};

export const defaultLogisticsSettings: Omit<LogisticsSettings, "updatedAt"> = {
  materialReturnLeadDays: 4,
  materialReturnGraceDays: 3,
  inventory: {
    ...materialEventInventory,
  },
  replacementValues: {
    ...materialEventReplacementValues,
  },
};

function clampWholeNumber(value: unknown, fallback: number, min: number, max: number) {
  const parsed = Number.parseInt(String(value ?? ""), 10);
  if (!Number.isFinite(parsed)) return fallback;
  return Math.min(max, Math.max(min, parsed));
}

function sanitizeInventory(rawInventory: unknown) {
  const nextInventory = { ...defaultLogisticsSettings.inventory } as Record<MaterialEventItemKey, number | null>;

  if (!rawInventory || typeof rawInventory !== "object") {
    return nextInventory;
  }

  for (const item of materialEventItems) {
    const rawValue = (rawInventory as Record<string, unknown>)[item.key];
    if (rawValue === null || rawValue === "") {
      nextInventory[item.key] = null;
      continue;
    }

    const parsed = Number.parseInt(String(rawValue ?? ""), 10);
    if (Number.isFinite(parsed) && parsed >= 0) {
      nextInventory[item.key] = parsed;
    }
  }

  return nextInventory;
}

function sanitizeReplacementValues(rawReplacementValues: unknown) {
  const nextReplacementValues = {
    ...defaultLogisticsSettings.replacementValues,
  } as Record<MaterialEventItemKey, number | null>;

  if (!rawReplacementValues || typeof rawReplacementValues !== "object") {
    return nextReplacementValues;
  }

  for (const item of materialEventItems) {
    const rawValue = (rawReplacementValues as Record<string, unknown>)[item.key];
    if (rawValue === null || rawValue === "") {
      nextReplacementValues[item.key] = null;
      continue;
    }

    const parsed = Number.parseInt(String(rawValue ?? ""), 10);
    if (Number.isFinite(parsed) && parsed >= 0) {
      nextReplacementValues[item.key] = parsed;
    }
  }

  return nextReplacementValues;
}

function sanitizeLogisticsSettings(rawValue: unknown, updatedAt: string | null): LogisticsSettings {
  const source = rawValue && typeof rawValue === "object" ? rawValue as Record<string, unknown> : {};

  return {
    materialReturnLeadDays: clampWholeNumber(
      source.materialReturnLeadDays,
      defaultLogisticsSettings.materialReturnLeadDays,
      1,
      30
    ),
    materialReturnGraceDays: clampWholeNumber(
      source.materialReturnGraceDays,
      defaultLogisticsSettings.materialReturnGraceDays,
      0,
      30
    ),
    inventory: sanitizeInventory(source.inventory),
    replacementValues: sanitizeReplacementValues(source.replacementValues),
    updatedAt,
  };
}

export async function getLogisticsSettings(): Promise<LogisticsSettings> {
  const record = await db.getPortalSettingRecord(LOGISTICS_SETTINGS_KEY);
  if (!record?.valeur) {
    return {
      ...defaultLogisticsSettings,
      updatedAt: null,
    };
  }

  try {
    const parsed = JSON.parse(record.valeur);
    return sanitizeLogisticsSettings(parsed, record.updatedAt?.toISOString?.() || null);
  } catch {
    return {
      ...defaultLogisticsSettings,
      updatedAt: record.updatedAt?.toISOString?.() || null,
    };
  }
}

export async function saveLogisticsSettings(input: {
  materialReturnLeadDays?: number;
  materialReturnGraceDays?: number;
  inventory?: Partial<Record<MaterialEventItemKey, number | null>>;
  replacementValues?: Partial<Record<MaterialEventItemKey, number | null>>;
}) {
  const current = await getLogisticsSettings();
  const next = sanitizeLogisticsSettings(
    {
      materialReturnLeadDays: input.materialReturnLeadDays ?? current.materialReturnLeadDays,
      materialReturnGraceDays: input.materialReturnGraceDays ?? current.materialReturnGraceDays,
      inventory: {
        ...current.inventory,
        ...(input.inventory || {}),
      },
      replacementValues: {
        ...current.replacementValues,
        ...(input.replacementValues || {}),
      },
    },
    new Date().toISOString()
  );

  await db.setPortalSetting(
    LOGISTICS_SETTINGS_KEY,
    JSON.stringify({
      materialReturnLeadDays: next.materialReturnLeadDays,
      materialReturnGraceDays: next.materialReturnGraceDays,
      inventory: next.inventory,
      replacementValues: next.replacementValues,
    }),
    "Réglages logistiques globaux du portail"
  );

  return getLogisticsSettings();
}
