import { expect } from '@playwright/test';
import { test } from '../../fixtures/tmpdir.fixture';
import { EditPage } from '../../pages/edit.page';
import { ReviewPage } from '../../pages/review.page';
import { RevisionPage } from '../../pages/revision.page';
import { SearchPage } from '../../pages/search.page';
import { BulkSubmissionPage, SingleSequenceSubmissionPage } from '../../pages/submission.page';
import {
    EBOLA_SUDAN_SHORT_SEQUENCE,
    EBOLA_SUDAN_SMALL_FASTQ,
    EBOLA_SUDAN_MEDIUM_FASTQ,
} from '../../test-helpers/test-data';

const ORGANISM_NAME = 'Ebola Sudan';
const ORGANISM_URL_NAME = 'ebola-sudan';
const RAW_READS = 'rawReads';
const METADATA_HEADERS = [
    'submissionId',
    'geoLocCountry',
    'sampleCollectionDate',
    'sequencingInstrument',
];
const AUTHOR_AFFILIATIONS = 'Test Institute';
const COUNTRY_1 = 'Norway';
const COUNTRY_2 = 'Uganda';
const SEQUENCING_INSTRUMENT = 'Illumina MiSeq';
const ID_1 = 'sub1';
const ID_2 = 'sub2';
const FILES_SINGLE = { 'testfile.fastq': EBOLA_SUDAN_SMALL_FASTQ(1) };
const FILES_DOUBLE = {
    'file1.fastq': EBOLA_SUDAN_SMALL_FASTQ(1),
    'file2.fastq': EBOLA_SUDAN_SMALL_FASTQ(2),
};

test('submit single seq w/ 2 FASTQ files thru single seq submission form', async ({
    page,
    groupId,
    tmpDir,
}) => {
    test.setTimeout(200_000);
    void groupId;
    const submissionPage = new SingleSequenceSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.fillSubmissionForm({
        submissionId: ID_1,
        collectionCountry: COUNTRY_1,
        collectionDate: '2023-10-15',
        authorAffiliations: AUTHOR_AFFILIATIONS,
        sequencingInstrument: SEQUENCING_INSTRUMENT,
    });
    await submissionPage.fillSequenceData({ main: EBOLA_SUDAN_SHORT_SEQUENCE });
    await submissionPage.uploadExternalFiles(RAW_READS, FILES_DOUBLE, tmpDir);
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    await reviewPage.checkFilesInReviewDialog(FILES_DOUBLE);
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();
    await searchPage.waitForAndOpenModalByRoleAndName('cell', COUNTRY_1);
    await searchPage.checkAllFileContents(FILES_DOUBLE);
});

test('reject non-FASTQ raw_reads file with a format-validation error', async ({
    page,
    groupId,
    tmpDir,
}) => {
    test.setTimeout(200_000);
    void groupId;
    const submissionPage = new SingleSequenceSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.fillSubmissionForm({
        submissionId: 'invalid-format',
        collectionCountry: COUNTRY_1,
        collectionDate: '2023-11-01',
        authorAffiliations: AUTHOR_AFFILIATIONS,
        sequencingInstrument: SEQUENCING_INSTRUMENT,
    });
    await submissionPage.fillSequenceData({ main: EBOLA_SUDAN_SHORT_SEQUENCE });
    await submissionPage.uploadExternalFiles(
        RAW_READS,
        { 'reads.fastq': 'This is not a FASTQ file.' },
        tmpDir,
    );
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    await reviewPage.expectFileProcessingError(/This is not a FASTQ file./i);
    await reviewPage.expectNoValidSequencesToApprove();
});

test('bulk submit 2 seqs with 1 & 2 FASTQ files respectively', async ({
    page,
    groupId,
    tmpDir,
}) => {
    test.setTimeout(240_000);
    void groupId;
    const submissionPage = new BulkSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.uploadMetadataFile(METADATA_HEADERS, [
        [ID_1, COUNTRY_1, '2022-12-02', SEQUENCING_INSTRUMENT],
        [ID_2, COUNTRY_2, '2022-12-13', SEQUENCING_INSTRUMENT],
    ]);
    await submissionPage.uploadSequencesFile({
        [ID_1]: EBOLA_SUDAN_SHORT_SEQUENCE,
        [ID_2]: EBOLA_SUDAN_SHORT_SEQUENCE,
    });
    await submissionPage.uploadExternalFiles(
        RAW_READS,
        { [ID_1]: FILES_SINGLE, [ID_2]: FILES_DOUBLE },
        tmpDir,
    );
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();
    await searchPage.checkFileContentInModal('cell', COUNTRY_1, FILES_SINGLE);
    await searchPage.checkFileContentInModal('cell', COUNTRY_2, FILES_DOUBLE);
});

test('bulk submit 1 seq: discarding and reading a FASTQ file', async ({
    page,
    groupId,
    tmpDir,
}) => {
    test.setTimeout(240_000);
    void groupId;
    const submissionPage = new BulkSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.uploadMetadataFile(METADATA_HEADERS, [
        [ID_1, COUNTRY_1, '2023-01-01', SEQUENCING_INSTRUMENT],
    ]);
    await submissionPage.uploadSequencesFile({ [ID_1]: EBOLA_SUDAN_SHORT_SEQUENCE });
    await submissionPage.uploadExternalFiles(RAW_READS, { [ID_1]: FILES_SINGLE }, tmpDir);
    await submissionPage.discardFiles(RAW_READS);
    await submissionPage.uploadExternalFiles(RAW_READS, { [ID_1]: FILES_DOUBLE }, tmpDir);
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    await reviewPage.checkFilesInReviewDialog(FILES_DOUBLE, Object.keys(FILES_SINGLE));
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();
    await searchPage.checkFileContentInModal('cell', COUNTRY_1, FILES_DOUBLE);
});

test('bulk submit 1 seq with a 35 MB FASTQ file', async ({ page, groupId, tmpDir }) => {
    test.setTimeout(400_000);
    void groupId;

    // With 10 MB per part, 35 MB will require 4 parts, allowing us to check that the multipart
    // upload functions as expected.
    const FILE_SIZE_MB = 35_000_000;
    const PATTERN = EBOLA_SUDAN_SMALL_FASTQ(1, 1);
    const REPEATS = FILE_SIZE_MB / PATTERN.length;
    const largeFileContent = [];
    for (let i = 0; i < REPEATS; i++) {
        largeFileContent.push(EBOLA_SUDAN_SMALL_FASTQ(1, i + 1));
    }
    const LARGE_FILE = { 'large_file.fastq': largeFileContent.join('') };

    const submissionPage = new BulkSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.uploadMetadataFile(METADATA_HEADERS, [
        [ID_1, COUNTRY_1, '2024-01-01', SEQUENCING_INSTRUMENT],
    ]);
    await submissionPage.uploadSequencesFile({ [ID_1]: EBOLA_SUDAN_SHORT_SEQUENCE });
    await submissionPage.uploadExternalFiles(RAW_READS, { [ID_1]: LARGE_FILE }, tmpDir);
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(240_000);
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();
    await searchPage.checkFileContentInModal('cell', COUNTRY_1, LARGE_FILE);
});

const REVISION_METADATA_HEADERS = [
    'accession',
    'submissionId',
    'geoLocCountry',
    'sampleCollectionDate',
    'sequencingInstrument',
];
const REVISION_FILES = { 'revised_file.fastq': EBOLA_SUDAN_MEDIUM_FASTQ(1) };
const REVISION_FILES_2 = { 'another_file.fastq': EBOLA_SUDAN_MEDIUM_FASTQ(2) };

test('bulk revise 2 seqs with files', async ({ page, groupId, tmpDir }) => {
    test.setTimeout(400_000);

    const timestamp = Date.now();
    const id1 = `bulk-rev-1-${timestamp}`;
    const id2 = `bulk-rev-2-${timestamp}`;
    const revId1 = `bulk-rev-updated-1-${timestamp}`;
    const revId2 = `bulk-rev-updated-2-${timestamp}`;

    // Step 1: Submit and release 2 sequences
    const submissionPage = new BulkSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.uploadMetadataFile(METADATA_HEADERS, [
        [id1, COUNTRY_1, '2022-01-01', SEQUENCING_INSTRUMENT],
        [id2, COUNTRY_2, '2022-01-02', SEQUENCING_INSTRUMENT],
    ]);
    await submissionPage.uploadSequencesFile({
        [id1]: EBOLA_SUDAN_SHORT_SEQUENCE,
        [id2]: EBOLA_SUDAN_SHORT_SEQUENCE,
    });
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();

    // Get the accessions of the released sequences
    const accessionVersions = await searchPage.waitForSequencesInSearch(2);
    const accession1 = accessionVersions.find((av) => av.version === 1)?.accession;
    const accession2 = accessionVersions.find(
        (av) => av.version === 1 && av.accession !== accession1,
    )?.accession;
    expect(accession1).toBeDefined();
    expect(accession2).toBeDefined();

    // Step 2: Bulk revise with files
    const revisionPage = new RevisionPage(page);
    await revisionPage.goto(ORGANISM_URL_NAME, groupId);

    // Upload revision metadata (with accession column)
    const revisionMetadata = [
        [accession1, revId1, COUNTRY_1, '2022-02-01', SEQUENCING_INSTRUMENT],
        [accession2, revId2, COUNTRY_2, '2022-02-02', SEQUENCING_INSTRUMENT],
    ];
    await page.getByTestId('metadata_file').setInputFiles({
        name: 'revision_metadata.tsv',
        mimeType: 'text/plain',
        buffer: Buffer.from(
            [
                REVISION_METADATA_HEADERS.join('\t'),
                ...revisionMetadata.map((r) => r.join('\t')),
            ].join('\n'),
        ),
    });

    // Upload the revised consensus sequences (required since Ebola Sudan has consensus sequences enabled)
    const revisedSequencesFasta = [
        `>${revId1}\n${EBOLA_SUDAN_SHORT_SEQUENCE}`,
        `>${revId2}\n${EBOLA_SUDAN_SHORT_SEQUENCE}`,
    ].join('\n');
    await revisionPage.uploadSequenceFile('revised_sequences.fasta', revisedSequencesFasta);

    // Upload files for each revision
    await revisionPage.uploadExternalFiles(
        RAW_READS,
        { [revId1]: REVISION_FILES, [revId2]: REVISION_FILES_2 },
        tmpDir,
    );
    await revisionPage.submitRevision();

    // Step 3: Verify in review page and release
    const reviewPage2 = new ReviewPage(page);
    await reviewPage2.waitForZeroProcessing(180_000);
    await reviewPage2.releaseValidSequences();

    const searchPage2 = new SearchPage(page);
    await page.goto(
        `/${ORGANISM_URL_NAME}/submission/${groupId}/released?column_submissionId=true`,
    );

    // Check that revised sequences have the files
    await searchPage2.checkFileContentInModal('cell', revId1, REVISION_FILES);
    await searchPage2.checkFileContentInModal('cell', revId2, REVISION_FILES_2);
});

test('single revise seq with files via edit page', async ({ page, groupId, tmpDir }) => {
    test.setTimeout(400_000);

    // Step 1: Submit and release a sequence
    const submissionPage = new SingleSequenceSubmissionPage(page);
    await submissionPage.navigateToSubmissionPage(ORGANISM_NAME);
    await submissionPage.fillSubmissionForm({
        submissionId: 'single-rev',
        collectionCountry: COUNTRY_1,
        collectionDate: '2023-01-01',
        authorAffiliations: AUTHOR_AFFILIATIONS,
        sequencingInstrument: SEQUENCING_INSTRUMENT,
    });
    await submissionPage.fillSequenceData({ main: EBOLA_SUDAN_SHORT_SEQUENCE });
    const reviewPage = await submissionPage.submitAndWaitForProcessingDone(180_000);
    const searchPage = await reviewPage.releaseAndGoToReleasedSequences();

    // Step 2: Wait until sequence is available and directly go to revise/edit page
    const accessionVersions = await searchPage.waitForSequencesInSearch(1);
    const editPage = new EditPage(page);
    await editPage.goto(
        ORGANISM_URL_NAME,
        accessionVersions[0].accession,
        accessionVersions[0].version,
    );

    // Step 3: Upload files in the edit page
    await editPage.uploadExternalFiles(RAW_READS, REVISION_FILES, tmpDir);
    const reviewPage2 = await editPage.submitChanges();
    await reviewPage2.waitForZeroProcessing(180_000);
    await reviewPage2.releaseValidSequences();

    // Step 4: Release and verify files
    const searchPage2 = new SearchPage(page);
    await searchPage2.goToReleasedSequences(ORGANISM_URL_NAME, groupId);
    await searchPage2.checkFileContentInModal(
        'link',
        `${accessionVersions[0].accession}.${accessionVersions[0].version + 1}`,
        REVISION_FILES,
    );
});
