import { DOCUMENT } from '@angular/common';
import { Injectable, computed, effect, inject, signal } from '@angular/core';

/**
 * Shell-level layout state shared by the header and the sidebar.
 *
 * v1 surfaces two settings:
 *   - `sidebarCollapsed`: whether the sidebar is collapsed to its
 *     icon-only rail. The header reads it to size its logo zone in
 *     lock-step with the sidebar.
 *   - `themeMode`: the user's color-scheme preference among
 *     `light` / `dark` / `auto`. `effectiveTheme` resolves it to a
 *     concrete `'light' | 'dark'` by consulting the system preference
 *     when in `auto` mode, and reacts to OS-level theme changes.
 *     A side-effect toggles the `.dark` class on <html> so every
 *     `dark:` Tailwind utility flips at once.
 *
 * Both pieces of state persist in `localStorage` so the user's
 * preferences survive a reload without a backend round-trip.
 *
 * Lives outside of the components that consume it so multiple
 * surfaces stay synchronised without prop-drilling, and so the
 * service is the natural home for future cross-cutting shell state
 * (density, RTL, panel pinning, ...).
 */

const SIDEBAR_STORAGE_KEY = 'portal-shell:sidebar-collapsed';
const THEME_STORAGE_KEY = 'portal-shell:theme-mode';
const DARK_CLASS = 'dark';

export type ThemeMode = 'light' | 'dark' | 'auto';

@Injectable({ providedIn: 'root' })
export class LayoutStateService {
  private readonly document = inject(DOCUMENT);

  private readonly _sidebarCollapsed = signal(readPersistedSidebarCollapsed());
  private readonly _themeMode = signal<ThemeMode>(readPersistedThemeMode());
  private readonly _systemPrefersDark = signal(systemPrefersDark(this.document));

  readonly sidebarCollapsed = this._sidebarCollapsed.asReadonly();
  readonly themeMode = this._themeMode.asReadonly();
  readonly effectiveTheme = computed<'light' | 'dark'>(() => {
    const mode = this._themeMode();
    if (mode === 'auto') {
      return this._systemPrefersDark() ? 'dark' : 'light';
    }
    return mode;
  });

  constructor() {
    effect(() => {
      const value = this._sidebarCollapsed();
      try {
        localStorage.setItem(SIDEBAR_STORAGE_KEY, value ? '1' : '0');
      } catch {
        // localStorage can throw in private-mode contexts; the UI
        // still works, the preference just doesn't persist.
      }
    });

    effect(() => {
      const mode = this._themeMode();
      try {
        localStorage.setItem(THEME_STORAGE_KEY, mode);
      } catch {
        // See above.
      }
    });

    effect(() => {
      const theme = this.effectiveTheme();
      const root = this.document.documentElement;
      if (theme === 'dark') {
        root.classList.add(DARK_CLASS);
      } else {
        root.classList.remove(DARK_CLASS);
      }
    });

    this.watchSystemPreference();
  }

  toggleSidebar(): void {
    this._sidebarCollapsed.update((v) => !v);
  }

  setSidebarCollapsed(value: boolean): void {
    this._sidebarCollapsed.set(value);
  }

  setThemeMode(mode: ThemeMode): void {
    this._themeMode.set(mode);
  }

  private watchSystemPreference(): void {
    const mql = this.document.defaultView?.matchMedia?.('(prefers-color-scheme: dark)');
    if (!mql) {
      return;
    }
    const listener = (e: MediaQueryListEvent) => this._systemPrefersDark.set(e.matches);
    // `addEventListener` is the modern API; older Safari only exposes
    // the deprecated `addListener` — guarded for robustness.
    if (typeof mql.addEventListener === 'function') {
      mql.addEventListener('change', listener);
    } else {
      mql.addListener(listener);
    }
  }
}

function readPersistedSidebarCollapsed(): boolean {
  try {
    return localStorage.getItem(SIDEBAR_STORAGE_KEY) === '1';
  } catch {
    return false;
  }
}

function readPersistedThemeMode(): ThemeMode {
  try {
    const raw = localStorage.getItem(THEME_STORAGE_KEY);
    if (raw === 'light' || raw === 'dark' || raw === 'auto') {
      return raw;
    }
  } catch {
    // fall through
  }
  return 'auto';
}

function systemPrefersDark(doc: Document): boolean {
  return doc.defaultView?.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false;
}
