import { expect, test, type Page, type Route } from '@playwright/test';
import { gameBasePath, gameProfile, gameTrpcRoute } from './gameTestPaths.js';

const response = (data: unknown) => ({ result: { data } });

const operationNames = (route: Route): string[] => {
    const url = new URL(route.request().url());
    return decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf('/trpc/') + 6)).split(',');
};

const publicResponse = (operation: string): unknown => {
    if (operation === 'public.getMapLayout') {
        return response({ mapName: 'che', cityList: [] });
    }
    if (operation === 'public.getCachedMap') {
        return response({ year: 180, month: 1, cityList: [], nationList: [], history: [] });
    }
    if (operation === 'public.getWorldTrend') {
        return response({ year: 180, month: 1, turnTerm: 5 });
    }
    if (operation === 'public.getNationList' || operation === 'public.getGeneralList') {
        return response([]);
    }
    throw new Error(`Unhandled public tRPC operation: ${operation}`);
};

const seedGameStorage = async (page: Page, gameToken: string): Promise<void> => {
    await page.addInitScript(
        ({ token, profile }) => {
            window.localStorage.setItem('sammo-game-token', token);
            window.localStorage.setItem('sammo-game-profile', profile);
        },
        { token: gameToken, profile: gameProfile }
    );
};

test('consumes a same-tab gateway transfer from a clean profile URL', async ({ page }) => {
    await page.addInitScript((profile) => {
        window.sessionStorage.setItem(
            'sammo-pending-game-session',
            JSON.stringify({ profile, gatewayToken: 'gateway-transfer-token' })
        );
    }, gameProfile);
    const exchangedInputs: unknown[] = [];
    await page.route(gameTrpcRoute, async (route) => {
        const operations = operationNames(route);
        const requestBody = route.request().postDataJSON() as
            { json?: unknown; 0?: { json?: unknown; gatewayToken?: string } } | undefined;
        const results = operations.map((operation) => {
            if (operation === 'auth.exchangeGatewayToken') {
                exchangedInputs.push(requestBody?.json ?? requestBody?.[0]?.json ?? requestBody?.[0]);
                return response({
                    accessToken: 'ga_transferred',
                    profile: gameProfile,
                    expiresAt: '2026-08-11T01:00:00Z',
                });
            }
            if (operation === 'auth.status') {
                expect(route.request().headers().authorization).toBe('Bearer ga_transferred');
                return response({ userId: 'transfer-user' });
            }
            if (operation === 'lobby.info') {
                return response({ myGeneral: null });
            }
            if (operation === 'join.getConfig') {
                return response({});
            }
            return publicResponse(operation);
        });
        await route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify(results),
        });
    });

    await page.goto('select-general');

    await expect(page).toHaveURL(new RegExp(`${gameBasePath}/select-general$`));
    await expect(page.locator('.page-title')).toContainText('장 수 선 택');
    expect(exchangedInputs).toEqual([{ gatewayToken: 'gateway-transfer-token' }]);
    expect(await page.evaluate(() => window.sessionStorage.getItem('sammo-pending-game-session'))).toBeNull();
    expect(await page.evaluate(() => window.localStorage.getItem('sammo-game-profile'))).toBe(gameProfile);
    expect(await page.evaluate(() => window.localStorage.getItem('sammo-game-token'))).toBe('ga_transferred');
});

test('removes an invalid ga_ token and redirects an authenticated route to public', async ({ page }) => {
    await seedGameStorage(page, 'ga_invalid');
    let gatewayRequests = 0;
    await page.route('http://127.0.0.1:15120/api/trpc/**', async (route) => {
        gatewayRequests += 1;
        await route.abort();
    });
    await page.route(gameTrpcRoute, async (route) => {
        const operations = operationNames(route);
        if (operations.includes('auth.status')) {
            expect(route.request().headers().authorization).toBe('Bearer ga_invalid');
            await route.fulfill({
                status: 401,
                contentType: 'application/json',
                body: JSON.stringify({ error: 'invalid token' }),
            });
            return;
        }
        await route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify(operations.map(publicResponse)),
        });
    });

    await page.goto('select-general');
    await expect(page).toHaveURL(new RegExp(`${gameBasePath}/public$`));
    await expect(page.getByRole('heading', { name: '공개 동향' })).toBeVisible();
    expect(await page.evaluate(() => window.localStorage.getItem('sammo-game-token'))).toBeNull();
    expect(gatewayRequests).toBe(0);
});

test('keeps a valid ga_ token when only lobby.info is unavailable', async ({ page }) => {
    await seedGameStorage(page, 'ga_valid');
    page.on('dialog', (dialog) => dialog.accept());
    let gatewayRequests = 0;
    let statusRequests = 0;
    let lobbyRequests = 0;
    await page.route('http://127.0.0.1:15120/api/trpc/**', async (route) => {
        gatewayRequests += 1;
        await route.abort();
    });
    await page.route(gameTrpcRoute, async (route) => {
        const operations = operationNames(route);
        if (operations.includes('auth.status')) {
            statusRequests += 1;
            expect(route.request().headers().authorization).toBe('Bearer ga_valid');
            await route.fulfill({
                status: 200,
                contentType: 'application/json',
                body: JSON.stringify([response({ userId: 'valid-user' })]),
            });
            return;
        }
        if (operations.includes('lobby.info')) {
            lobbyRequests += 1;
            await route.fulfill({
                status: 503,
                contentType: 'application/json',
                body: JSON.stringify({ error: 'lobby unavailable' }),
            });
            return;
        }
        await route.fulfill({
            status: 503,
            contentType: 'application/json',
            body: JSON.stringify({ error: 'fixture page data unavailable' }),
        });
    });

    await page.goto('select-general');
    await expect(page).toHaveURL(new RegExp(`${gameBasePath}/select-general$`));
    await expect(page.locator('.page-title')).toContainText('장 수 선 택');
    expect(await page.evaluate(() => window.localStorage.getItem('sammo-game-token'))).toBe('ga_valid');
    expect(statusRequests).toBe(1);
    expect(lobbyRequests).toBe(1);
    expect(gatewayRequests).toBe(0);
});
