import { useSyncExternalStore } from 'react';
import {
  doesIsolatedChatRoomScopeMatch,
  normalizeIsolatedChatRoomScope,
  type IsolatedChatRoomScope,
} from './isolatedChatRooms';

const ACTIVE_SCOPE_STORAGE_KEY = 'isolated-chat-room:active-scope';
const SESSION_SCOPE_STORAGE_KEY = 'isolated-chat-room:session-scopes';
const WINDOW_OPEN_STORAGE_KEY = 'isolated-chat-room:window-open';
const MINIMIZED_SCOPE_LIST_STORAGE_KEY = 'isolated-chat-room:minimized-scopes';
const listeners = new Set<() => void>();
let cachedActiveScopeRaw = '';
let cachedActiveScopeValue: IsolatedChatRoomScope | null = null;
let cachedMinimizedScopeListRaw = '';
let cachedMinimizedScopeListValue: MinimizedIsolatedChatRoomEntry[] = [];

export type MinimizedIsolatedChatRoomEntry = {
  id: string;
  scope: IsolatedChatRoomScope;
  minimizedAt: string;
};

function emit() {
  listeners.forEach((listener) => listener());
}

function readStorageValue(key: string) {
  if (typeof window === 'undefined') {
    return '';
  }

  return window.sessionStorage.getItem(key) ?? '';
}

export function readActiveIsolatedChatRoomScope() {
  const rawValue = readStorageValue(ACTIVE_SCOPE_STORAGE_KEY) || 'null';

  if (rawValue === cachedActiveScopeRaw) {
    return cachedActiveScopeValue;
  }

  try {
    cachedActiveScopeRaw = rawValue;
    cachedActiveScopeValue = normalizeIsolatedChatRoomScope(JSON.parse(rawValue));
    return cachedActiveScopeValue;
  } catch {
    cachedActiveScopeRaw = rawValue;
    cachedActiveScopeValue = null;
    return cachedActiveScopeValue;
  }
}

export function writeActiveIsolatedChatRoomScope(scope: IsolatedChatRoomScope | null) {
  if (typeof window === 'undefined') {
    return;
  }

  const previousRawValue = readStorageValue(ACTIVE_SCOPE_STORAGE_KEY) || 'null';

  if (!scope) {
    if (previousRawValue === 'null') {
      cachedActiveScopeRaw = 'null';
      cachedActiveScopeValue = null;
      return;
    }

    cachedActiveScopeRaw = 'null';
    cachedActiveScopeValue = null;
    window.sessionStorage.removeItem(ACTIVE_SCOPE_STORAGE_KEY);
    emit();
    return;
  }

  const normalizedScope = normalizeIsolatedChatRoomScope(scope);

  if (!normalizedScope) {
    if (previousRawValue === 'null') {
      cachedActiveScopeRaw = 'null';
      cachedActiveScopeValue = null;
      return;
    }

    cachedActiveScopeRaw = 'null';
    cachedActiveScopeValue = null;
    window.sessionStorage.removeItem(ACTIVE_SCOPE_STORAGE_KEY);
    emit();
    return;
  }

  const rawValue = JSON.stringify(normalizedScope);

  if (rawValue === previousRawValue) {
    cachedActiveScopeRaw = rawValue;
    cachedActiveScopeValue = normalizedScope;
    return;
  }

  cachedActiveScopeRaw = rawValue;
  cachedActiveScopeValue = normalizedScope;
  window.sessionStorage.setItem(ACTIVE_SCOPE_STORAGE_KEY, rawValue);
  emit();
}

export function readIsolatedChatRoomsWindowOpen() {
  if (typeof window === 'undefined') {
    return false;
  }

  return readStorageValue(WINDOW_OPEN_STORAGE_KEY) === '1';
}

export function writeIsolatedChatRoomsWindowOpen(open: boolean) {
  if (typeof window === 'undefined') {
    return;
  }

  if (open) {
    window.sessionStorage.setItem(WINDOW_OPEN_STORAGE_KEY, '1');
  } else {
    window.sessionStorage.removeItem(WINDOW_OPEN_STORAGE_KEY);
  }

  emit();
}

function readSessionScopeMap() {
  if (typeof window === 'undefined') {
    return {} as Record<string, IsolatedChatRoomScope>;
  }

  try {
    const parsed = JSON.parse(readStorageValue(SESSION_SCOPE_STORAGE_KEY) || '{}') as Record<string, IsolatedChatRoomScope>;
    return typeof parsed === 'object' && parsed ? parsed : {};
  } catch {
    return {};
  }
}

function writeSessionScopeMap(map: Record<string, IsolatedChatRoomScope>) {
  if (typeof window === 'undefined') {
    return;
  }

  const rawValue = JSON.stringify(map);
  const previousRawValue = readStorageValue(SESSION_SCOPE_STORAGE_KEY) || '{}';

  if (rawValue === previousRawValue) {
    return;
  }

  window.sessionStorage.setItem(SESSION_SCOPE_STORAGE_KEY, rawValue);
  emit();
}

function createMinimizedEntryId() {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }

  return `minimized-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
}

function readMinimizedScopeList() {
  if (typeof window === 'undefined') {
    return [] as MinimizedIsolatedChatRoomEntry[];
  }

  const rawValue = readStorageValue(MINIMIZED_SCOPE_LIST_STORAGE_KEY) || '[]';

  if (rawValue === cachedMinimizedScopeListRaw) {
    return cachedMinimizedScopeListValue;
  }

  try {
    const parsed = JSON.parse(rawValue);

    if (!Array.isArray(parsed)) {
      cachedMinimizedScopeListRaw = rawValue;
      cachedMinimizedScopeListValue = [];
      return cachedMinimizedScopeListValue;
    }

    cachedMinimizedScopeListRaw = rawValue;
    cachedMinimizedScopeListValue = parsed
      .map((item) => {
        const scope = normalizeIsolatedChatRoomScope(item?.scope ?? null);

        if (!scope) {
          return null;
        }

        return {
          id: String(item?.id ?? '').trim() || createMinimizedEntryId(),
          scope,
          minimizedAt: String(item?.minimizedAt ?? '').trim() || new Date().toISOString(),
        } satisfies MinimizedIsolatedChatRoomEntry;
      })
      .filter((item): item is MinimizedIsolatedChatRoomEntry => item != null);
    return cachedMinimizedScopeListValue;
  } catch {
    cachedMinimizedScopeListRaw = rawValue;
    cachedMinimizedScopeListValue = [];
    return cachedMinimizedScopeListValue;
  }
}

function writeMinimizedScopeList(entries: MinimizedIsolatedChatRoomEntry[]) {
  if (typeof window === 'undefined') {
    return;
  }

  if (entries.length === 0) {
    cachedMinimizedScopeListRaw = '[]';
    cachedMinimizedScopeListValue = [];
    window.sessionStorage.removeItem(MINIMIZED_SCOPE_LIST_STORAGE_KEY);
    emit();
    return;
  }

  const rawValue = JSON.stringify(entries);
  cachedMinimizedScopeListRaw = rawValue;
  cachedMinimizedScopeListValue = entries;
  window.sessionStorage.setItem(MINIMIZED_SCOPE_LIST_STORAGE_KEY, rawValue);
  emit();
}

export function readIsolatedChatRoomSessionScope(sessionId: string | null | undefined) {
  const normalizedSessionId = String(sessionId ?? '').trim();

  if (!normalizedSessionId) {
    return null;
  }

  return normalizeIsolatedChatRoomScope(readSessionScopeMap()[normalizedSessionId] ?? null);
}

export function writeIsolatedChatRoomSessionScope(sessionId: string, scope: IsolatedChatRoomScope | null) {
  const normalizedSessionId = sessionId.trim();

  if (!normalizedSessionId) {
    return;
  }

  const current = readSessionScopeMap();

  if (!scope) {
    if (!current[normalizedSessionId]) {
      return;
    }

    delete current[normalizedSessionId];
    writeSessionScopeMap(current);
    return;
  }

  const normalizedScope = normalizeIsolatedChatRoomScope(scope);

  if (!normalizedScope) {
    if (!current[normalizedSessionId]) {
      return;
    }

    delete current[normalizedSessionId];
    writeSessionScopeMap(current);
    return;
  }

  const previousScope = normalizeIsolatedChatRoomScope(current[normalizedSessionId] ?? null);

  if (previousScope && doesIsolatedChatRoomScopeMatch(previousScope, normalizedScope)) {
    current[normalizedSessionId] = previousScope;
    return;
  }

  current[normalizedSessionId] = normalizedScope;
  writeSessionScopeMap(current);
}

export function pruneIsolatedChatRoomSessionScopes(validSessionIds: string[]) {
  if (typeof window === 'undefined') {
    return;
  }

  const validSessionIdSet = new Set(validSessionIds.map((item) => String(item).trim()).filter(Boolean));
  const current = readSessionScopeMap();
  let didChange = false;

  Object.keys(current).forEach((sessionId) => {
    if (validSessionIdSet.has(sessionId)) {
      return;
    }

    delete current[sessionId];
    didChange = true;
  });

  if (didChange) {
    writeSessionScopeMap(current);
  }
}

export function readMinimizedIsolatedChatRoomEntries() {
  return readMinimizedScopeList();
}

export function upsertMinimizedIsolatedChatRoomEntry(scope: IsolatedChatRoomScope | null | undefined) {
  const normalizedScope = normalizeIsolatedChatRoomScope(scope);

  if (!normalizedScope) {
    return null;
  }

  const current = readMinimizedScopeList();
  const existing = current.find((item) => doesIsolatedChatRoomScopeMatch(item.scope, normalizedScope)) ?? null;
  const nextEntry: MinimizedIsolatedChatRoomEntry = {
    id: existing?.id ?? createMinimizedEntryId(),
    scope: normalizedScope,
    minimizedAt: new Date().toISOString(),
  };
  const nextEntries = [nextEntry, ...current.filter((item) => item.id !== nextEntry.id)];
  writeMinimizedScopeList(nextEntries);
  return nextEntry;
}

export function removeMinimizedIsolatedChatRoomEntry(entryId: string | null | undefined) {
  const normalizedEntryId = String(entryId ?? '').trim();

  if (!normalizedEntryId) {
    return;
  }

  const current = readMinimizedScopeList();
  const nextEntries = current.filter((item) => item.id !== normalizedEntryId);

  if (nextEntries.length === current.length) {
    return;
  }

  writeMinimizedScopeList(nextEntries);
}

export function removeMinimizedIsolatedChatRoomEntryByScope(scope: IsolatedChatRoomScope | null | undefined) {
  const normalizedScope = normalizeIsolatedChatRoomScope(scope);

  if (!normalizedScope) {
    return;
  }

  const current = readMinimizedScopeList();
  const nextEntries = current.filter((item) => !doesIsolatedChatRoomScopeMatch(item.scope, normalizedScope));

  if (nextEntries.length === current.length) {
    return;
  }

  writeMinimizedScopeList(nextEntries);
}

export function useActiveIsolatedChatRoomScope() {
  return useSyncExternalStore(
    (listener) => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
    readActiveIsolatedChatRoomScope,
    () => null,
  );
}

export function useIsolatedChatRoomsWindowOpen() {
  return useSyncExternalStore(
    (listener) => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
    readIsolatedChatRoomsWindowOpen,
    () => false,
  );
}

export function useMinimizedIsolatedChatRoomEntries() {
  return useSyncExternalStore(
    (listener) => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
    readMinimizedIsolatedChatRoomEntries,
    () => [],
  );
}
