import { describe, test, expect } from 'vitest';
import { buildSavePayload } from './useWhpApi';
import { PageData } from '../types';
import { DEFAULT_SITE_DESIGN } from '../state/SiteDesignContext';

const pageA: PageData = { id: 'home', name: 'Home', slug: 'index', craftState: 'STORED_HOME' };
const pageB: PageData = { id: 'page_2', name: 'About', slug: 'about', craftState: 'STORED_ABOUT' };
const headerPage: PageData = { id: '__header__', name: 'Header', slug: '__header__', craftState: 'STALE_HEADER' };
const footerPage: PageData = { id: '__footer__', name: 'Footer', slug: '__footer__', craftState: 'STALE_FOOTER' };

describe('buildSavePayload', () => {
  test('editing header: live serialize lands in header_craft_state, not in any page slot', () => {
    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_HEADER',
      pages: [pageA, pageB],
      headerPage,
      footerPage,
      activePageId: '__header__',
      isEditingHeader: true,
      isEditingFooter: false,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design: DEFAULT_SITE_DESIGN,
    });

    // The fresh live canvas must be reflected in header_craft_state, not the stale stored one.
    expect(payload.header_craft_state).toBe('LIVE_HEADER');
    expect(payload.header_craft_state).not.toBe('STALE_HEADER');

    // Footer must remain untouched (stored state, since we're not editing it).
    expect(payload.footer_craft_state).toBe('STALE_FOOTER');

    // Live header content must never leak into a page slot or the top-level page fields.
    expect(payload.craft_state).not.toBe('LIVE_HEADER');
    for (const p of payload.pages_craft_state) {
      expect(p.craftState).not.toBe('LIVE_HEADER');
    }
    // Top-level page fields should fall back to the landing page's own stored state.
    expect(payload.craft_state).toBe('STORED_HOME');

    // Per-page slots must reflect each page's own stored state, untouched.
    expect(payload.pages_craft_state.find((p) => p.id === 'home')?.craftState).toBe('STORED_HOME');
    expect(payload.pages_craft_state.find((p) => p.id === 'page_2')?.craftState).toBe('STORED_ABOUT');
  });

  test('editing footer: live serialize lands in footer_craft_state, not in any page slot', () => {
    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_FOOTER',
      pages: [pageA, pageB],
      headerPage,
      footerPage,
      activePageId: '__footer__',
      isEditingHeader: false,
      isEditingFooter: true,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design: DEFAULT_SITE_DESIGN,
    });

    expect(payload.footer_craft_state).toBe('LIVE_FOOTER');
    expect(payload.footer_craft_state).not.toBe('STALE_FOOTER');
    expect(payload.header_craft_state).toBe('STALE_HEADER');

    expect(payload.craft_state).not.toBe('LIVE_FOOTER');
    for (const p of payload.pages_craft_state) {
      expect(p.craftState).not.toBe('LIVE_FOOTER');
    }
  });

  test('editing a real page: behavior unchanged — live serialize goes to that page + top-level, header/footer come from stored state', () => {
    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_PAGE_HOME',
      pages: [pageA, pageB],
      headerPage,
      footerPage,
      activePageId: 'home',
      isEditingHeader: false,
      isEditingFooter: false,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design: DEFAULT_SITE_DESIGN,
    });

    // Live canvas goes to the active page and top-level slots.
    expect(payload.craft_state).toBe('LIVE_PAGE_HOME');
    expect(payload.pages_craft_state.find((p) => p.id === 'home')?.craftState).toBe('LIVE_PAGE_HOME');
    // Other pages keep their stored state.
    expect(payload.pages_craft_state.find((p) => p.id === 'page_2')?.craftState).toBe('STORED_ABOUT');

    // Header/footer come from stored (stale-but-correct, since we're not editing them) state.
    expect(payload.header_craft_state).toBe('STALE_HEADER');
    expect(payload.footer_craft_state).toBe('STALE_FOOTER');
  });

  test('I-1: activePageId matches no page (dangling, e.g. original Home deleted then reload reset it to "home") -- live serialize still lands in pages_craft_state[0] / index.html, not lost', () => {
    // pages[0] has id 'page_x' (the replacement landing page after the
    // original 'home' was deleted); activePageId is stuck at the stale
    // default 'home', which matches no entry in `pages`.
    const pageX: PageData = { id: 'page_x', name: 'Home', slug: 'index', craftState: 'STORED_X' };
    const p2: PageData = { id: 'p2', name: 'About', slug: 'about', craftState: 'STORED_ABOUT_2' };

    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_EDIT',
      pages: [pageX, p2],
      headerPage,
      footerPage,
      activePageId: 'home',
      isEditingHeader: false,
      isEditingFooter: false,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design: DEFAULT_SITE_DESIGN,
    });

    // The live edit must land in pages_craft_state[0] (index.html slot),
    // not be silently dropped to only the legacy top-level fields.
    expect(payload.pages_craft_state[0].craftState).toBe('LIVE_EDIT');
    expect(payload.pages[0].filename).toBe('index.html');
    // Top-level fields (legacy) should also reflect the live edit.
    expect(payload.craft_state).toBe('LIVE_EDIT');
    // The other page is untouched.
    expect(payload.pages_craft_state.find((p) => p.id === 'p2')?.craftState).toBe('STORED_ABOUT_2');
  });

  test('head code + design tokens are included in the save payload', () => {
    const design = { ...DEFAULT_SITE_DESIGN, headCode: '<meta name="x">' };

    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_PAGE_HOME',
      pages: [pageA, pageB],
      headerPage,
      footerPage,
      activePageId: 'home',
      isEditingHeader: false,
      isEditingFooter: false,
      headCode: '<meta name="x">',
      design,
    });

    expect(payload.head_code).toBe('<meta name="x">');
    expect(payload.design).toEqual(design);
    expect(payload.design.headCode).toBe('<meta name="x">');
  });

  test('PKG-H §5: per-page seo is included in both pages_craft_state and pages entries', () => {
    const pageWithSeo: PageData = {
      id: 'home',
      name: 'Home',
      slug: 'index',
      craftState: 'STORED_HOME',
      seo: {
        metaTitle: 'Custom Title',
        metaDescription: 'Custom description',
        ogTitle: 'Custom OG',
        ogImage: '/uploads/og.jpg',
        twitterCard: 'summary_large_image',
        noindex: true,
      },
    };

    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_PAGE_HOME',
      pages: [pageWithSeo, pageB],
      headerPage,
      footerPage,
      activePageId: 'home',
      isEditingHeader: false,
      isEditingFooter: false,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design: DEFAULT_SITE_DESIGN,
    });

    expect(payload.pages_craft_state.find((p) => p.id === 'home')?.seo).toEqual(pageWithSeo.seo);
    expect(payload.pages.find((p) => p.filename === 'index.html')?.seo).toEqual(pageWithSeo.seo);

    // A page with no seo overrides omits the field entirely (undefined),
    // not an empty object -- back-compat with pre-PKG-H saved shapes.
    expect(payload.pages_craft_state.find((p) => p.id === 'page_2')?.seo).toBeUndefined();
    expect(payload.pages.find((p) => p.filename === 'about.html')?.seo).toBeUndefined();
  });

  test('PKG-H §5: favicon flows through the design object already carried by the payload', () => {
    const design = { ...DEFAULT_SITE_DESIGN, favicon: '/uploads/favicon.png' };

    const payload = buildSavePayload({
      siteId: 1,
      siteName: 'Test Site',
      liveCraftState: 'LIVE_PAGE_HOME',
      pages: [pageA, pageB],
      headerPage,
      footerPage,
      activePageId: 'home',
      isEditingHeader: false,
      isEditingFooter: false,
      headCode: DEFAULT_SITE_DESIGN.headCode,
      design,
    });

    expect(payload.design.favicon).toBe('/uploads/favicon.png');
  });
});
