import { useSyncExternalStore } from 'react';

const STORAGE_KEY = 'learnitive:editor-tabs:v1';
const MAX_TABS_PER_WORKSPACE = 40;
const EMPTY_WORKSPACE = Object.freeze({ tabs: Object.freeze([]) as readonly string[], active: undefined as string | undefined });

type WorkspaceTabs = { tabs: readonly string[]; active?: string };
type EditorTabState = Record<string, WorkspaceTabs>;

let state: EditorTabState | undefined;
const listeners = new Set<() => void>();
let storageListenerInstalled = false;

function normalizePath(value: string): string {
  return String(value || '').replace(/^\/+|\/+$/gu, '');
}

function parseState(value: string | null): EditorTabState {
  if (!value) return {};
  try {
    const raw = JSON.parse(value) as Record<string, { tabs?: unknown; active?: unknown }>;
    const next: EditorTabState = {};
    for (const [workspace, entry] of Object.entries(raw || {})) {
      if (!workspace || !entry || !Array.isArray(entry.tabs)) continue;
      const tabs = [...new Set(entry.tabs.map((item) => normalizePath(String(item))).filter(Boolean))].slice(-MAX_TABS_PER_WORKSPACE);
      if (!tabs.length) continue;
      const requestedActive = typeof entry.active === 'string' ? normalizePath(entry.active) : '';
      next[workspace] = { tabs, active: tabs.includes(requestedActive) ? requestedActive : tabs.at(-1) };
    }
    return next;
  } catch {
    return {};
  }
}

function ensureState(): EditorTabState {
  if (state) return state;
  state = typeof window === 'undefined' ? {} : parseState(window.localStorage.getItem(STORAGE_KEY));
  if (typeof window !== 'undefined' && !storageListenerInstalled) {
    storageListenerInstalled = true;
    window.addEventListener('storage', (event) => {
      if (event.key !== STORAGE_KEY) return;
      state = parseState(event.newValue);
      for (const listener of listeners) listener();
    });
  }
  return state;
}

function commit(next: EditorTabState): void {
  state = next;
  if (typeof window !== 'undefined') {
    try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next)); } catch { /* local storage is an optional session convenience */ }
  }
  for (const listener of listeners) listener();
}

function updateWorkspace(workspace: string, updater: (current: WorkspaceTabs) => WorkspaceTabs | undefined): WorkspaceTabs {
  const currentState = ensureState();
  const current = currentState[workspace] || EMPTY_WORKSPACE;
  const updated = updater(current);
  if (updated === current) return current;
  const next = { ...currentState };
  if (updated?.tabs.length) next[workspace] = updated;
  else delete next[workspace];
  commit(next);
  return updated || EMPTY_WORKSPACE;
}

function subscribe(listener: () => void): () => void {
  ensureState();
  listeners.add(listener);
  return () => listeners.delete(listener);
}

function snapshot(workspace?: string): WorkspaceTabs {
  if (!workspace) return EMPTY_WORKSPACE;
  return ensureState()[workspace] || EMPTY_WORKSPACE;
}

export function useEditorTabs(workspace?: string): WorkspaceTabs {
  return useSyncExternalStore(subscribe, () => snapshot(workspace), () => EMPTY_WORKSPACE);
}

export function openEditorTab(workspace: string, path: string): WorkspaceTabs {
  const normalized = normalizePath(path);
  if (!workspace || !normalized) return EMPTY_WORKSPACE;
  return updateWorkspace(workspace, (current) => {
    const tabs = current.tabs.includes(normalized)
      ? current.tabs
      : [...current.tabs, normalized].slice(-MAX_TABS_PER_WORKSPACE);
    return { tabs, active: normalized };
  });
}

export function activateEditorTab(workspace: string, path: string): WorkspaceTabs {
  return openEditorTab(workspace, path);
}

export function closeEditorTab(workspace: string, path: string): WorkspaceTabs {
  const normalized = normalizePath(path);
  return updateWorkspace(workspace, (current) => {
    const index = current.tabs.indexOf(normalized);
    if (index < 0) return current;
    const tabs = current.tabs.filter((item) => item !== normalized);
    if (!tabs.length) return undefined;
    const active = current.active === normalized
      ? tabs[Math.min(index, tabs.length - 1)]
      : (current.active && tabs.includes(current.active) ? current.active : tabs.at(-1));
    return { tabs, active };
  });
}

export function moveEditorTab(workspace: string, fromPath: string, toPath: string): WorkspaceTabs {
  const from = normalizePath(fromPath);
  const to = normalizePath(toPath);
  if (!workspace || !from || !to || from === to) return snapshot(workspace);
  return updateWorkspace(workspace, (current) => {
    if (!current.tabs.includes(from)) return current;
    const tabs: string[] = [];
    for (const item of current.tabs) {
      const candidate = item === from ? to : item;
      if (!tabs.includes(candidate)) tabs.push(candidate);
    }
    return { tabs, active: current.active === from ? to : current.active };
  });
}

export function pruneEditorTabs(workspace: string, availablePaths: readonly string[]): WorkspaceTabs {
  if (!workspace) return EMPTY_WORKSPACE;
  const available = new Set(availablePaths.map(normalizePath));
  return updateWorkspace(workspace, (current) => {
    const tabs = current.tabs.filter((item) => available.has(item));
    if (tabs.length === current.tabs.length) return current;
    if (!tabs.length) return undefined;
    return { tabs, active: current.active && tabs.includes(current.active) ? current.active : tabs.at(-1) };
  });
}
