import type { Locator, Page } from '@playwright/test';
import { siteConfig } from '../config/siteConfig';

/**
 * Wait for React hydration — checks that the root element has interactive content.
 */
export async function waitForHydration(page: Page, timeout = 10000): Promise<void> {
    try {
        await page.waitForFunction(
            () => {
                const root = document.getElementById('__next') || document.getElementById('root');
                if (!root) return true;
                return root.children.length > 0 &&
                    root.querySelectorAll('a, button, input, select').length > 0;
            },
            { timeout }
        );
    } catch {
        // Continue even if hydration check times out
    }
}

/**
 * Navigate to a PDP page and wait for it to load.
 */
export async function navigateToPdp(page: Page, path: string): Promise<void> {
    const response = await page.goto(path, { waitUntil: 'domcontentloaded', timeout: 30000 });

    if (!response || response.status() === 404) {
        throw new Error(`PDP returned 404: ${path}`);
    }

    await page.waitForLoadState('networkidle', { timeout: siteConfig.timeouts.networkIdle }).catch(() => {});
    await waitForHydration(page);

    if ((await page.locator('#no-route').count()) > 0) {
        throw new Error(`Page not found: ${path}`);
    }
}

/**
 * Find the "Add To My Bag" button on PDP.
 * Real DOM: button "Add To My Bag"
 */
export async function findAddToCartButton(page: Page): Promise<Locator | null> {
    // Primary: add to cart/bag buttons
    const cartCandidates = [
        page.getByRole('button', { name: /add to my bag/i }),
        page.getByRole('button', { name: /add to bag/i }),
        page.getByRole('button', { name: /add to cart/i }),
        page.getByRole('button', { name: /カートに追加/i }),
    ];

    for (const btn of cartCandidates) {
        try {
            await btn.waitFor({ state: 'visible', timeout: 5000 });
            return btn;
        } catch {
            continue;
        }
    }

    // Fallback: reservation buttons (for appointment products)
    const reservationCandidates = [
        page.getByRole('button', { name: /proceed to store visit reservation/i }),
        page.getByRole('button', { name: /store visit reservation/i }),
        page.getByRole('button', { name: /reservation/i }),
        page.getByRole('button', { name: /来店予約/i }),
    ];

    for (const btn of reservationCandidates) {
        try {
            await btn.waitFor({ state: 'visible', timeout: 3000 });
            return btn;
        } catch {
            continue;
        }
    }

    return null;
}

export async function findActionButton(page: Page): Promise<{
    button: Locator;
    type: 'add-to-cart' | 'reservation';
} | null> {
    const cartCandidates = [
        page.getByRole('button', { name: /add to my bag/i }),
        page.getByRole('button', { name: /add to bag/i }),
        page.getByRole('button', { name: /add to cart/i }),
        page.getByRole('button', { name: /カートに追加/i }),
    ];

    for (const btn of cartCandidates) {
        try {
            await btn.waitFor({ state: 'visible', timeout: 5000 });
            return { button: btn, type: 'add-to-cart' };
        } catch {
            continue;
        }
    }

    const reservationCandidates = [
        page.getByRole('button', { name: /proceed to store visit reservation/i }),
        page.getByRole('button', { name: /store visit reservation/i }),
        page.getByRole('button', { name: /reservation/i }),
        page.getByRole('button', { name: /来店予約/i }),
    ];

    for (const btn of reservationCandidates) {
        try {
            await btn.waitFor({ state: 'visible', timeout: 3000 });
            return { button: btn, type: 'reservation' };
        } catch {
            continue;
        }
    }

    return null;
}

/**
 * Open the mini-cart drawer.
 * Real DOM: button "Toggle mini cart. You have N items in your cart."
 */
export async function openMiniCart(page: Page): Promise<Locator | null> {
    const cartButton = page.getByRole('button', { name: /toggle mini cart/i });
    if (!(await cartButton.isVisible().catch(() => false))) return null;

    await cartButton.click();
    // Wait for the drawer to appear
    await page.waitForTimeout(1000);
    return cartButton;
}

/**
 * Get product name from PDP h1 headings.
 * Real DOM: two h1 elements — brand + product name.
 */
export async function getProductName(page: Page): Promise<string | null> {
    const headings = page.locator('main h1');
    const texts = await headings.allTextContents();
    return texts.filter(t => t.trim()).join(' ') || null;
}

/**
 * Find configurable product options (swatch buttons, color selectors, etc.)
 */
export async function findProductOptions(page: Page): Promise<Locator | null> {
    const selectors = [
        '[data-testid="product-options"]',
        'main fieldset:has(input[type="radio"], select, button)',
        'main [role="group"]:has(input[type="radio"], select, button)',
    ];

    for (const sel of selectors) {
        const el = page.locator(sel).first();
        if (await el.isVisible().catch(() => false)) return el;
    }
    return null;
}

/**
 * Select the first available option value from a product options container.
 */
export async function selectOptionValue(
    page: Page,
    optionContainer: Locator
): Promise<boolean> {
    // Try radio buttons — only click if visible AND not already checked
    const radios = optionContainer.locator('input[type="radio"]:not(:checked)');
    if ((await radios.count()) > 0) {
        const firstUnchecked = radios.first();
        if (await firstUnchecked.isVisible({ timeout: 1000 }).catch(() => false)) {
            await firstUnchecked.click({ timeout: 3000 });
            return true;
        }
    }
    // If all radios are checked, a default is already selected
    const checkedRadios = optionContainer.locator('input[type="radio"]:checked');
    if ((await checkedRadios.count()) > 0) return true;

    // Try swatch/option buttons
    const buttons = optionContainer.locator('button:not([disabled])');
    for (let i = 0; i < await buttons.count(); i++) {
        const btn = buttons.nth(i);
        if (await btn.isVisible().catch(() => false)) {
            await btn.click();
            return true;
        }
    }

    // Try select dropdown
    const selects = optionContainer.locator('select');
    if ((await selects.count()) > 0) {
        await selects.first().selectOption({ index: 1 });
        return true;
    }

    return false;
}

/**
 * Wait for loading indicators (spinners, skeleton loaders) to disappear.
 */
export async function waitForLoadingIndicators(page: Page): Promise<void> {
    const loadingImg = page.locator('img[alt="Loading"]');
    if (await loadingImg.isVisible().catch(() => false)) {
        await loadingImg.waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {});
    }
}

/**
 * Clear all items from the cart via the mini-cart drawer.
 */
export async function clearCart(page: Page): Promise<void> {
    const cartBtn = await openMiniCart(page);
    if (!cartBtn) return;

    let attempts = 10;
    while (attempts > 0) {
        const deleteBtn = page.locator(
            'button[aria-label*="delete" i], button[aria-label*="remove" i]'
        ).first();

        if (!(await deleteBtn.isVisible().catch(() => false))) break;

        await deleteBtn.click();
        await page.waitForTimeout(1500);
        attempts--;
    }

    // Close the drawer
    const closeBtn = page.locator('button[aria-label*="close" i]').first();
    if (await closeBtn.isVisible().catch(() => false)) {
        await closeBtn.click();
    }
}
