import { describe, test, expect, afterEach } from 'vitest';
import { renderEditorHarness, EditorHarness } from '../editorHarness';
import { allTemplates } from '../../templates';
import { buildNodeTree } from '../../utils/craft-tree';
import { templateComponentToTreeNode } from '../../templates/apply-template';
import { exportBodyHtml } from '../../utils/html-export';

/**
 * Real-`@craftjs/core` integration coverage for loading a template.
 *
 * Historical bug: `TemplateModal.addTemplateComponents` used to build each
 * template component via `React.createElement(Component, comp.props)` with
 * NO children argument, silently dropping every nested `children` array
 * authored in `templates/definitions.ts` (e.g. `makeHeader()`'s
 * `Container > Logo + Menu`, `makeFooterContent()`'s `Container > TextBlock`,
 * or any page `Section` wrapping a `Heading`/`ColumnLayout`). The resulting
 * Craft.js node had `nodes: []`, so both the editor's own header/footer zone
 * preview AND the published HTML (same `exportBodyHtml` path) rendered as an
 * empty strip instead of the real nav/footer/section content. The mocked
 * unit test for `templateComponentToTreeNode` (`apply-template.test.ts`)
 * tests the pure conversion function in isolation and would pass either way
 * -- it never drove the result through a real Craft.js tree/render, so it
 * couldn't have caught `TemplateModal` itself dropping children at the
 * call site.
 *
 * This suite drives the EXACT same path `TemplateModal.tsx`'s
 * `addTemplateComponents` uses against a REAL Craft.js editor mounted with a
 * REAL `<Frame>` (via `editorHarness.tsx`) -- so it also exercises real
 * component rendering (Section/Container SHELL_INNER canvases, ColumnLayout
 * linked columns), not just tree-shape assertions.
 */

const tpl = allTemplates.find((t) => t.id === 'saas-landing')!;

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

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

const EMPTY_FOOTER_ROOT = JSON.stringify({
  ROOT: {
    type: { resolvedName: 'Container' },
    isCanvas: true,
    props: { style: {}, tag: 'footer' },
    displayName: 'Container',
    custom: {},
    hidden: false,
    nodes: [],
    linkedNodes: {},
  },
});

let harness: EditorHarness | null = null;

afterEach(() => {
  if (harness) {
    harness.unmount();
    harness = null;
  }
});

describe('template load (real @craftjs/core editor + real <Frame> render)', () => {
  test('sanity: the SaaS Landing template actually has nested header/footer/page content to lose', () => {
    expect(tpl).toBeDefined();
    expect(tpl.header.components.length).toBeGreaterThan(0);
    expect(tpl.footer.components.length).toBeGreaterThan(0);
    expect(tpl.pages[0].content.components.length).toBeGreaterThan(0);
    const hasNestedChildren = tpl.header.components.some((c: any) => (c.children?.length ?? 0) > 0);
    expect(hasNestedChildren).toBe(true);
  });

  test('loading the header keeps its nested nav links + logo -- both in the real DOM and in export', () => {
    harness = renderEditorHarness({ initialState: EMPTY_HEADER_ROOT, frameTag: 'header' });

    expect(() => {
      harness!.act(() => addTemplateComponents(harness!, tpl.header.components));
    }).not.toThrow();

    // Real rendered DOM -- not just serialized state.
    expect(harness.container.textContent).toContain('FlowStack');
    expect(harness.container.textContent).toContain('Features');
    expect(harness.container.textContent).toContain('Start Free');

    const { html } = exportBodyHtml(harness.getSerialized());
    expect(html).toContain('FlowStack');
    expect(html).toContain('Features');
    expect(html).toContain('Start Free');
  });

  test('loading the footer keeps its nested copyright/tagline content -- both in the real DOM and in export', () => {
    harness = renderEditorHarness({ initialState: EMPTY_FOOTER_ROOT, frameTag: 'footer' });

    expect(() => {
      harness!.act(() => addTemplateComponents(harness!, tpl.footer.components));
    }).not.toThrow();

    expect(harness.container.textContent).toContain('FlowStack, Inc.');
    expect(harness.container.textContent).toContain('Ship products faster');

    const { html } = exportBodyHtml(harness.getSerialized());
    expect(html).toContain('FlowStack, Inc.');
    expect(html).toContain('Ship products faster');
  });

  test('loading the home page keeps a Section-wrapped Heading\'s nested content -- both in the real DOM and in export', () => {
    const homePage = tpl.pages[0];
    const sectionComp = homePage.content.components.find((c: any) => c.type === 'Section');
    expect(sectionComp).toBeDefined();
    expect(sectionComp!.children?.length).toBeGreaterThan(0);

    harness = renderEditorHarness(); // default empty 'div' root, like a regular page

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

    expect(harness.container.textContent).toContain('Trusted by 10,000+ development teams');

    const { html } = exportBodyHtml(harness.getSerialized());
    expect(html).toContain('Trusted by 10,000+ development teams');
  });

  test('loading the FULL home page (all components) keeps every top-level node\'s content -- no silent drops', () => {
    const homePage = tpl.pages[0];
    harness = renderEditorHarness();

    expect(() => {
      harness!.act(() => addTemplateComponents(harness!, homePage.content.components));
    }).not.toThrow();

    // ROOT should have exactly as many direct children as the template
    // defines top-level page components -- if children got dropped, nested
    // content wouldn't show up in ROOT's descendant count via serialize, but
    // an easier, more direct regression signal is: every top-level
    // component's OWN text ends up rendered somewhere on the canvas.
    const serialized = JSON.parse(harness.getSerialized());
    const totalNodeCount = Object.keys(serialized).length;
    // A flat (children-dropped) tree would have exactly
    // homePage.content.components.length + 1 (ROOT) nodes. The real template
    // has nested children, so a correct load must produce MORE nodes than
    // that -- this is the shape-level assertion the pre-existing mocked test
    // could never make (it never touched a real tree).
    expect(totalNodeCount).toBeGreaterThan(homePage.content.components.length + 1);
  });
});
