import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { act } from 'react-dom/test-utils';
import { EditorConfigProvider } from '../state/EditorConfigContext';
import { PageProvider, usePages } from '../state/PageContext';
import { SiteDesignProvider } from '../state/SiteDesignContext';
import { useWhpApi } from './useWhpApi';
import { WhpConfig } from '../types';

/**
 * PKG-H §5 round-trip coverage: `load()` must restore per-page `seo`
 * (PageSeo) from `proj.pages_craft_state[].seo` back onto the reconstructed
 * `PageData`, exactly like it already restores `craftState`. Mocks
 * `@craftjs/core`'s `useEditor` (same pattern as
 * `PageContext.pure-updaters.test.tsx`) since this test only needs
 * `query.serialize`/`actions.deserialize` as inert stubs -- it drives
 * `load()`, not the live canvas.
 */
const deserializeMock = vi.fn();
vi.mock('@craftjs/core', () => ({
  useEditor: () => ({
    query: { serialize: () => '{}' },
    actions: { deserialize: deserializeMock },
  }),
}));

const whpConfig: WhpConfig = {
  user: 'testuser',
  apiUrl: '/panel/api/site-builder',
  csrfToken: 'tok',
  siteId: 42,
  siteDomain: 'example.com',
  siteName: 'Test Site',
  backUrl: '/panel/sites',
  isRoot: false,
};

let container: HTMLDivElement;
let root: Root;

interface Captured {
  load: ReturnType<typeof useWhpApi>['load'];
  pages: ReturnType<typeof usePages>['pages'];
}

function render(): { get: () => Captured } {
  container = document.createElement('div');
  document.body.appendChild(container);
  let captured: Captured | null = null;

  const Consumer: React.FC = () => {
    const { load } = useWhpApi();
    const { pages } = usePages();
    captured = { load, pages };
    return null;
  };

  act(() => {
    root = createRoot(container);
    root.render(
      <EditorConfigProvider config={whpConfig}>
        <SiteDesignProvider>
          <PageProvider>
            <Consumer />
          </PageProvider>
        </SiteDesignProvider>
      </EditorConfigProvider>,
    );
  });

  return { get: () => captured! };
}

function unmount() {
  act(() => {
    root.unmount();
  });
  container.remove();
}

describe('useWhpApi load() restores PageData.seo (PKG-H §5)', () => {
  beforeEach(() => {
    deserializeMock.mockClear();
  });

  afterEach(() => {
    vi.unstubAllGlobals();
  });

  test('a saved project with per-page seo restores seo onto the reconstructed PageData', async () => {
    const seoPayload = {
      metaTitle: 'Custom Title',
      metaDescription: 'A custom description.',
      ogTitle: 'Custom OG Title',
      ogImage: '/uploads/og.jpg',
      twitterCard: 'summary_large_image' as const,
      noindex: true,
    };

    const fetchMock = vi.fn().mockResolvedValue({
      json: async () => ({
        success: true,
        project: {
          design: null,
          header_craft_state: null,
          footer_craft_state: null,
          pages_craft_state: [
            { id: 'home', name: 'Home', slug: 'index', craftState: '{"ROOT":{}}', seo: seoPayload },
            { id: 'page_2', name: 'About', slug: 'about', craftState: '{"ROOT":{}}' },
          ],
        },
      }),
    });
    vi.stubGlobal('fetch', fetchMock);

    const harness = render();

    await act(async () => {
      await harness.get().load();
    });

    const { pages } = harness.get();
    expect(pages.find((p) => p.id === 'home')?.seo).toEqual(seoPayload);
    // A page with no seo in the payload stays undefined -- back-compat, not
    // coerced into an empty object.
    expect(pages.find((p) => p.id === 'page_2')?.seo).toBeUndefined();

    unmount();
  });

  test('a legacy project with no seo on any page loads without adding seo fields', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      json: async () => ({
        success: true,
        project: {
          design: null,
          header_craft_state: null,
          footer_craft_state: null,
          pages_craft_state: [
            { id: 'home', name: 'Home', slug: 'index', craftState: '{"ROOT":{}}' },
          ],
        },
      }),
    });
    vi.stubGlobal('fetch', fetchMock);

    const harness = render();

    await act(async () => {
      await harness.get().load();
    });

    const { pages } = harness.get();
    expect(pages.find((p) => p.id === 'home')?.seo).toBeUndefined();

    unmount();
  });
});
