import { ensureUserExists, login } from './helpers/login'
import { isExcludedBySharding, startWith } from './helpers/config'
import { runScript } from './helpers/hostAdminClient'
import { createProject, openProjectByName } from './helpers/project'
import { prepareWaitForNextCompileSlot } from './helpers/compile'
import { v4 as uuid } from 'uuid'

const USER = 'user@example.com'
const PROJECT_NAME = 'Old Project'

describe('Upgrading', function () {
  if (isExcludedBySharding('PRO_CUSTOM_3')) return

  let recompile: () => void
  let waitForCompile: (triggerCompile: () => void) => void

  function testUpgrade(
    steps: {
      version: string
      vars?: Object
      newProjectButtonMatcher?: RegExp
      hook?: () => void
    }[]
  ) {
    const startOptions = steps.shift()!

    before(async function () {
      cy.log('Create old instance')
    })
    startWith({
      pro: true,
      version: startOptions.version,
      withDataDir: true,
      resetData: true,
      vars: startOptions.vars,
    })
    before(function () {
      cy.log('Create initial user after deleting it')
    })
    ensureUserExists({ email: USER })
    before(function () {
      cy.log('Populate old instance')
      login(USER)
      ;({ recompile, waitForCompile } = prepareWaitForNextCompileSlot())
      waitForCompile(() => {
        createProject(PROJECT_NAME, {
          newProjectButtonMatcher: startOptions.newProjectButtonMatcher,
        })
      })

      cy.log('Wait for successful compile')
      cy.findByLabelText(/Page.*1/i).findByText(PROJECT_NAME)

      cy.log('Increment the doc version three times')
      for (let i = 0; i < 3; i++) {
        cy.log('Add content')
        cy.findByText('\\maketitle').parent().click()
        cy.findByText('\\maketitle')
          .parent()
          .type(`\n\\section{{}Old Section ${i}}`)

        cy.log('Trigger full flush')
        recompile()
        cy.findByRole('navigation', {
          name: 'Project Layout, Sharing, and Submission',
        })
          .findByRole('button', { name: 'Menu' })
          .click()
        cy.findByRole('link', { name: 'Source' }).click()
        cy.get('body').type('{esc}')
      }

      cy.log('Check compile and history')
      for (let i = 0; i < 3; i++) {
        cy.findByLabelText(/Page.*1/i).findByText(`Old Section ${i}`)
      }
      cy.findByRole('button', { name: 'History' }).click()
      for (let i = 0; i < 3; i++) {
        cy.findByText(new RegExp(`\\\\section{Old Section ${i}}`))
      }
    })

    for (const step of steps) {
      before(function () {
        cy.log(`Upgrade to version ${step.version}`)

        // Navigate way from editor to avoid redirect to /login when the next instance comes up (which slows down tests)
        cy.visit('/project', {})
      })
      before(async function () {
        cy.log('Graceful shutdown: flush all the things')
        this.timeout(20 * 1000)
        // Ideally we could use the container shutdown procedure, but it's too slow and unreliable for tests.
        // TODO(das7pad): adopt the below after speeding up the graceful shutdown procedure on all supported releases
        // await dockerCompose('stop', 'sharelatex')

        // For now, we are stuck with manually flushing things
        await runScript({
          cwd: 'services/document-updater',
          script: 'scripts/flush_all.js',
        })
        await runScript({
          cwd: 'services/project-history',
          script: 'scripts/flush_all.js',
        })
      })
      startWith({
        pro: true,
        version: step.version,
        vars: step.vars,
        withDataDir: true,
      })

      step.hook?.()
    }
    beforeEach(function () {
      login(USER)
    })

    it('should list the old project', function () {
      cy.visit('/project')
      cy.findByRole('link', { name: PROJECT_NAME })
    })

    it('should open the old project', function () {
      waitForCompile(() => {
        openProjectByName(PROJECT_NAME)
      })

      cy.url().should('match', /\/project\/[a-fA-F0-9]{24}/)
      cy.findByRole('navigation', {
        name: 'Project actions',
      }).within(() => {
        cy.findByText(PROJECT_NAME)
      })

      cy.log('wait for successful compile')
      cy.findByLabelText(/Page.*1/i).findByText(PROJECT_NAME)
      cy.findByLabelText(/Page.*1/i).findByText('Old Section 2')

      cy.log('Add more content')
      const newSection = `New Section ${uuid()}`
      cy.findByText('\\maketitle').parent().click()
      cy.findByText('\\maketitle').parent().type(`\n\\section{{}${newSection}}`)

      cy.log('Check compile and history')
      recompile()
      cy.findByLabelText(/Page.*1/i).findByText(newSection)
      cy.findByRole('button', { name: 'History' }).click()
      cy.findByText(/\\section\{Old Section 2}/)
      cy.findByText(new RegExp(`\\\\section\\{${newSection}}`))
    })
  }

  const optionsFourDotTwo = {
    version: '4.2',
    vars: {
      // Add core vars with old branding
      SHARELATEX_SITE_URL: 'http://sharelatex',
      SHARELATEX_MONGO_URL: 'mongodb://mongo/sharelatex',
      SHARELATEX_REDIS_HOST: 'redis',
    },
    newProjectButtonMatcher: /create first project/i,
  }
  const optionsBinaryFilesMigration = {
    version: '5.5.5',
    hook() {
      before(async function () {
        await runScript({
          cwd: 'services/history-v1',
          script: 'storage/scripts/back_fill_file_hash.mjs',
          args: ['--all'],
        })
      })
    },
  }
  describe('from 4.2 to latest', function () {
    testUpgrade([
      optionsFourDotTwo,
      optionsBinaryFilesMigration,
      { version: 'latest' },
    ])
  })
  describe('from 5.0 to latest', function () {
    testUpgrade([
      { version: '5.0' },
      optionsBinaryFilesMigration,
      { version: 'latest' },
    ])
  })
})
