export interface StoredColumnConfig {
  visibleKeys: string[];
  orderedKeys: string[];
}

interface ResolveStoredColumnConfigOptions {
  storageKey: string;
  allKeys: string[];
  defaultVisibleKeys: string[];
  requiredKeys?: string[];
}

function getBrowserStorage() {
  if (typeof window === 'undefined') {
    return null;
  }

  const storage = window.localStorage;
  if (
    !storage
    || typeof storage.getItem !== 'function'
    || typeof storage.setItem !== 'function'
    || typeof storage.removeItem !== 'function'
  ) {
    return null;
  }

  return storage;
}

function readJson<T>(key: string): T | null {
  const storage = getBrowserStorage();
  if (!storage) {
    return null;
  }

  const rawValue = storage.getItem(key);
  if (!rawValue) {
    return null;
  }

  try {
    return JSON.parse(rawValue) as T;
  } catch {
    storage.removeItem(key);
    return null;
  }
}

function writeJson<T>(key: string, value: T) {
  const storage = getBrowserStorage();
  if (!storage) {
    return;
  }

  storage.setItem(key, JSON.stringify(value));
}

function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every(item => typeof item === 'string');
}

export function getStoredColumnConfig(storageKey: string): StoredColumnConfig | null {
  const value = readJson<StoredColumnConfig>(storageKey);

  if (!value || !isStringArray(value.visibleKeys) || !isStringArray(value.orderedKeys)) {
    return null;
  }

  return value;
}

export function saveStoredColumnConfig(storageKey: string, config: StoredColumnConfig) {
  writeJson(storageKey, config);
}

export function resolveStoredColumnConfig({
  storageKey,
  allKeys,
  defaultVisibleKeys,
  requiredKeys = [],
}: ResolveStoredColumnConfigOptions) {
  const stored = getStoredColumnConfig(storageKey);
  const allowedKeys = new Set(allKeys);
  const requiredKeySet = new Set(requiredKeys.filter(key => allowedKeys.has(key)));

  const orderedKeys = [
    ...new Set([
      ...(stored?.orderedKeys.filter(key => allowedKeys.has(key)) ?? []),
      ...allKeys,
    ]),
  ];

  const visibleKeys = new Set(
    (stored?.visibleKeys.filter(key => allowedKeys.has(key)) ?? defaultVisibleKeys.filter(key => allowedKeys.has(key)))
  );

  requiredKeySet.forEach(key => visibleKeys.add(key));

  return {
    visibleCols: visibleKeys,
    orderedKeys,
  };
}
