// @vitest-environment jsdom
import { act } from 'react';
import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import { PageSnapshotProvider } from '../provider';
import {
  usePageSnapshot,
  type PageSnapshotContextValue,
} from '../use-page-snapshot';

// React 19 act() environment flag.
(globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;

let container: HTMLDivElement;
let root: Root;
/** Latest context value, captured by the probe component each render. */
let ctx: PageSnapshotContextValue | null = null;

/** Probe component — publishes the context value to the test. */
function Probe() {
  ctx = usePageSnapshot();
  return null;
}

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
  root = createRoot(container);
});

afterEach(() => {
  act(() => root.unmount());
  container.remove();
  ctx = null;
});

/** Render the provider with the probe inside; flushes effects. */
async function render(defaultLinked = false) {
  await act(async () => {
    root.render(
      createElement(PageSnapshotProvider, {
        defaultLinked,
        children: createElement(Probe),
      }),
    );
  });
}

describe('PageSnapshotProvider / usePageSnapshot', () => {
  it('defaults to not linked', async () => {
    await render();
    expect(ctx!.isLinked).toBe(false);
    expect(ctx!.lastSnapshot).toBeNull();
  });

  it('capture() returns null while not linked', async () => {
    await render();
    let result: unknown;
    act(() => {
      result = ctx!.capture();
    });
    expect(result).toBeNull();
  });

  it('getChatMetadata() returns undefined while not linked', async () => {
    await render();
    expect(ctx!.getChatMetadata()).toBeUndefined();
  });

  it('generatePreview() works even when not linked (preview gate)', async () => {
    document.body.innerHTML += '<main><h1>Preview Page</h1></main>';
    await render();
    let preview: ReturnType<PageSnapshotContextValue['generatePreview']>;
    act(() => {
      preview = ctx!.generatePreview();
    });
    expect(preview!).not.toBeNull();
    expect(preview!.payload.snapshot.type).toBe('root');
  });

  it('opting in captures an initial snapshot', async () => {
    document.body.innerHTML += '<main><h1>Linked Page</h1></main>';
    await render();
    await act(async () => {
      ctx!.setIsLinked(true);
    });
    expect(ctx!.isLinked).toBe(true);
    expect(ctx!.lastSnapshot).not.toBeNull();
  });

  it('getChatMetadata() returns { pageContext } when linked', async () => {
    document.body.innerHTML += '<main><h1>Meta Page</h1></main>';
    await render(true);
    let meta: Record<string, unknown> | undefined;
    await act(async () => {
      meta = ctx!.getChatMetadata();
    });
    expect(meta).toBeDefined();
    expect(meta).toHaveProperty('pageContext');
  });
});
