import { test as base, expect, type Locator, Page } from "@playwright/test";

/**
 * Viewport profiles for responsive testing
 */
export const VIEWPORT_PROFILES = {
	mobile: { width: 375, height: 667 },
	tablet: { width: 768, height: 1024 },
	desktop: { width: 1280, height: 720 },
	widescreen: { width: 1920, height: 1080 },
} as const;

/**
 * Theme profiles for accessibility testing
 */
export const THEME_PROFILES = {
	light: "light",
	dark: "dark",
	contrast: "contrast",
} as const;

export type ThemeProfile = keyof typeof THEME_PROFILES;
export type ViewportProfile = keyof typeof VIEWPORT_PROFILES;

/**
 * Article data shape for deterministic testing
 */
export interface TestArticle {
	id: number;
	headline: string;
	summary: string;
	source_url: string;
	source_citation: string;
	published_at: string;
	image_url: string;
	summary_image_url?: string;
	tldr_points?: string[];
	summary_body?: string;
	language?: string;
}

/**
 * Test fixture interface
 */
export interface ClawFortFixtures {
	/**
	 * Navigate to application with optional article permalink
	 */
	gotoApp: (options?: {
		articleId?: number;
		policy?: "terms" | "attribution";
	}) => Promise<void>;

	/**
	 * Set theme preference on the page
	 */
	setTheme: (theme: ThemeProfile) => Promise<void>;

	/**
	 * Set viewport to a named profile
	 */
	setViewport: (profile: ViewportProfile) => Promise<void>;

	/**
	 * Wait for hero section to be loaded
	 */
	waitForHero: () => Promise<Locator>;

	/**
	 * Wait for news feed to be loaded
	 */
	waitForFeed: () => Promise<Locator>;

	/**
	 * Get hero article data from page
	 */
	getHeroArticle: () => Promise<TestArticle | null>;

	/**
	 * Get feed articles data from page
	 */
	getFeedArticles: () => Promise<TestArticle[]>;

	/**
	 * Open summary modal for an article
	 */
	openSummaryModal: (articleId: number) => Promise<Locator>;

	/**
	 * Close summary modal
	 */
	closeSummaryModal: () => Promise<void>;

	/**
	 * Check if summary modal is open
	 */
	isSummaryModalOpen: () => Promise<boolean>;

	/**
	 * Get stable selector for critical controls
	 */
	getControl: (name: string) => Locator;

	/**
	 * Wait for app to be fully initialized
	 */
	waitForAppReady: () => Promise<void>;
}

/**
 * Extended test with ClawFort fixtures
 */
export const test = base.extend<ClawFortFixtures>({
	gotoApp: async ({ page }, use) => {
		await use(async (options = {}) => {
			let url = "/";

			if (options.articleId) {
				url = `/?article=${options.articleId}`;
			} else if (options.policy) {
				url = `/?policy=${options.policy}`;
			}

			await page.goto(url);
			await page.waitForLoadState("networkidle");
		});
	},

	setTheme: async ({ page }, use) => {
		await use(async (theme: ThemeProfile) => {
			// Open theme menu
			const themeButton = page.locator("#theme-menu-button");
			await themeButton.click();

			// Select theme option
			const themeOption = page.locator(`[data-theme-option="${theme}"]`);
			await themeOption.click();

			// Wait for theme to apply
			await page.waitForSelector(`html[data-theme="${theme}"]`, {
				timeout: 5000,
			});
		});
	},

	setViewport: async ({ page }, use) => {
		await use(async (profile: ViewportProfile) => {
			const viewport = VIEWPORT_PROFILES[profile];
			await page.setViewportSize(viewport);
		});
	},

	waitForHero: async ({ page }, use) => {
		await use(async () => {
			const hero = page
				.locator(
					'article[itemscope][itemtype="https://schema.org/NewsArticle"]',
				)
				.first();
			await hero.waitFor({ state: "visible", timeout: 15000 });
			return hero;
		});
	},

	waitForFeed: async ({ page }, use) => {
		await use(async () => {
			const feed = page.locator('section:has(h2:has-text("Recent News"))');
			await feed.waitFor({ state: "visible", timeout: 15000 });
			return feed;
		});
	},

	getHeroArticle: async ({ page }, use) => {
		await use(async () => {
			const hero = await page
				.locator(
					'article[itemscope][itemtype="https://schema.org/NewsArticle"]',
				)
				.first();

			// Check if hero exists and has content
			const count = await hero.count();
			if (count === 0) return null;

			// Extract hero article data
			const headline = await hero
				.locator("h1")
				.textContent()
				.catch(() => null);
			const summary = await hero
				.locator(".hero-summary")
				.textContent()
				.catch(() => null);
			const id = await hero
				.getAttribute("id")
				.then((id) => (id ? parseInt(id.replace("news-", "")) : null));

			if (!headline || !id) return null;

			return {
				id,
				headline,
				summary: summary || "",
				source_url: "",
				source_citation: "",
				published_at: new Date().toISOString(),
				image_url: "",
			};
		});
	},

	getFeedArticles: async ({ page }, use) => {
		await use(async () => {
			const articles = await page
				.locator(
					'article[itemscope][itemtype="https://schema.org/NewsArticle"]',
				)
				.all();
			const feedArticles: TestArticle[] = [];

			for (const article of articles.slice(1)) {
				// Skip hero
				const id = await article
					.getAttribute("id")
					.then((id) => (id ? parseInt(id.replace("news-", "")) : null));
				const headline = await article
					.locator("h3")
					.textContent()
					.catch(() => null);
				const summary = await article
					.locator(".news-card-summary")
					.textContent()
					.catch(() => null);

				if (id && headline) {
					feedArticles.push({
						id,
						headline,
						summary: summary || "",
						source_url: "",
						source_citation: "",
						published_at: new Date().toISOString(),
						image_url: "",
					});
				}
			}

			return feedArticles;
		});
	},

	openSummaryModal: async ({ page }, use) => {
		await use(async (articleId: number) => {
			// Find article and click its "Read TL;DR" button
			const article = page.locator(`#news-${articleId}`);
			const readButton = article.locator('button:has-text("Read TL;DR")');
			await readButton.click();

			// Wait for modal to appear
			const modal = page
				.locator('[role="dialog"][aria-modal="true"]')
				.filter({ hasText: "TL;DR" });
			await modal.waitFor({ state: "visible", timeout: 5000 });

			return modal;
		});
	},

	closeSummaryModal: async ({ page }, use) => {
		await use(async () => {
			const closeButton = page.locator(
				'[role="dialog"] button:has-text("Close")',
			);
			await closeButton.click();

			// Wait for modal to disappear
			const modal = page
				.locator('[role="dialog"][aria-modal="true"]')
				.filter({ hasText: "TL;DR" });
			await modal.waitFor({ state: "hidden", timeout: 5000 });
		});
	},

	isSummaryModalOpen: async ({ page }, use) => {
		await use(async () => {
			const modal = page
				.locator('[role="dialog"][aria-modal="true"]')
				.filter({ hasText: "TL;DR" });
			return await modal.isVisible().catch(() => false);
		});
	},

	getControl: async ({ page }, use) => {
		await use((name: string) => {
			// Stable selector strategy: prefer test-id, then role, then accessible name
			const selectors: Record<string, string> = {
				"theme-menu": "#theme-menu-button",
				"language-select": "#language-select",
				"back-to-top": '[aria-label="Back to top"]',
				"share-x": '[aria-label="Share on X"]',
				"share-whatsapp": '[aria-label="Share on WhatsApp"]',
				"share-linkedin": '[aria-label="Share on LinkedIn"]',
				"share-copy": '[aria-label="Copy article link"]',
				"modal-close": '[role="dialog"] button:has-text("Close")',
				"terms-link": 'button:has-text("Terms of Use")',
				"attribution-link": 'button:has-text("Attribution")',
				"hero-read-more": 'article:first-of-type button:has-text("Read TL;DR")',
				"skip-link": 'a[href="#main-content"]',
			};

			const selector = selectors[name];
			if (!selector) {
				throw new Error(
					`Unknown control: ${name}. Available controls: ${Object.keys(selectors).join(", ")}`,
				);
			}

			return page.locator(selector);
		});
	},

	waitForAppReady: async ({ page }, use) => {
		await use(async () => {
			// Wait for page to be fully loaded
			await page.waitForLoadState("networkidle");

			// Wait for Alpine.js to initialize
			await page.waitForFunction(
				() => {
					return (
						document.querySelector("html")?.hasAttribute("data-theme") ||
						document.readyState === "complete"
					);
				},
				{ timeout: 10000 },
			);

			// Wait for either hero or feed to be visible
			await Promise.race([
				page.waitForSelector("article[itemscope]", { timeout: 15000 }),
				page.waitForSelector('.text-6xl:has-text("🤖")', { timeout: 15000 }), // No news state
			]);
		});
	},
});

export { expect };
