import { DEFAULT_SETTINGS, type Settings, type SiteDefinition } from "./types";
import { validateDefinitionConfiguration } from "./site-definitions";
import type { DefinitionSubscription } from "./types";

const KEY = "vibeguard.settings";
export const DEFAULT_SUBSCRIPTION: DefinitionSubscription = {
  id: "vibeguard-default",
  name: "VibeGuard default definitions",
  url: "https://git.jordanwages.com/wagesj45/vibeguard/raw/branch/main/src/content/definitions/default.json",
  enabled: true,
  priority: 0
};

export async function loadSettings(): Promise<Settings> {
  const stored = await getStorageItem(KEY);
  return mergeSettings(stored[KEY] as Partial<Settings> | undefined);
}

export async function saveSettings(update: Partial<Settings>): Promise<Settings> {
  const settings = mergeSettings({ ...(await loadSettings()), ...update });
  await setStorageItem({ [KEY]: settings });
  return settings;
}

function getStorageItem(key: string): Promise<Record<string, unknown>> {
  return new Promise((resolve, reject) => {
    chrome.storage.local.get(key, (items) => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve(items);
    });
  });
}

function setStorageItem(items: Record<string, unknown>): Promise<void> {
  return new Promise((resolve, reject) => {
    chrome.storage.local.set(items, () => {
      const error = chrome.runtime.lastError;
      if (error) reject(new Error(error.message));
      else resolve();
    });
  });
}

type StoredSettings = Partial<Settings> & { cpuThreads?: unknown; inferenceDevice?: unknown };

export function mergeSettings(input?: StoredSettings): Settings {
  // CPU thread selection was removed. Do not carry the legacy value forward
  // when an existing installation saves its settings.
  const { cpuThreads: _legacyCpuThreads, inferenceDevice: _legacyInferenceDevice, ...current } = input ?? {};
  const subscriptions = current.subscriptions?.length ? current.subscriptions : [DEFAULT_SUBSCRIPTION];
  const definitionConfiguration = validateDefinitionConfiguration({
    customDefinitions: current.customDefinitions ?? DEFAULT_SETTINGS.customDefinitions,
    disabledDefinitionIds: current.disabledDefinitionIds ?? DEFAULT_SETTINGS.disabledDefinitionIds,
    subscriptions,
    subscriptionCollections: current.subscriptionCollections ?? DEFAULT_SETTINGS.subscriptionCollections
  });
  return {
    ...DEFAULT_SETTINGS,
    ...current,
    threshold: clamp(Number(current.threshold ?? DEFAULT_SETTINGS.threshold), 0, 1),
    customDefinitions: definitionConfiguration.valid ? definitionConfiguration.value.customDefinitions : DEFAULT_SETTINGS.customDefinitions,
    disabledDefinitionIds: definitionConfiguration.valid ? definitionConfiguration.value.disabledDefinitionIds : DEFAULT_SETTINGS.disabledDefinitionIds,
    subscriptions: definitionConfiguration.valid ? definitionConfiguration.value.subscriptions : [DEFAULT_SUBSCRIPTION],
    subscriptionCollections: definitionConfiguration.valid ? definitionConfiguration.value.subscriptionCollections : DEFAULT_SETTINGS.subscriptionCollections
  };
}

export function saveDefinitionConfiguration(configuration: { customDefinitions: SiteDefinition[]; disabledDefinitionIds: string[]; subscriptions?: DefinitionSubscription[]; subscriptionCollections?: Settings["subscriptionCollections"] }): Promise<Settings> {
  const validation = validateDefinitionConfiguration(configuration);
  if (!validation.valid) return Promise.reject(new Error(validation.errors.join("\n")));
  return saveSettings(validation.value);
}

export function setDefinitionPaused(settings: Settings, definitionId: string, paused: boolean): Settings {
  const disabledDefinitionIds = new Set(settings.disabledDefinitionIds);
  if (paused) disabledDefinitionIds.add(definitionId);
  else disabledDefinitionIds.delete(definitionId);
  return mergeSettings({ ...settings, disabledDefinitionIds: [...disabledDefinitionIds] });
}

function clamp(value: number, min: number, max: number): number {
  return Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : min;
}
