/*
 * Copyright (C) 2023-2024  Yomitan Authors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

import {test as base, chromium} from '@playwright/test';
import path from 'path';
import {fileURLToPath} from 'url';

const dirname = path.dirname(fileURLToPath(import.meta.url));

export const root = path.join(dirname, '..', '..');

export const test = base.extend({
    // eslint-disable-next-line no-empty-pattern
    context: async ({}, use) => {
        const pathToExtension = path.join(root, 'ext');
        const context = await chromium.launchPersistentContext('', {
            // Disabled: headless: false,
            args: [
                '--headless=new',
                `--disable-extensions-except=${pathToExtension}`,
                `--load-extension=${pathToExtension}`
            ]
        });
        await use(context);
        await context.close();
    },
    extensionId: async ({context}, use) => {
        let [background] = context.serviceWorkers();
        if (!background) {
            background = await context.waitForEvent('serviceworker');
        }

        const extensionId = background.url().split('/')[2];
        await use(extensionId);
    }
});

export const expect = test.expect;

/**
 * @returns {Map<string, string>}
 */
export function getMockModelFields() {
    return new Map([
        ['Word', '{expression}'],
        ['Reading', '{furigana-plain}'],
        ['Sentence', '{clipboard-text}'],
        ['Audio', '{audio}']
    ]);
}

/**
 * @param {import('playwright').Route} route
 * @returns {Promise<void>}
 */
export async function mockAnkiRouteHandler(route) {
    try {
        /** @type {unknown} */
        const requestJson = route.request().postDataJSON();
        if (typeof requestJson !== 'object' || requestJson === null) {
            throw new Error(`Invalid request type: ${typeof requestJson}`);
        }
        const body = getResponseBody(/** @type {import('core').SerializableObject} */ (requestJson).action);
        const responseJson = {
            status: 200,
            contentType: 'text/json',
            body: JSON.stringify(body)
        };
        await route.fulfill(responseJson);
    } catch {
        return await route.abort();
    }
}

/**
 * @param {import('playwright').Page} page
 * @param {string} text
 * @returns {Promise<void>}
 */
export const writeToClipboardFromPage = async (page, text) => {
    await page.evaluate(`navigator.clipboard.writeText('${text}')`);
};

/**
 * @returns {Record<string, unknown>}
 */
export function getExpectedAddNoteBody() {
    return {
        version: 2,
        action: 'addNote',
        params: {
            note: {
                fields: {
                    Word: '読む',
                    Reading: '読[よ]む',
                    Audio: '[sound:mock_audio.mp3]',
                    Sentence: '読むの例文'
                },
                tags: ['yomitan'],
                deckName: 'Mock Deck',
                modelName: 'Mock Model',
                options: {
                    allowDuplicate: true,
                    duplicateScope: 'collection',
                    duplicateScopeOptions: {
                        deckName: null,
                        checkChildren: false,
                        checkAllModels: false
                    }
                }
            }
        }
    };
}

/**
 * @param {unknown} action
 * @returns {unknown}
 * @throws {Error}
 */
function getResponseBody(action) {
    switch (action) {
        case 'version': return 6;
        case 'deckNames': return ['Mock Deck'];
        case 'modelNames': return ['Mock Model'];
        case 'modelFieldNames': return [...getMockModelFields().keys()];
        case 'canAddNotes': return [true, true];
        case 'storeMediaFile': return 'mock_audio.mp3';
        case 'addNote': return 102312488912;
        case 'multi': return [];
        default: throw new Error(`Unknown action: ${action}`);
    }
}