import { writable } from "svelte/store";
import { get } from "svelte/store";
import { invoke } from "$lib/runtime/invoke";

const defaultTags = ["Personal", "Work", "Ideas", "Journal"];
const defaultFragmentTypes = ["Quote", "Snippet", "Reference"];
const startupViews = [
  "entries",
  "calendar",
  "fragments",
  "todos",
  "lists",
  "coach",
] as const;
const defaultStartupView = "entries";
export type StartupView = (typeof startupViews)[number];

export const settingsTags = writable<string[]>([...defaultTags]);
export const settingsFragmentTypes = writable<string[]>([
  ...defaultFragmentTypes,
]);
export const settingsDefaultStartupView =
  writable<StartupView>(defaultStartupView);

let hydrationComplete = false;
let hydrating = false;

type UiSettingsPayload = {
  tags?: string[];
  fragmentTypes?: string[];
  defaultStartupView?: string;
};

function normalize(value: string): string {
  return value.trim().toLowerCase();
}

function hasDuplicate(
  values: string[],
  candidate: string,
  excludeIndex?: number,
): boolean {
  const normalized = normalize(candidate);
  return values.some(
    (value, index) => index !== excludeIndex && normalize(value) === normalized,
  );
}

function normalizeValues(values: string[], fallback: string[]): string[] {
  const seen = new Set<string>();
  const result: string[] = [];
  for (const value of values) {
    const trimmed = value.trim();
    if (!trimmed) continue;
    const key = normalize(trimmed);
    if (seen.has(key)) continue;
    seen.add(key);
    result.push(trimmed);
  }
  return result.length ? result : [...fallback];
}

function normalizeStartupView(value: string | undefined): StartupView {
  const normalized = (value ?? "").trim().toLowerCase();
  if (startupViews.includes(normalized as StartupView)) {
    return normalized as StartupView;
  }
  return defaultStartupView;
}

export async function hydrateUiSettings(): Promise<void> {
  if (hydrating || hydrationComplete) return;
  hydrating = true;
  try {
    const payload = await invoke<UiSettingsPayload>("get_ui_settings");
    settingsTags.set(normalizeValues(payload.tags ?? [], defaultTags));
    settingsFragmentTypes.set(
      normalizeValues(payload.fragmentTypes ?? [], defaultFragmentTypes),
    );
    settingsDefaultStartupView.set(
      normalizeStartupView(payload.defaultStartupView),
    );
  } catch (error) {
    console.error("[settings] hydrate failed", error);
  } finally {
    hydrating = false;
    hydrationComplete = true;
  }
}

export async function persistUiSettings(): Promise<void> {
  const tags = normalizeValues(get(settingsTags), defaultTags);
  const fragmentTypes = normalizeValues(
    get(settingsFragmentTypes),
    defaultFragmentTypes,
  );
  const startupView = normalizeStartupView(get(settingsDefaultStartupView));
  settingsTags.set(tags);
  settingsFragmentTypes.set(fragmentTypes);
  settingsDefaultStartupView.set(startupView);

  await invoke("set_ui_settings", {
    tags,
    fragmentTypes,
    fragment_types: fragmentTypes,
    defaultStartupView: startupView,
    default_startup_view: startupView,
  });
}

function queuePersist(): void {
  if (!hydrationComplete || hydrating) return;
  void persistUiSettings().catch((error) => {
    console.error("[settings] persist failed", error);
  });
}

export function addSettingsTag(value: string): boolean {
  const next = value.trim();
  if (!next) return false;
  const tags = get(settingsTags);
  if (hasDuplicate(tags, next)) return false;
  settingsTags.set([...tags, next]);
  queuePersist();
  return true;
}

export function updateSettingsTag(index: number, value: string): boolean {
  const next = value.trim();
  if (!next) return false;
  const tags = get(settingsTags);
  if (index < 0 || index >= tags.length) return false;
  if (hasDuplicate(tags, next, index)) return false;
  settingsTags.set(tags.map((tag, idx) => (idx === index ? next : tag)));
  queuePersist();
  return true;
}

export function removeSettingsTag(index: number): boolean {
  const tags = get(settingsTags);
  if (index < 0 || index >= tags.length) return false;
  settingsTags.set(tags.filter((_, idx) => idx !== index));
  queuePersist();
  return true;
}

export function addFragmentType(value: string): boolean {
  const next = value.trim();
  if (!next) return false;
  const types = get(settingsFragmentTypes);
  if (hasDuplicate(types, next)) return false;
  settingsFragmentTypes.set([...types, next]);
  queuePersist();
  return true;
}

export function updateFragmentType(index: number, value: string): boolean {
  const next = value.trim();
  if (!next) return false;
  const types = get(settingsFragmentTypes);
  if (index < 0 || index >= types.length) return false;
  if (hasDuplicate(types, next, index)) return false;
  settingsFragmentTypes.set(
    types.map((type, idx) => (idx === index ? next : type)),
  );
  queuePersist();
  return true;
}

export function removeFragmentType(index: number): boolean {
  const types = get(settingsFragmentTypes);
  if (index < 0 || index >= types.length) return false;
  settingsFragmentTypes.set(types.filter((_, idx) => idx !== index));
  queuePersist();
  return true;
}

export function setDefaultStartupView(value: string): boolean {
  const next = normalizeStartupView(value);
  if (get(settingsDefaultStartupView) === next) return false;
  settingsDefaultStartupView.set(next);
  queuePersist();
  return true;
}
