import type {
  AgentStudioLayoutSnapshot,
  AgentStudioNodeView,
  AgentStudioViewport,
} from '../../components/agent-studio/types';
import type {AgentDraftState} from '../../types';

import {computed, onBeforeUnmount, reactive, toRaw, watch} from 'vue';

const STORAGE_PREFIX = 'agent-studio-layout';
const WRITE_DELAY = 260;

function readLayout(key: string): AgentStudioLayoutSnapshot {
  if (typeof window === 'undefined') {
    return { nodePositions: {} };
  }
  try {
    const raw = window.localStorage.getItem(key);
    if (!raw) return { nodePositions: {} };
    const parsed = JSON.parse(raw) as AgentStudioLayoutSnapshot;
    return {
      nodePositions: parsed.nodePositions || {},
      viewport: parsed.viewport,
    };
  } catch {
    return { nodePositions: {} };
  }
}

function isFinitePosition(
  position: unknown,
): position is { x: number; y: number } {
  return (
    typeof position === 'object' &&
    position !== null &&
    Number.isFinite((position as { x?: number }).x) &&
    Number.isFinite((position as { y?: number }).y)
  );
}

function isFiniteViewport(viewport: unknown): viewport is AgentStudioViewport {
  return (
    isFinitePosition(viewport) &&
    Number.isFinite((viewport as { zoom?: number }).zoom)
  );
}

export function useAgentStudioLayout(state: AgentDraftState) {
  const storageKey = computed(() => {
    const agentId = state.agent.id ? String(state.agent.id) : 'new';
    return `${STORAGE_PREFIX}:${agentId}`;
  });
  const snapshot = reactive<AgentStudioLayoutSnapshot>({
    nodePositions: {},
  });
  let writeTimer: number | undefined;

  function load() {
    const next = readLayout(storageKey.value);
    snapshot.nodePositions = { ...next.nodePositions };
    snapshot.viewport = next.viewport;
  }

  function hasCapturedLayout() {
    return (
      Object.keys(snapshot.nodePositions).length > 0 ||
      isFiniteViewport(snapshot.viewport)
    );
  }

  function persistSnapshot() {
    if (typeof window === 'undefined') return;
    window.localStorage.setItem(
      storageKey.value,
      JSON.stringify(toRaw(snapshot)),
    );
  }

  function scheduleWrite() {
    if (typeof window === 'undefined') return;
    if (writeTimer) {
      window.clearTimeout(writeTimer);
    }
    writeTimer = window.setTimeout(() => {
      persistSnapshot();
      writeTimer = undefined;
    }, WRITE_DELAY);
  }

  function capture(data: {
    nodes?: AgentStudioNodeView[];
    viewport?: AgentStudioViewport;
  }) {
    let changed = false;
    if (Array.isArray(data.nodes)) {
      const activeIds = new Set(data.nodes.map((node) => node.id));
      const nextPositions: AgentStudioLayoutSnapshot['nodePositions'] = {};
      for (const [key, position] of Object.entries(snapshot.nodePositions)) {
        if (activeIds.has(key)) nextPositions[key] = position;
        else changed = true;
      }
      data.nodes.forEach((node) => {
        if (isFinitePosition(node.position)) {
          const current = nextPositions[node.id];
          if (
            !current ||
            current.x !== node.position.x ||
            current.y !== node.position.y
          ) {
            nextPositions[node.id] = { ...node.position };
            changed = true;
          }
        }
      });
      snapshot.nodePositions = nextPositions;
    }
    if (
      isFiniteViewport(data.viewport) &&
      (!snapshot.viewport ||
        snapshot.viewport.x !== data.viewport.x ||
        snapshot.viewport.y !== data.viewport.y ||
        snapshot.viewport.zoom !== data.viewport.zoom)
    ) {
      snapshot.viewport = { ...data.viewport };
      changed = true;
    }
    if (changed) {
      scheduleWrite();
    }
  }

  watch(
    storageKey,
    (nextKey, previousKey) => {
      const persisted = readLayout(nextKey);
      const shouldCarryNewDraftLayout =
        previousKey?.endsWith(':new') &&
        !persisted.viewport &&
        Object.keys(persisted.nodePositions).length === 0 &&
        hasCapturedLayout();
      if (shouldCarryNewDraftLayout) {
        persistSnapshot();
        return;
      }
      load();
    },
    { immediate: true },
  );

  onBeforeUnmount(() => {
    if (writeTimer && typeof window !== 'undefined') {
      window.clearTimeout(writeTimer);
      persistSnapshot();
    }
  });

  return {
    capture,
    layout: snapshot,
  };
}
