<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css"
        integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
</html>
import { addPageLinksToNav } from '@launch/api/WPApi';

// Mock @wordpress/api-fetch to prevent actual API calls
let capturedNavContent = null;
jest.mock('@wordpress/api-fetch', () =>
	jest.fn((options) => {
		if (options.path?.includes('navigation')) {
			capturedNavContent = options.data?.content || '';
		}
		return Promise.resolve({});
	}),
);

// Helper to create a mock page object (created pages use originalSlug)
const createPage = (slug, id = Math.random(), isPlugin = false) => {
	const data = {
		id,
		title: { rendered: slug.charAt(0).toUpperCase() + slug.slice(1) },
		link: `https://example.com/${slug}`,
		type: 'page',
	};
	return isPlugin ? { ...data, slug } : { ...data, originalSlug: slug };
};

// Helper to extract page labels from the navigation content
const extractLabels = (navContent) => {
	const matches = navContent.matchAll(/"label":"([^"]+)"/g);
	return [...matches].map((m) => m[1]);
};

// Helper to extract top-level labels (outside submenu blocks)
const extractTopLevelLabels = (navContent) => {
	const withoutSubmenus = navContent.replace(
		/<!-- wp:navigation-submenu[\s\S]*?<!-- \/wp:navigation-submenu -->/g,
		'',
	);
	return extractLabels(withoutSubmenus);
};

// Helper to extract submenu labels
const extractSubmenuLabels = (navContent) => {
	const submenuMatch = navContent.match(
		/<!-- wp:navigation-submenu[\s\S]*?<!-- \/wp:navigation-submenu -->/,
	);
	return submenuMatch ? extractLabels(submenuMatch[0]) : [];
};

describe('addPageLinksToNav', () => {
	beforeEach(() => {
		capturedNavContent = null;
	});

	describe('sorting by navOrder', () => {
		it('should sort pages by their navOrder', async () => {
			const allPages = [
				{ slug: 'about' }, // navOrder: 19
				{ slug: 'services' }, // navOrder: 1
				{ slug: 'contact' }, // navOrder: 5
				{ slug: 'pricing' }, // navOrder: 4
			];
			const createdPages = allPages.map((p) => createPage(p.slug));

			await addPageLinksToNav('nav-1', allPages, createdPages);

			const labels = extractLabels(capturedNavContent);
			// Sorted by navOrder: Services (1), Pricing (4), About (19)
			expect(labels).toEqual(['Services', 'Pricing', 'About', 'Contact']);
		});

		it('should resolve plugin page slugs via aliases (e.g., shop -> products)', async () => {
			const allPages = [{ slug: 'services' }, { slug: 'about' }];
			const createdPages = [createPage('services'), createPage('about')];
			const pluginPages = [createPage('shop', null, true)]; // shop is alias for products (navOrder: 2)

			await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);

			const labels = extractLabels(capturedNavContent);
			// Services (1), Shop/Products (2), About (19)
			expect(labels).toEqual(['Services', 'Shop', 'About']);
		});

		it('should place unknown slugs after sorted pages but before contact', async () => {
			const allPages = [{ slug: 'services' }, { slug: 'contact' }];
			const createdPages = [createPage('services'), createPage('contact')];
			const pluginPages = [
				createPage('unknown-a', null, true),
				createPage('unknown-b', null, true),
			];

			await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);

			const labels = extractLabels(capturedNavContent);
			// Services (1), then unknowns (maxOrder), then Contact (always last)
			expect(labels).toEqual(['Services', 'Unknown-a', 'Unknown-b', 'Contact']);
		});
	});

	describe('contact page positioning', () => {
		it('should place contact at end when 6 or fewer pages (no submenu)', async () => {
			const allPages = [
				{ slug: 'services' }, // 1
				{ slug: 'products' }, // 2
				{ slug: 'book' }, // 3
				{ slug: 'pricing' }, // 4
				{ slug: 'about' }, // 19
				{ slug: 'contact' }, // moved to last
			];
			const createdPages = allPages.map((p) => createPage(p.slug));

			await addPageLinksToNav('nav-1', allPages, createdPages);

			const labels = extractLabels(capturedNavContent);

			// All 6 should be top-level with contact last
			expect(labels).toEqual([
				'Services',
				'Products',
				'Book',
				'Pricing',
				'About',
				'Contact',
			]);
			expect(labels[5]).toBe('Contact');

			// No submenu when exactly 6 pages
			expect(capturedNavContent).not.toContain('wp:navigation-submenu');
		});

		it('should place contact at index 4 with remaining pages in submenu when 7+ pages', async () => {
			const allPages = [
				{ slug: 'services' }, // 1
				{ slug: 'products' }, // 2
				{ slug: 'book' }, // 3
				{ slug: 'pricing' }, // 4
				{ slug: 'blog' }, // 18
				{ slug: 'about' }, // 19
				{ slug: 'contact' }, // moved to 5th (index 4)
			];
			const createdPages = allPages.map((p) => createPage(p.slug));

			await addPageLinksToNav('nav-1', allPages, createdPages);

			const topLevelLabels = extractTopLevelLabels(capturedNavContent);
			const submenuLabels = extractSubmenuLabels(capturedNavContent);

			// Top 5: Services, Products, Book, Pricing, Contact (promoted to last top-level)
			expect(topLevelLabels).toHaveLength(5);
			expect(topLevelLabels).toEqual([
				'Services',
				'Products',
				'Book',
				'Pricing',
				'Contact',
			]);
			expect(topLevelLabels[4]).toBe('Contact');

			// Submenu should contain remaining pages (Blog, About) plus "More" label
			expect(capturedNavContent).toContain('wp:navigation-submenu');
			expect(submenuLabels).toContain('More');
			expect(submenuLabels).toContain('Blog');
			expect(submenuLabels).toContain('About');

			// Contact should NOT be in submenu
			expect(submenuLabels).not.toContain('Contact');
		});

		it('should handle case when no contact page exists', async () => {
			const allPages = [
				{ slug: 'services' },
				{ slug: 'products' },
				{ slug: 'about' },
			];
			const createdPages = allPages.map((p) => createPage(p.slug));

			await addPageLinksToNav('nav-1', allPages, createdPages);

			const labels = extractLabels(capturedNavContent);
			expect(labels).toEqual(['Services', 'Products', 'About']);
			expect(labels).not.toContain('Contact');
		});

		it('should recognize contact page by alias (contact-form)', async () => {
			const allPages = [{ slug: 'services' }, { slug: 'about' }];
			const createdPages = [createPage('services'), createPage('about')];
			const pluginPages = [createPage('contact-form', null, true)]; // alias for contact

			await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);

			const labels = extractLabels(capturedNavContent);
			// Contact-form should be treated as contact and placed last
			expect(labels).toEqual(['Services', 'About', 'Contact-form']);
			expect(labels[labels.length - 1]).toBe('Contact-form');
		});
	});

	describe('edge cases', () => {
		it('should handle empty pages array', async () => {
			await addPageLinksToNav('nav-1', [], []);

			expect(capturedNavContent).toBe('');
		});

		it('should exclude home page from navigation', async () => {
			const allPages = [
				{ slug: 'home' },
				{ slug: 'services' },
				{ slug: 'contact' },
			];
			const createdPages = allPages.map((p) => createPage(p.slug));

			await addPageLinksToNav('nav-1', allPages, createdPages);

			const labels = extractLabels(capturedNavContent);
			expect(labels).not.toContain('Home');
			expect(labels).toEqual(['Services', 'Contact']);
		});
	});
});
