// Copyright (C) CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

/// <reference types="cypress" />

context('Requests page', () => {
    const projectName = 'Project for testing requests page';
    const mainLabelName = 'requests_page_label';

    const cloudStorageData = {
        displayName: 'Demo bucket',
        resource: 'public',
        manifest: 'images_with_manifest/manifest.jsonl',
        endpointUrl: Cypress.config('minioUrl'),
    };

    const rectanglePayload = {
        objectType: 'shape',
        labelName: mainLabelName,
        frame: 0,
        type: 'rectangle',
        points: [250, 64, 491, 228],
        occluded: false,
    };

    const attrName = 'requests_attr';
    const imagesCount = 3;
    const imageFileName = `image_${mainLabelName}`;
    const width = 800;
    const height = 800;
    const posX = 10;
    const posY = 10;
    const color = 'gray';
    const archiveName = `${imageFileName}.zip`;
    const archivePath = `cypress/fixtures/${archiveName}`;
    const brokenArchiveName = `${imageFileName}_empty.zip`;
    const brokenArchivePath = `cypress/fixtures/${brokenArchiveName}`;
    const badAnnotationsName = `${imageFileName}_incorrect.xml`;
    const badAnnotationsPath = `cypress/fixtures/${badAnnotationsName}`;
    const imagesFolder = `cypress/fixtures/${imageFileName}`;
    const directoryToArchive = imagesFolder;
    const emptyDirectoryToArchive = `${imagesFolder}_empty`;
    const annotationsArchiveNameLocal = 'requests_annotations_archive_local';
    const annotationsArchiveNameCloud = 'requests_annotations_archive_cloud';
    const backupArchiveName = 'requests_backup';
    const exportFormat = 'CVAT for images';
    let exportFileName;

    const taskName = 'Annotation task for testing requests page';
    const brokenTaskName = 'Broken Annotation task for testing requests page';

    const data = {
        projectID: null,
        taskID: null,
        cloudStorageID: null,
    };

    function checkRequestStatus(
        selector,
        innerCheck,
        {
            checkResourceLink, resourceLink,
        } = { checkResourceLink: true, resourceLink: `/tasks/${data.taskID}` },
    ) {
        cy.contains('.cvat-header-button', 'Requests').click();
        cy.get(selector ? `.cvat-requests-card:contains("${selector}")` : '.cvat-requests-card')
            .first()
            .within(() => {
                innerCheck();
                if (checkResourceLink) {
                    cy.get('.cvat-requests-name').click();
                }
            });

        if (checkResourceLink) {
            cy.get('.cvat-spinner').should('not.exist');

            if (resourceLink) {
                cy.url().should('include', resourceLink);
            } else {
                cy.url().should('include', '/requests');
            }
        }
    }

    function openTask() {
        cy.contains('.cvat-header-button', 'Tasks').should('be.visible').click();
        cy.url().should('include', '/tasks');
        cy.openTask(taskName);
    }

    before(() => {
        cy.visit('/auth/login');
        cy.login();

        cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, mainLabelName, imagesCount);
        cy.createZipArchive(directoryToArchive, badAnnotationsPath);
        cy.createZipArchive(directoryToArchive, archivePath);
        cy.createZipArchive(emptyDirectoryToArchive, brokenArchivePath);

        data.cloudStorageID = cy.attachS3Bucket(cloudStorageData);

        const defaultAttrValue = 'Requests attr';
        const multiAttrParams = false;
        const advancedConfigurationParams = false;
        cy.goToProjectsList();
        cy.createProjects(
            projectName,
            mainLabelName,
            attrName,
            defaultAttrValue,
            multiAttrParams,
            advancedConfigurationParams,
        );
        cy.openProject(projectName);
        cy.url().then((url) => {
            data.projectID = Number(url.split('/').slice(-1)[0].split('?')[0]);

            const forProject = true;
            const attachToProject = true;
            let expectedResult = 'success';
            cy.goToTaskList();
            cy.createAnnotationTask(
                taskName,
                mainLabelName,
                attrName,
                defaultAttrValue,
                archiveName,
                multiAttrParams,
                advancedConfigurationParams,
                forProject,
                attachToProject,
                projectName,
                expectedResult,
            );

            expectedResult = 'fail';
            cy.goToTaskList();
            cy.createAnnotationTask(
                brokenTaskName,
                mainLabelName,
                attrName,
                defaultAttrValue,
                brokenArchiveName,
                multiAttrParams,
                advancedConfigurationParams,
                forProject,
                attachToProject,
                projectName,
                expectedResult,
            );

            cy.goToTaskList();
            cy.openTask(taskName);
            cy.url().then((taskUrl) => {
                data.taskID = Number(taskUrl.split('/').slice(-1)[0].split('?')[0]);
                cy.getJobIDFromIdx(0).then((jobID) => {
                    cy.headlessCreateObjects([rectanglePayload], jobID);
                });
            });
        });
    });

    after(() => {
        cy.contains('.cvat-header-button', 'Cloud Storages').should('be.visible').click();
        cy.url().should('include', '/cloudstorages');
        cy.deleteCloudStorage(cloudStorageData.displayName);

        cy.headlessDeleteProject(data.projectID);
    });

    describe('Requests page. Create a task.', () => {
        beforeEach(() => {
            cy.contains('.cvat-header-button', 'Requests').should('be.visible').click();
            cy.url().should('include', '/requests');
        });

        it('Creating a task creates a request. Correct task can be opened.', () => {
            checkRequestStatus(`Task #${data.taskID}`, () => {
                cy.contains('Create Task').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
            });
        });

        it('Creating a task creates a request. Incorrect task can not be opened.', () => {
            checkRequestStatus('', () => {
                cy.contains('Create Task').should('exist');
                cy.get('.cvat-request-item-progress-failed').should('exist');
            }, { resourceLink: '' });
        });
    });

    describe('Requests page. Export a task.', () => {
        beforeEach(openTask);

        it('Export creates a request. Task can be opened from request. Export can be downloaded after page reload.', () => {
            cy.exportTask({
                type: 'annotations',
                format: exportFormat,
                archiveCustomName: annotationsArchiveNameLocal,
            });

            checkRequestStatus(`Task #${data.taskID}`, () => {
                cy.contains('Export Annotations').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
                cy.contains('Expires').should('exist');
                cy.contains(exportFormat).should('exist');
            });

            cy.visit('/requests');
            cy.downloadExport({ expectNotification: false }).then((file) => {
                cy.verifyDownload(file);
                exportFileName = file;
            });
        });

        it('Export on cloud storage creates a request. Expire field does not exist.', () => {
            cy.exportTask({
                type: 'annotations',
                format: exportFormat,
                archiveCustomName: annotationsArchiveNameCloud,
                targetStorage: {
                    location: 'Cloud storage',
                    cloudStorageId: data.cloudStorageID,
                },
                useDefaultLocation: false,
            });
            cy.waitForFileUploadToCloudStorage();

            checkRequestStatus(`Task #${data.taskID}`, () => {
                cy.contains('Export Annotations').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
                cy.contains('Expires').should('not.exist');
                cy.contains(exportFormat).should('exist');
            });
        });
    });

    describe('Requests page. Import a task.', () => {
        beforeEach(() => {
            openTask();
            cy.clickInTaskMenu('Upload annotations', true);
        });

        it('Import creates a request. Task can be opened from request.', () => {
            cy.uploadAnnotations({
                format: exportFormat.split(' ')[0],
                filePath: exportFileName,
                confirmModalClassName: '.cvat-modal-content-load-task-annotation',
                waitAnnotationsGet: false,
            });

            checkRequestStatus(`Task #${data.taskID}`, () => {
                cy.contains('Import Annotations').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
            });
        });

        it('Import creates a request. Task can be opened from incorrect request.', () => {
            cy.uploadAnnotations({
                format: exportFormat.split(' ')[0],
                filePath: badAnnotationsName,
                confirmModalClassName: '.cvat-modal-content-load-task-annotation',
                waitAnnotationsGet: false,
                expectedResult: 'fail',
            });

            checkRequestStatus(`Task #${data.taskID}`, () => {
                cy.contains('Import Annotations').should('exist');
                cy.get('.cvat-request-item-progress-failed').should('exist');
            });
        });
    });

    describe('Requests page. Project backup.', () => {
        beforeEach(() => {
            cy.contains('.cvat-header-button', 'Projects').should('be.visible').click();
            cy.url().should('include', '/projects');
        });

        it('Export backup creates a request. Project can be opened.', () => {
            cy.backupProject(
                projectName,
                backupArchiveName,
            );

            checkRequestStatus(`Project #${data.projectID}`, () => {
                cy.contains('Export Backup').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
                cy.contains('Expires').should('exist');
            }, `/projects/${data.projectID}`);
            cy.downloadExport().then((file) => {
                cy.verifyDownload(file);
            });
        });

        it('Import backup creates a request. Project can not be opened.', () => {
            cy.restoreProject(
                `${backupArchiveName}.zip`,
            );

            checkRequestStatus('', () => {
                cy.contains('Import Backup').should('exist');
                cy.get('.cvat-request-item-progress-success').should('exist');
                cy.get('.cvat-requests-name').should('not.exist');
            }, { checkResourceLink: false });

            cy.goToProjectsList();
            cy.openProject(projectName);
            cy.deleteProjectViaActions(projectName);
        });
    });

    describe('Regression tests', () => {
        beforeEach(openTask);

        it('Export job in different formats from task page simultaneously.', () => {
            cy.intercept('GET', '/api/requests/**', (req) => {
                req.on('response', (res) => {
                    res.setDelay(5000);
                });
            });

            cy.getJobIDFromIdx(0).then((jobID) => {
                const closeExportNotification = () => {
                    cy.get('.ant-notification-notice').first().within((notification) => {
                        cy.contains('Export is finished').should('be.visible');
                        cy.get('span[aria-label="close"]').click();
                        cy.wrap(notification).should('not.exist');
                    });
                };

                const exportParams = {
                    type: 'dataset',
                    format: exportFormat,
                    archiveCustomName: 'job_annotations_cvat',
                    jobOnTaskPage: jobID,
                };

                cy.exportJob(exportParams);
                const newExportParams = {
                    ...exportParams,
                    format: 'COCO',
                    archiveCustomName: 'job_annotations_coco',
                };
                cy.exportJob(newExportParams);

                closeExportNotification();
                closeExportNotification();

                cy.contains('.cvat-header-button', 'Requests').should('be.visible').click();
                cy.url().should('include', '/requests');

                cy.get(`.cvat-requests-card:contains("Job #${jobID}")`)
                    .should('have.length', 2)
                    .each((card) => {
                        cy.wrap(card).within(() => {
                            cy.get('.cvat-request-item-progress-success').should('exist');
                        });
                    });
            });
        });

        it('Export task. Request for status fails, UI is not crushing', () => {
            cy.intercept('GET', '/api/requests/**', {
                statusCode: 500,
                body: 'Network error',
            });

            cy.exportTask({
                type: 'annotations',
                format: exportFormat,
                archiveCustomName: annotationsArchiveNameLocal,
            });

            cy.contains('Could not export dataset').should('be.visible');
            cy.closeNotification('.ant-notification-notice-error');

            cy.contains('.cvat-header-button', 'Requests').should('be.visible').click();
            cy.get('.cvat-requests-page').should('be.visible');
        });
    });
});
