import {
    ActionPanelQA,
    ConnectionsActionPanelControls,
    ConnectionsBaseQA,
    ConnectionsFormQA,
    ConnectionsS3BaseQA,
    ConnectionsYadocsQA,
    DialogCreateWorkbookEntryQa,
    EntryDialogQA,
    SharedEntriesBaseQa,
} from '../../../src/shared/constants';
import {ActionPanelEntryContextMenuQa} from '../../../src/shared/constants/qa/action-panel';
import {v1 as uuidv1} from 'uuid';

import {slct, waitForCondition} from '../../utils';
import {BasePage} from '../BasePage';
import type {BasePageProps} from '../BasePage';
import type {ConsoleMessage, Request, FileChooser} from '@playwright/test';
import Revisions from '../common/Revisions';
import XLSX from '@datalens-tech/xlsx';
import {CSV_FILE, XLSX_FILE} from '../../constants/mock-data';
import os from 'os';
import path from 'path';
import fs from 'fs';

type ConnectionsPageProps = BasePageProps;
type FillInputArgs = {name: string; value: string};
type FillFormInputArgs = {id: 'input'} & FillInputArgs;

class ConnectionsPage extends BasePage {
    revisions: Revisions;

    private createQlChartButtonSelector = slct(
        ConnectionsActionPanelControls.CREATE_QL_CHART_BUTTON,
    );

    private createDatasetButtonSelector = slct(
        ConnectionsActionPanelControls.CREATE_DATASET_BUTTON,
    );

    constructor({page}: ConnectionsPageProps) {
        super({page});
        this.revisions = new Revisions(page);
    }

    async checkIsReadonlyState() {
        await this.page.waitForSelector(slct(SharedEntriesBaseQa.OpenOriginalBtn));
    }

    async createQlChart() {
        await this.page.click(this.createQlChartButtonSelector);
    }

    async createDataset() {
        await this.page.click(this.createDatasetButtonSelector);
    }

    async fillCreateConnectionInFolder({name}: {name: string}) {
        const textInput = this.page.locator(slct(EntryDialogQA.PathSelect)).locator('input');
        // type connection name
        await textInput.fill(name);

        // create connection
        await this.page.locator(slct(EntryDialogQA.Apply)).click();
        try {
            await this.page.waitForURL(() => this.page.url().includes(name));
        } catch {
            throw new Error("Connection wasn't created");
        }
    }

    async createConnectionInFolder({name = uuidv1()}: {name?: string} = {}) {
        // open creation dialog
        await this.page.locator(slct(ConnectionsBaseQA.SUBMIT_ACTION_BUTTON)).click();

        await this.fillCreateConnectionInFolder({name});
    }

    async createConnectionInWorkbookOrCollection({
        name = uuidv1(),
        collectionId,
    }: {name?: string; collectionId?: string} = {}) {
        const formSubmit = await this.page.waitForSelector(
            slct(ConnectionsBaseQA.SUBMIT_ACTION_BUTTON),
        );
        // open creation dialog
        await formSubmit.click();
        const textInput = this.page
            .locator(slct(DialogCreateWorkbookEntryQa.Input))
            .locator('input');
        // clear input
        await textInput.press('Meta+A');
        await textInput.press('Backspace');
        // type connection name
        await textInput.fill(name);
        const dialogApplyButton = await this.page.waitForSelector(
            slct(DialogCreateWorkbookEntryQa.ApplyButton),
        );
        // create connection
        await dialogApplyButton.click();
        try {
            if (collectionId) {
                await this.page.waitForURL(() => {
                    return this.page.url().endsWith(collectionId);
                });
            } else {
                await this.page.waitForURL(() => {
                    return this.page.url().includes(name);
                });
            }
            return name;
        } catch {
            throw new Error("Connection wasn't created");
        }
    }

    async checkClientValidationErrors() {
        let hasValidationErrors = false;
        const onConsoleLog = (msg: ConsoleMessage) => {
            if (msg.text().includes('Validation errors')) {
                hasValidationErrors = true;
            }
        };
        this.page.on('console', onConsoleLog);
        const formSubmit = await this.page.waitForSelector(
            slct(ConnectionsBaseQA.SUBMIT_ACTION_BUTTON),
        );
        await formSubmit.click();
        await waitForCondition(async () => {
            return hasValidationErrors;
        });
        this.page.off('console', onConsoleLog);
    }

    getFieldSelector(name: string) {
        return `conn-input-${name}`;
    }

    async fillInput({name, value}: {name: string; value: string}) {
        const selector = this.getFieldSelector(name);
        const input = await this.page.waitForSelector(slct(selector));
        // focus input
        await input.click();
        await input.type(value);
    }

    async fillForm(args: FillFormInputArgs[]) {
        for (const control of args) {
            const {id, name, value} = control;
            switch (id) {
                case 'input': {
                    await this.fillInput({name, value});
                    break;
                }
            }
        }
    }

    async fillAndSubmitAddYadocDialog(fileUrl: string) {
        const fileInput = await this.page.waitForSelector(
            `${slct(ConnectionsYadocsQA.ADD_DOCUMENT_DIALOG_INPUT)} input`,
        );
        await fileInput.click();
        await fileInput.fill(fileUrl);
        const addFileDialogSubmitButton = await this.page.waitForSelector(
            slct(ConnectionsYadocsQA.ADD_DOCUMENT_DIALOG_SUBMIT_BUTTON),
        );
        await addFileDialogSubmitButton.click();
    }

    async applyS3SourceDialogAndForSourcesUpdating() {
        const updateFileSourceRequests: Request[] = [];
        this.page.on('request', (request) => {
            if (request.url().includes('updateFileSource')) {
                updateFileSourceRequests.push(request);
            }
        });
        const submitButton = await this.page.waitForSelector(
            slct(ConnectionsS3BaseQA.S3_SOURCE_DIALOG_SUBMIT_BUTTON),
        );
        await submitButton.click();
        await Promise.all(
            updateFileSourceRequests.map(async (request) => {
                const response = await request.response();
                expect(response?.status()).toBe(200);
            }),
        );
    }

    async saveUpdatedConnection() {
        await this.page.locator(slct(ConnectionsBaseQA.SUBMIT_ACTION_BUTTON)).click();
    }

    async removeConnection() {
        const moreButton = this.page.locator(slct(ActionPanelQA.MoreBtn));
        expect(moreButton).toBeVisible();
        await moreButton.click();

        const menuItemRemove = this.page
            .locator(slct(ActionPanelEntryContextMenuQa.Menu))
            .locator(slct(ActionPanelEntryContextMenuQa.Remove));
        expect(menuItemRemove).toBeVisible();
        await menuItemRemove.click();

        const applyButton = this.page.locator(slct(EntryDialogQA.Apply));
        expect(applyButton).toBeVisible();

        await applyButton.click();
    }

    async disableAutoCreateDashInBillingConnection() {
        const checkbox = await this.page.waitForSelector(
            slct(ConnectionsFormQA.AUTO_CREATE_DASH_CHECKBOX),
        );
        await checkbox.click();
    }

    async uploadCsvFile({
        fileName = `${uuidv1()}.csv`,
        file = CSV_FILE,
    }: {fileName?: string; file?: string[][]} = {}) {
        // make file
        os.tmpdir();
        const csvFile = path.join(os.tmpdir(), fileName);
        fs.writeFileSync(csvFile, file.map((row) => row.join(',')).join('\n'));

        await this.uploadS3File(csvFile);

        return {fileName, file};
    }

    async uploadXlsxFile({
        fileName = `${uuidv1()}.xlsx`,
        sheets = XLSX_FILE,
    }: {fileName?: string; sheets?: Record<string, string[][]>} = {}) {
        // make file
        const workbook = XLSX.utils.book_new();

        for (const [sheetName, data] of Object.entries(sheets)) {
            const worksheet = XLSX.utils.aoa_to_sheet(data);
            XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
        }
        const xlsxFile = path.join(os.tmpdir(), fileName);
        XLSX.writeFile(workbook, xlsxFile);

        await this.uploadS3File(xlsxFile);
        return {fileName, sheets};
    }

    private async uploadS3File(file: string) {
        const onFileChoose = (fileChooser: FileChooser) => {
            fileChooser.setFiles([file]);
        };
        this.page.on('filechooser', onFileChoose);
        await this.page.click(slct(ConnectionsBaseQA.S3_UPLOAD_BUTTON));
        this.page.off('filechooser', onFileChoose);
    }
}

export default ConnectionsPage;
