'use client';

import { useCallback, useEffect, useRef, useState } from 'react';

// Legacy localStorage key. Read once on mount and migrated to the server, then
// cleared. Kept here so users with existing client-side state don't lose work.
const LEGACY_STORAGE_KEY = 'madarx:spaces-v1';
const DEFAULT_SPACE = { id: 'default', name: 'main', is_active: true };
const SWITCH_DURATION_MS = 220;

function readLegacyLocalStorage() {
  if (typeof window === 'undefined') return null;
  try {
    const raw = localStorage.getItem(LEGACY_STORAGE_KEY);
    if (!raw) return null;
    const data = JSON.parse(raw);
    if (!data || !Array.isArray(data.spaces) || data.spaces.length === 0) return null;
    const spaces = data.spaces
      .filter((s) => s && typeof s.id === 'string')
      .map((s) => ({
        id: String(s.id),
        name: String(s.name || 'Space'),
        artifactIds: Array.isArray(s.artifactIds) ? s.artifactIds.map(String) : [],
      }));
    if (spaces.length === 0) return null;
    const activeSpaceId = spaces.some((s) => s.id === data.activeSpaceId)
      ? data.activeSpaceId
      : spaces[0].id;
    return { spaces, activeSpaceId };
  } catch {
    return null;
  }
}

function clearLegacyLocalStorage() {
  if (typeof window === 'undefined') return;
  try { localStorage.removeItem(LEGACY_STORAGE_KEY); } catch {}
}

function normalizeServerSpaces(payload) {
  if (!payload || !Array.isArray(payload.spaces) || payload.spaces.length === 0) {
    return { spaces: [DEFAULT_SPACE], activeSpaceId: DEFAULT_SPACE.id };
  }
  const spaces = payload.spaces.map((s) => ({
    id: String(s.id),
    name: String(s.name || 'Space'),
    is_active: !!s.is_active,
  }));
  const activeSpaceId = payload.active_space_id && spaces.some((s) => s.id === payload.active_space_id)
    ? String(payload.active_space_id)
    : spaces[0].id;
  return { spaces, activeSpaceId };
}

/**
 * Dashboard spaces hook. Owns space metadata + active selection only.
 *
 * Per-space artifact membership is derived from `artifact.space_id` in the
 * caller (use-dashboard-state). This hook does NOT track artifact IDs and does
 * NOT reconcile orphans — the server is the single source of truth.
 *
 * @param {Object} opts
 * @param {(artifactId: string, spaceId: string) => void} [opts.onArtifactMoved]
 *   Optional optimistic-update callback. Fires before the POST so the UI does
 *   not flicker through a stale-snapshot intermediate state.
 * @param {() => void} [opts.refresh]
 *   Optional callback to re-fetch dashboard state after a move/delete settles.
 */
export function useSpaces({ onArtifactMoved, refresh } = {}) {
  const [spaces, setSpaces] = useState([DEFAULT_SPACE]);
  const [activeSpaceId, setActiveSpaceIdRaw] = useState(DEFAULT_SPACE.id);
  const [isSwitching, setIsSwitching] = useState(false);
  const [ready, setReady] = useState(false);
  const switchTimerRef = useRef(null);
  const onArtifactMovedRef = useRef(onArtifactMoved);
  const refreshRef = useRef(refresh);
  onArtifactMovedRef.current = onArtifactMoved;
  refreshRef.current = refresh;

  useEffect(() => () => {
    if (switchTimerRef.current) clearTimeout(switchTimerRef.current);
  }, []);

  // ── Hydrate from server, with one-shot localStorage migration ────────────
  useEffect(() => {
    let cancelled = false;

    async function hydrate() {
      try {
        const initial = await fetch('/api/dashboard/spaces', { cache: 'no-store' })
          .then((r) => r.json())
          .catch(() => null);

        // First-run migration: if server returns just the seeded `default` and
        // the browser has prior localStorage, push it to the server then re-read.
        const onlyDefault =
          initial &&
          Array.isArray(initial.spaces) &&
          initial.spaces.length === 1 &&
          initial.spaces[0]?.id === 'default' &&
          (initial.spaces[0]?.artifact_ids?.length || 0) === 0;
        const legacy = onlyDefault ? readLegacyLocalStorage() : null;
        let payload = initial;
        if (legacy) {
          const imported = await fetch('/api/dashboard/spaces/import', {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify({
              spaces: legacy.spaces,
              active_space_id: legacy.activeSpaceId,
            }),
          })
            .then((r) => r.json())
            .catch(() => null);
          if (imported && imported.imported) {
            payload = imported;
            clearLegacyLocalStorage();
          }
        }

        if (cancelled) return;
        const normalized = normalizeServerSpaces(payload);
        setSpaces(normalized.spaces);
        setActiveSpaceIdRaw(normalized.activeSpaceId);
      } catch {
        if (cancelled) return;
        setSpaces([DEFAULT_SPACE]);
        setActiveSpaceIdRaw(DEFAULT_SPACE.id);
      } finally {
        if (!cancelled) {
          setReady(true);
        }
      }
    }

    hydrate();
    return () => { cancelled = true; };
  }, []);

  // ── Smooth space-switch helper ───────────────────────────────────────────
  const commitActive = useCallback((nextId) => {
    setActiveSpaceIdRaw(nextId);
    fetch(`/api/dashboard/spaces/${encodeURIComponent(nextId)}`, {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ is_active: true }),
    }).catch(() => {});
  }, []);

  const setActiveSpaceId = useCallback((nextId) => {
    if (nextId === activeSpaceId || !spaces.some((s) => s.id === nextId)) return;
    if (switchTimerRef.current) {
      clearTimeout(switchTimerRef.current);
      switchTimerRef.current = null;
    }
    setIsSwitching(true);
    switchTimerRef.current = setTimeout(() => {
      commitActive(nextId);
      switchTimerRef.current = setTimeout(() => {
        setIsSwitching(false);
        switchTimerRef.current = null;
      }, 20);
    }, Math.floor(SWITCH_DURATION_MS / 2));
  }, [activeSpaceId, spaces, commitActive]);

  const createSpace = useCallback(async () => {
    const provisionalName = `Space ${spaces.length + 1}`;
    try {
      const resp = await fetch('/api/dashboard/spaces', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ name: provisionalName }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok || !data?.space) return null;
      const space = { id: String(data.space.id), name: data.space.name, is_active: false };
      setSpaces((prev) => [...prev, space]);
      if (switchTimerRef.current) {
        clearTimeout(switchTimerRef.current);
        switchTimerRef.current = null;
      }
      setIsSwitching(true);
      switchTimerRef.current = setTimeout(() => {
        commitActive(space.id);
        switchTimerRef.current = setTimeout(() => {
          setIsSwitching(false);
          switchTimerRef.current = null;
        }, 20);
      }, Math.floor(SWITCH_DURATION_MS / 2));
      return space.id;
    } catch {
      return null;
    }
  }, [spaces.length, commitActive]);

  const renameSpace = useCallback((id, name) => {
    const trimmed = String(name || '').trim().slice(0, 32);
    if (!trimmed) return;
    setSpaces((prev) => prev.map((s) => (s.id === id ? { ...s, name: trimmed } : s)));
    fetch(`/api/dashboard/spaces/${encodeURIComponent(id)}`, {
      method: 'PATCH',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ name: trimmed }),
    }).catch(() => {});
  }, []);

  const deleteSpace = useCallback((id) => {
    let nextActive = null;
    setSpaces((prev) => {
      if (prev.length <= 1) return prev;
      const remaining = prev.filter((s) => s.id !== id);
      if (remaining.length === prev.length) return prev;
      nextActive = remaining[0]?.id || null;
      return remaining;
    });
    fetch(`/api/dashboard/spaces/${encodeURIComponent(id)}`, { method: 'DELETE' })
      .then(() => refreshRef.current?.())
      .catch(() => {});
    if (activeSpaceId === id && nextActive) {
      setActiveSpaceId(nextActive);
    }
  }, [activeSpaceId, setActiveSpaceId]);

  const moveArtifactToSpace = useCallback((artifactId, targetSpaceId) => {
    if (!spaces.some((s) => s.id === targetSpaceId)) return;
    // Optimistic: caller patches state.artifacts[id].space_id locally so the
    // UI moves immediately. Server POST persists the new binding.
    onArtifactMovedRef.current?.(artifactId, targetSpaceId);
    fetch(`/api/dashboard/spaces/${encodeURIComponent(targetSpaceId)}/move`, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ artifact_id: artifactId }),
    })
      .then(() => refreshRef.current?.())
      .catch(() => {});
  }, [spaces]);

  const activeSpace = spaces.find((s) => s.id === activeSpaceId) || spaces[0];

  return {
    spaces,
    activeSpaceId,
    activeSpace,
    isSwitching,
    ready,
    setActiveSpaceId,
    createSpace,
    renameSpace,
    deleteSpace,
    moveArtifactToSpace,
  };
}
