import { describe, test, expect } from 'vitest';
import React from 'react';
import { createRoot, Root } from 'react-dom/client';
import { act } from 'react-dom/test-utils';
import { Editor, useEditor } from '@craftjs/core';
import { componentResolver } from '../../components/resolver';
import { allTemplates } from '../../templates';
import { buildNodeTree } from '../../utils/craft-tree';
import { templateComponentToTreeNode } from '../../templates/apply-template';
import { exportBodyHtml } from '../../utils/html-export';
import { DEFAULT_HEADER_STATE } from '../../state/PageContext';

/**
 * Regression test for: loading a template makes the header/footer ZONE
 * PREVIEW (`ZonePreview` in `editor/Canvas.tsx`, which calls
 * `exportBodyHtml(craftState)`) render as an empty strip -- no nav links, no
 * logo, no footer text -- and (a second bug found while fixing the first)
 * makes a template page's `Section`-wrapped content crash the live editor
 * entirely.
 *
 * This drives the EXACT same path `TemplateModal.tsx`'s `applyZone` /
 * `addTemplateComponents` uses to load a template's header/footer/page
 * content onto the live Craft.js canvas (`buildNodeTree` +
 * `actions.addNodeTree(tree, 'ROOT')`), then serializes it
 * (`query.serialize()`, exactly what `PageContext`'s `switchPage` /
 * `saveCurrentState` do right after `TemplateModal` finishes applying a
 * zone/page) and feeds the result through `exportBodyHtml` -- the same
 * function `ZonePreview` calls, and the same function `handlePublish` uses
 * to compose the published header/footer HTML. So this also covers whether
 * published output is affected (it was).
 */
let api: any;
function Harness() {
  const { actions, query } = useEditor();
  api = { actions, query };
  return null;
}

function mount() {
  const container = document.createElement('div');
  document.body.appendChild(container);
  let root!: Root;
  act(() => {
    root = createRoot(container);
    root.render(
      <Editor resolver={componentResolver}>
        <Harness />
      </Editor>,
    );
  });
  return {
    container,
    unmount: () => {
      act(() => {
        root.unmount();
      });
      container.remove();
    },
  };
}

/** Mirrors TemplateModal.tsx's addTemplateComponents(). */
function addTemplateComponents(components: any[]) {
  for (const comp of components) {
    const tree = buildNodeTree(api.query, templateComponentToTreeNode(comp));
    api.actions.addNodeTree(tree, 'ROOT');
  }
}

const EMPTY_HEADER_ROOT =
  '{"ROOT":{"type":{"resolvedName":"Container"},"isCanvas":true,"props":{"style":{},"tag":"header"},"displayName":"Container","custom":{},"hidden":false,"nodes":[],"linkedNodes":{}}}';

const EMPTY_CANVAS_ROOT =
  '{"ROOT":{"type":{"resolvedName":"Container"},"isCanvas":true,"props":{"style":{},"tag":"div"},"displayName":"Container","custom":{},"hidden":false,"nodes":[],"linkedNodes":{}}}';

describe('template header/footer zone export (loading a template)', () => {
  test('sanity check: the DEFAULT header (fresh project, no template) exports its content', () => {
    const { html } = exportBodyHtml(DEFAULT_HEADER_STATE);
    expect(html).toContain('MySite');
  });

  test('a template header (SaaS Landing) exports its real nav/logo content -- not an empty strip', () => {
    const tpl = allTemplates.find((t) => t.id === 'saas-landing');
    expect(tpl).toBeDefined();
    expect(tpl!.header.components.length).toBeGreaterThan(0);

    const { unmount } = mount();
    act(() => {
      api.actions.deserialize(EMPTY_HEADER_ROOT);
    });
    act(() => {
      addTemplateComponents(tpl!.header.components);
    });

    const serialized = api.query.serialize();
    const { html } = exportBodyHtml(serialized);

    // The template's logo text and a real nav link -- if these are missing,
    // the header rendered as an empty strip (the reported bug).
    expect(html).toContain('FlowStack');
    expect(html).toContain('Features');
    expect(html).toContain('Start Free');

    unmount();
  });

  test('a template footer (SaaS Landing) exports its real copyright/tagline content -- not an empty strip', () => {
    const tpl = allTemplates.find((t) => t.id === 'saas-landing');
    expect(tpl).toBeDefined();
    expect(tpl!.footer.components.length).toBeGreaterThan(0);

    const { unmount } = mount();
    act(() => {
      api.actions.deserialize(
        '{"ROOT":{"type":{"resolvedName":"Container"},"isCanvas":true,"props":{"style":{},"tag":"footer"},"displayName":"Container","custom":{},"hidden":false,"nodes":[],"linkedNodes":{}}}',
      );
    });
    act(() => {
      addTemplateComponents(tpl!.footer.components);
    });

    const serialized = api.query.serialize();
    const { html } = exportBodyHtml(serialized);

    expect(html).toContain('FlowStack, Inc.');
    expect(html).toContain('Ship products faster');

    unmount();
  });

  test('a template page Section with nested children (SaaS Landing Home) builds + exports without crashing', () => {
    // Regression for the SECOND bug surfaced while fixing the first: naively
    // rebuilding nested children via React.createElement + parseReactElement
    // crashes any component with an internal SHELL_INNER linked canvas
    // (Section/BackgroundSection/FormContainer) or linked columns
    // (ColumnLayout). The SaaS Landing home page's second component is a
    // `Section` wrapping a `Heading` ("Trusted by 10,000+ development
    // teams...") -- exactly this shape.
    const tpl = allTemplates.find((t) => t.id === 'saas-landing');
    const homePage = tpl!.pages[0];
    const sectionComp = homePage.content.components.find((c) => c.type === 'Section');
    expect(sectionComp).toBeDefined();
    expect(sectionComp!.children?.length).toBeGreaterThan(0);

    const { unmount } = mount();
    act(() => {
      api.actions.deserialize(EMPTY_CANVAS_ROOT);
    });

    expect(() => {
      act(() => {
        addTemplateComponents([sectionComp!]);
      });
    }).not.toThrow();

    const serialized = api.query.serialize();
    const { html } = exportBodyHtml(serialized);
    expect(html).toContain('Trusted by 10,000+ development teams');

    unmount();
  });
});
