import {
  cleanupTransientMapResources,
  disposeMapResources,
  isMapResourcePersistent,
  markMapResourcePersistent,
} from "./mapLifecycle";

const createCollection = <T,>(items: T[]) => ({
  getArray: () => items,
  clear: () => items.splice(0, items.length),
});

const createResource = () => {
  const properties = new Map<string, unknown>();
  return {
    get: (key: string) => properties.get(key),
    set: (key: string, value: unknown) => properties.set(key, value),
    dispose: jest.fn(),
  };
};

describe("map lifecycle", () => {
  it("preserves shared resources and releases session resources", () => {
    const persistentLayer = markMapResourcePersistent(createResource());
    const transientSource = { clear: jest.fn(), dispose: jest.fn() };
    const transientLayer = {
      ...createResource(),
      getSource: () => transientSource,
    };
    const persistentInteraction = markMapResourcePersistent(createResource());
    const transientInteraction = createResource();
    const persistentControl = markMapResourcePersistent(createResource());
    const transientControl = createResource();

    const layers = [persistentLayer, transientLayer];
    const interactions = [persistentInteraction, transientInteraction];
    const controls = [persistentControl, transientControl];
    const overlays: ReturnType<typeof createResource>[] = [];
    const map = {
      getLayers: () => createCollection(layers),
      removeLayer: (resource: unknown) => layers.splice(layers.indexOf(resource as never), 1),
      getInteractions: () => createCollection(interactions),
      removeInteraction: (resource: unknown) =>
        interactions.splice(interactions.indexOf(resource as never), 1),
      getControls: () => createCollection(controls),
      removeControl: (resource: unknown) => controls.splice(controls.indexOf(resource as never), 1),
      getOverlays: () => createCollection(overlays),
      removeOverlay: (resource: unknown) => overlays.splice(overlays.indexOf(resource as never), 1),
    } as any;

    cleanupTransientMapResources(map);

    expect(layers).toEqual([persistentLayer]);
    expect(interactions).toEqual([persistentInteraction]);
    expect(controls).toEqual([persistentControl]);
    expect(transientSource.clear).toHaveBeenCalledTimes(1);
    expect(transientSource.dispose).toHaveBeenCalledTimes(1);
    expect(transientLayer.dispose).toHaveBeenCalledTimes(1);
    expect(transientInteraction.dispose).toHaveBeenCalledTimes(1);
    expect(transientControl.dispose).toHaveBeenCalledTimes(1);
  });

  it("marks only explicitly shared resources as persistent", () => {
    const resource = createResource();
    expect(isMapResourcePersistent(resource)).toBe(false);
    markMapResourcePersistent(resource);
    expect(isMapResourcePersistent(resource)).toBe(true);
  });

  it("keeps memoized layers reusable across Strict Mode effect cleanup", () => {
    const source = { clear: jest.fn(), dispose: jest.fn() };
    const layer = { ...createResource(), getSource: () => source };
    const layers = [layer];
    const interactions: ReturnType<typeof createResource>[] = [];
    const controls: ReturnType<typeof createResource>[] = [];
    const overlays: ReturnType<typeof createResource>[] = [];
    const map = {
      getLayers: () => createCollection(layers),
      removeLayer: (resource: unknown) => layers.splice(layers.indexOf(resource as never), 1),
      getInteractions: () => createCollection(interactions),
      getControls: () => createCollection(controls),
      getOverlays: () => createCollection(overlays),
      setTarget: jest.fn(),
      dispose: jest.fn(),
    } as any;

    disposeMapResources(map, { disposeLayers: false });

    expect(source.clear).toHaveBeenCalledTimes(1);
    expect(source.dispose).not.toHaveBeenCalled();
    expect(layer.dispose).not.toHaveBeenCalled();
    expect(map.dispose).toHaveBeenCalledTimes(1);
  });

  it("detaches a comparison map without clearing shared sources", () => {
    const source = { clear: jest.fn(), dispose: jest.fn() };
    const layer = { ...createResource(), getSource: () => source };
    const layers = [layer];
    const interactions: ReturnType<typeof createResource>[] = [];
    const controls: ReturnType<typeof createResource>[] = [];
    const overlays: ReturnType<typeof createResource>[] = [];
    const map = {
      getLayers: () => createCollection(layers),
      removeLayer: (resource: unknown) => layers.splice(layers.indexOf(resource as never), 1),
      getInteractions: () => createCollection(interactions),
      getControls: () => createCollection(controls),
      getOverlays: () => createCollection(overlays),
      setTarget: jest.fn(),
      dispose: jest.fn(),
    } as any;

    disposeMapResources(map, { disposeSources: false });

    expect(source.clear).not.toHaveBeenCalled();
    expect(source.dispose).not.toHaveBeenCalled();
    expect(layer.dispose).toHaveBeenCalledTimes(1);
    expect(map.dispose).toHaveBeenCalledTimes(1);
  });
});
