/**
 * W5 Step 6 — Base.astro + post/[slug].astro render assertions via AstroContainer.
 * Run: pnpm exec vitest run --config vitest.render.config.ts
 */
import { beforeAll, describe, expect, it, vi } from 'vitest';

const h = vi.hoisted(() => ({
  menuTree: null as Awaited<ReturnType<typeof import('@platform-modules/menus').getMenuTree>>,
}));

vi.mock('@platform-modules/fields', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@platform-modules/fields')>();
  return {
    ...actual,
    getEntityValues: async (_db: unknown, ref: { entityType: string; entityId: string }) => {
      if (ref.entityType !== 'content' || ref.entityId !== 'post-w5-1') return {};
      return { seo_description: 'W5 custom field value' };
    },
  };
});

vi.mock('@platform-modules/menus', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@platform-modules/menus')>();
  return {
    ...actual,
    getMenuTree: async () => h.menuTree,
  };
});

vi.mock('../lib/menus.ts', async (importOriginal) => {
  const actual = await importOriginal<typeof import('../lib/menus.ts')>();
  return {
    ...actual,
    getMenusDb: () => ({}),
  };
});

vi.mock('../lib/fields.ts', async (importOriginal) => {
  const actual = await importOriginal<typeof import('../lib/fields.ts')>();
  return {
    ...actual,
    getFieldsDb: () => ({}),
  };
});

import reactRenderer from '@astrojs/react/server.js';

const POST_SLOT = '<p class="w5-post-body">W5 render verification post body</p>';
const POST_SLUG = 'w5-test-post';

type ActiveSelection = {
  themeId: string;
  paletteId: string;
  mode: 'light' | 'dark' | 'system';
};

type Container = {
  renderToString: (
    component: unknown,
    options?: {
      props?: Record<string, unknown>;
      slots?: Record<string, string>;
      params?: Record<string, string | undefined>;
      partial?: boolean;
    },
  ) => Promise<string>;
};

let container: Container;
let renderBase: (selection: ActiveSelection) => Promise<string>;
let renderPostPage: (slug: string) => Promise<string>;

/**
 * ConsentBanner SSR is intentionally null (ready=false). The real Base path still mounts the
 * client:idle island when consent is enabled — assert that mount contract, not isolated open SSR.
 */
function assertConsentBannerOnRenderPath(html: string) {
  expect(html).toMatch(/<astro-island\b/);
  expect(html).toMatch(/component-export="ConsentBanner"/);
}

function assertSharedRenderAssertions(html: string, selection: ActiveSelection) {
  expect(html).toMatch(/<html[^>]*\sdata-theme="mod-cms"/);
  expect(html).not.toMatch(/data-theme="default"/);
  expect(html).not.toMatch(/data-theme="editorial"/);
  expect((html.match(/<main\b/g) ?? []).length).toBe(1);
  expect(html).toMatch(/<script\b/);
  expect(html).toContain("localStorage.getItem(key)");
  expect(html).toContain("var key = 'mod-cms-theme'");
  expect(html).toContain(`data-palette="${selection.paletteId}"`);
  expect(html).toContain(`data-mode="${selection.mode}"`);
  expect(html).toContain(POST_SLOT);
}

describe('W5 render verification (AstroContainer)', () => {
  beforeAll(async () => {
    const { experimental_AstroContainer: AstroContainer } = await import('astro/container');
    const Base = (await import('../layouts/Base.astro')).default;
    const PostPage = (await import('../pages/post/[slug].astro')).default;

    const astroContainer = await AstroContainer.create();
    astroContainer.addServerRenderer({ renderer: reactRenderer });
    astroContainer.addClientRenderer({
      name: '@astrojs/react',
      entrypoint: '@astrojs/react/client.js',
    });
    container = astroContainer;

    renderBase = (selection: ActiveSelection) =>
      container.renderToString(Base, {
        props: { title: 'W5 render verification', selection },
        slots: { default: POST_SLOT },
      });

    renderPostPage = (slug: string) =>
      container.renderToString(PostPage, {
        params: { slug },
        partial: false,
      });
  });

  describe('Base.astro', () => {
    it('6a default presentation theme → data-theme="mod-cms"', async () => {
      const html = await renderBase({ themeId: 'default', paletteId: 'default', mode: 'light' });
      assertSharedRenderAssertions(html, { themeId: 'default', paletteId: 'default', mode: 'light' });
      expect(html).toContain('class="site-header"');
      expect(html).not.toContain('class="editorial-shell"');
      expect(html).toContain('href="/#modules"');
      expect(html).toContain('Modules');
    });

    it('6a editorial presentation theme → still data-theme="mod-cms"', async () => {
      const html = await renderBase({ themeId: 'editorial', paletteId: 'default', mode: 'light' });
      assertSharedRenderAssertions(html, { themeId: 'editorial', paletteId: 'default', mode: 'light' });
      expect(html).toContain('class="editorial-shell"');
      expect(html).not.toContain('class="site-header"');
      expect(html).toContain('editorial-shell__masthead');
    });

    it('6b editorial shell is visibly distinct from default', async () => {
      const defaultHtml = await renderBase({ themeId: 'default', paletteId: 'default', mode: 'light' });
      const editorialHtml = await renderBase({ themeId: 'editorial', paletteId: 'default', mode: 'light' });
      expect(defaultHtml).toContain('site-header');
      expect(editorialHtml).toContain('editorial-shell__masthead');
      expect(defaultHtml).not.toContain('editorial-shell__masthead');
      expect(editorialHtml).not.toContain('site-header');
    });

    it('6b Base render path mounts ConsentBanner when consent is enabled', async () => {
      const html = await renderBase({ themeId: 'default', paletteId: 'default', mode: 'light' });
      assertConsentBannerOnRenderPath(html);
    });

    it('6c live primary menu resolves url targets into shell nav', async () => {
      h.menuTree = {
        menu: {
          id: 'menu-primary',
          key: 'primary',
          label: 'Primary',
          createdAtMs: 0,
          updatedAtMs: 0,
        },
        items: [
          {
            id: 'item-about',
            menuId: 'menu-primary',
            parentId: null,
            position: 0,
            depth: 0,
            label: 'About',
            targetKind: 'url',
            url: '/about',
            targetEntityType: null,
            targetEntityId: null,
            openInNew: false,
            createdAtMs: 0,
            updatedAtMs: 0,
            children: [],
          },
        ],
      };
      try {
        const html = await renderBase({ themeId: 'default', paletteId: 'default', mode: 'light' });
        assertSharedRenderAssertions(html, { themeId: 'default', paletteId: 'default', mode: 'light' });
        expect(html).toContain('href="/about"');
        expect(html).toContain('About');
        expect(html).not.toContain('href="/#modules"');
      } finally {
        h.menuTree = null;
      }
    });
  });

  describe('post/[slug].astro', () => {
    it('published post adopts Base and emits ConsentBanner through the real page path', async () => {
      const html = await renderPostPage(POST_SLUG);
      expect(html).toMatch(/<html[^>]*\sdata-theme="mod-cms"/);
      expect((html.match(/<main\b/g) ?? []).length).toBe(1);
      expect(html).toContain('W5 consent path post');
      expect(html).toContain('W5 post page body');
      expect(html).toContain('class="content-fields"');
      expect(html).toContain('SEO description');
      expect(html).toContain('W5 custom field value');
      assertConsentBannerOnRenderPath(html);
    });
  });
});
