import { arrayBufferToBase64 } from './../util/helper-functions'
import * as moment from 'moment'

const username = Cypress.env('username')
const password = Cypress.env('password')
const hostUrl = Cypress.env('hosturl')
const appLocation = Cypress.env('appLocation')
const longerCommandTimeout = Cypress.env('longerCommandTimeout')
const fixturePath = 'excels_general/'
const serverType = Cypress.env('serverType')
const site_id = Cypress.env(`site_id_${serverType}`)
const libraryToOpenIncludes = Cypress.env(`libraryToOpenIncludes_${serverType}`)
const testLicenceUserLimits = Cypress.env('testLicenceUserLimits')

/** IMPORTANT NOTICE
 * Before running tests, make sure that table `MPE_USERS` is present
 */

interface EditConfigTableCells {
  varName: string
  varValue: string
}

context('licensing tests: ', function () {
  this.beforeAll(() => {
    cy.loginAndUpdateValidKey()
  })

  this.beforeEach(() => {
    cy.visit(hostUrl + appLocation)

    visitPage('home')
  })

  it('1 | key valid, not expired', (done) => {
    let keyData = {
      valid_until: moment().add(1, 'year').format('YYYY-MM-DD'),
      users_allowed: 4,
      hot_license_key: '',
      demo: false,
      site_id: site_id
    }

    let keys: { licenseKey: any; activationKey: any }
    generateKeys(keyData, (keysGen: any) => {
      keys = keysGen

      cy.wait(2000)

      isLicensingPage((result: boolean) => {
        if (result) {
          inputLicenseKeyPage(keys.licenseKey, keys.activationKey)

          cy.wait(2000)
        }

        acceptTermsIfPresented((result: boolean) => {
          if (result) {
            cy.wait(10000)
          }

          visitPage('home')

          cy.get('.nav-tree clr-tree > clr-tree-node', {
            timeout: longerCommandTimeout
          }).then((treeNodes: any) => {
            done()
          })
        })
      })
    })
  })

  it('2 | Key will expire in less then 14 days, not free tier', (done) => {
    // make 2 separate for this one
    let keyData = {
      valid_until: moment().add(10, 'day').format('YYYY-MM-DD'),
      users_allowed: 4,
      hot_license_key: '',
      demo: false,
      site_id: site_id
    }

    let keys: { licenseKey: any; activationKey: any }
    generateKeys(keyData, (keysGen: any) => {
      keys = keysGen
      console.log('keys', keys)

      cy.wait(2000)

      acceptTermsIfPresented((result: boolean) => {
        if (result) {
          cy.wait(20000)
        }
        updateLicenseKeyQuick(keysGen, () => {
          cy.wait(2000)
          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            verifyLicensingWarning('This license key will expire in ', () => {
              done()
            })
          })
        })
      })
    })
  })

  it('3 | key expired, free tier works', (done) => {
    let keyData = {
      valid_until: moment().subtract(1, 'day').format('YYYY-MM-DD'),
      users_allowed: 4,
      hot_license_key: '',
      demo: false,
      site_id: site_id
    }

    let keys: { licenseKey: any; activationKey: any }
    generateKeys(keyData, (keysGen: any) => {
      keys = keysGen

      cy.wait(2000)

      acceptTermsIfPresented((result: boolean) => {
        if (result) {
          cy.wait(20000)
        }
        cy.wait(2000)
        updateLicenseKeyQuick(keysGen, () => {
          cy.wait(2000)
          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            verifyLicensingPage(
              'Licence key is expired - please contact',
              (success: boolean) => {
                if (success) {
                  verifyLicensingWarning(
                    '(FREE Tier) - Problem with licence',
                    () => {
                      done()
                    }
                  )
                }
              }
            )
          })
        })
      })
    })
  })

  it('4 | key invalid, free tier works', (done) => {
    let keyData = {
      valid_until: moment().subtract(1, 'day').format('YYYY-MM-DD'),
      users_allowed: 4,
      hot_license_key: '',
      demo: false,
      site_id: site_id
    }

    let keys: { licenseKey: any; activationKey: any }
    generateKeys(keyData, (keysGen: any) => {
      keys = keysGen
      keys.activationKey = 'invalid' + keys.activationKey

      cy.wait(2000)

      acceptTermsIfPresented((result: boolean) => {
        if (result) {
          cy.wait(20000)
        }
        cy.wait(2000)
        updateLicenseKeyQuick(keysGen, () => {
          cy.wait(2000)
          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            verifyLicensingPage(
              'Licence key is invalid - please contact',
              (success: boolean) => {
                if (success) {
                  verifyLicensingWarning(
                    '(FREE Tier) - Problem with licence',
                    () => {
                      done()
                    }
                  )
                }
              }
            )
          })
        })
      })
    })
  })

  it('5 | key for wrong organisation, free tier works', (done) => {
    let keyData = {
      valid_until: moment().add(1, 'year').format('YYYY-MM-DD'),
      users_allowed: 4,
      hot_license_key: '',
      demo: false,
      site_id: 100
    }

    let keys: { licenseKey: any; activationKey: any }
    generateKeys(keyData, (keysGen: any) => {
      keys = keysGen
      keys.activationKey = keys.activationKey

      cy.wait(2000)

      acceptTermsIfPresented((result: boolean) => {
        if (result) {
          cy.wait(20000)
        }
        cy.wait(2000)
        updateLicenseKeyQuick(keysGen, () => {
          cy.wait(2000)
          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            verifyLicensingPage(
              'SYSSITE (below) is not found',
              (success: boolean) => {
                if (success) {
                  verifyLicensingWarning(
                    '(FREE Tier) - Problem with licence',
                    () => {
                      done()
                    }
                  )
                }
              }
            )
          })
        })
      })
    })
  })

  if (testLicenceUserLimits) {
    it('4 | User try to register when limit is reached', (done) => {
      let keyData = {
        valid_until: moment().add(1, 'month').format('YYYY-MM-DD'),
        users_allowed: 10,
        hot_license_key: '',
        demo: false,
        site_id: site_id
      }
      let keyData2 = {
        valid_until: moment().add(1, 'month').format('YYYY-MM-DD'),
        users_allowed: 1,
        hot_license_key: '',
        demo: false,
        site_id: site_id
      }
      generateKeys(keyData, (keysGen: any) => {
        generateKeys(keyData2, (keysGen2: any) => {
          cy.wait(2000)

          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            updateLicenseKeyQuick(keysGen, () => {
              cy.wait(2000)
              acceptTermsIfPresented((result: boolean) => {
                if (result) {
                  cy.wait(20000)
                }

                updateLicenseKeyQuick(keysGen2, () => {
                  cy.wait(2000)

                  const random = Cypress._.random(0, 1000)
                  const newUser = {
                    username: `randomusername${random}notregistered`,
                    last_seen_at: moment().add(1, 'month').format('YYYY-MM-DD'),
                    registered_at: moment().add(1, 'month').format('YYYY-MM-DD')
                  }
                  updateUsersTable(
                    { deleteAll: true, newUsers: [newUser] },
                    () => {
                      logout(() => {
                        cy.visit(hostUrl + appLocation)
                        // cy.get('input.username').type(username)
                        // cy.get('input.password').type(password)
                        // cy.get('.login-group button').click()

                        visitPage('home')

                        cy.wait(2000)

                        verifyLicensingPage(
                          'The registered number of users reached the limit specified for your licence.',
                          (success: boolean) => {
                            if (success) done()
                          }
                        )
                      })
                    }
                  )
                })
              })
            })
          })
        })
      })
    })

    it('5 | Show warning banner when limit is exceeded', (done) => {
      let keyData = {
        valid_until: moment().add(1, 'month').format('YYYY-MM-DD'),
        users_allowed: 10,
        hot_license_key: '',
        demo: false,
        site_id: site_id
      }
      let keyData2 = {
        valid_until: moment().add(1, 'month').format('YYYY-MM-DD'),
        users_allowed: 1,
        hot_license_key: '',
        demo: false,
        site_id: site_id
      }

      generateKeys(keyData, (keysGen: any) => {
        generateKeys(keyData2, (keysGen2: any) => {
          cy.wait(2000)
          acceptTermsIfPresented((result: boolean) => {
            if (result) {
              cy.wait(20000)
            }
            updateLicenseKeyQuick(keysGen, () => {
              cy.wait(2000)
              acceptTermsIfPresented((result: boolean) => {
                if (result) {
                  cy.wait(20000)
                }
                const random = Cypress._.random(0, 1000)
                const newUser = {
                  username: `randomusername${random}`,
                  last_seen_at: moment().add(1, 'month').format('YYYY-MM-DD'),
                  registered_at: moment().add(1, 'month').format('YYYY-MM-DD')
                }
                updateUsersTable(
                  { deleteAll: true, keep: username, newUsers: [newUser] },
                  () => {
                    updateLicenseKeyQuick(keysGen2, () => {
                      cy.wait(2000)
                      verifyLicensingWarning(
                        'The registered number of users exceeds the limit specified for your license.',
                        () => {
                          done()
                        }
                      )
                    })
                  }
                )
              })
            })
          })
        })
      })
    })
  }
})

const logout = (callback?: any) => {
  cy.get('.header-actions .dropdown-toggle')
    .click()
    .then(() => {
      cy.get('.header-actions .dropdown-menu > .separator')
        .next()
        .click()
        .then(() => {
          if (callback) callback()
        })
    })
}

const acceptTermsIfPresented = (callback?: any) => {
  cy.url().then((url: string) => {
    if (url.includes('licensing/register')) {
      cy.get('.card-block')
        .scrollTo('bottom')
        .then(() => {
          cy.get('#checkbox1')
            .click()
            .then(() => {
              if (callback) callback(true)
            })
        })
    } else {
      if (callback) callback(false)
    }
  })
}

const isLicensingPage = (callback: any) => {
  return cy.url().then((url: string) => {
    callback(
      url.includes('#/licensing/') && !url.includes('licensing/register')
    )
  })
}

const verifyLicensingPage = (text: string, callback: any) => {
  // visitPage('home')

  cy.wait(1000)
  isLicensingPage((result: boolean) => {
    if (result) {
      cy.get('p.key-error')
        .should('contain', text)
        .then((treeNodes: any) => {
          callback(true)
        })
    }
  })
}

const verifyLicensingWarning = (text: string, callback: any) => {
  visitPage('home')

  cy.wait(1000)
  cy.get("div[role='alert'] .alert-text")
    .invoke('text')
    .should('contain', text)
    .then(() => {
      callback()
    })
}

const inputLicenseKeyPage = (licenseKey: string, activationKey: string) => {
  cy.get('button').contains('Paste licence').click()

  cy.get('.license-key-form textarea', { timeout: longerCommandTimeout })
    .invoke('val', licenseKey)
    .trigger('input')
    .should('not.be.undefined')
  cy.get('.activation-key-form textarea', { timeout: longerCommandTimeout })
    .invoke('val', activationKey)
    .trigger('input')
    .should('not.be.undefined')
  cy.get('button.apply-keys').click()
}

const updateUsersTable = (options: any, callback?: any) => {
  visitPage('home')
  openTableFromTree(libraryToOpenIncludes, 'mpe_users')

  clickOnEdit(() => {
    cy.get('.ht_master tbody tr').then((rows: any) => {
      if (options.deleteAll) {
        for (let row of rows) {
          const user_id = row.childNodes[2]
          if (!options.keep || user_id.innerText !== options.keep) {
            cy.get(row.childNodes[1])
              .dblclick()
              .then(() => {
                cy.focused().type('{selectall}').type('Yes').type('{enter}')
              })
          }
        }
      }
      if (options.newUsers && options.newUsers.length) {
        for (let newUser of options.newUsers) {
          clickOnAddRow(() => {
            cy.get('#hotInstance tbody tr:last-child').then((rows: any) => {
              cy.get(rows[0].childNodes[2])
                .dblclick()
                .then(() => {
                  cy.focused()
                    .type('{selectall}')
                    .type(newUser.username)
                    .type('{enter}')
                })
              // cy.get(rows[0].childNodes[3])
              //   .dblclick()
              //   .then(() => {
              //     cy.focused()
              //       .type('{selectall}')
              //       .type(newUser.last_seen_at)
              //       .type('{enter}')
              //   })
              // cy.get(rows[0].childNodes[4])
              //   .dblclick()
              //   .then(() => {
              //     cy.focused()
              //       .type('{selectall}')
              //       .type(newUser.registered_at)
              //       .type('{enter}')
              //   })
              submitTable(() => {
                cy.wait(2000)
                approveTable(callback)
              })
            })
          })
        }
      }
    })
  })
}

const changeLicenseKeyTable = (keys: any, callback?: any) => {
  visitPage('home')
  openTableFromTree(libraryToOpenIncludes, 'mpe_config')

  clickOnEdit(() => {
    editTableField(
      [
        { varName: 'DC_ACTIVATION_KEY', varValue: keys.activationKey },
        { varName: 'DC_LICENCE_KEY', varValue: keys.licenseKey }
      ],
      () => {
        submitTable(() => {
          cy.wait(2000)
          approveTable(() => {
            cy.reload()
            if (callback) callback()
          })
        })
      }
    )
  })
}

const updateLicenseKeyQuick = (keys: any, callback: any) => {
  isLicensingPage((result: boolean) => {
    if (!result) {
      visitPage('licensing/update')
      cy.wait(2000)
    }
    inputLicenseKeyPage(keys.licenseKey, keys.activationKey)

    callback()
  })
}

const generateKeys = async (licenseData: any, resultCallback?: any) => {
  let keyPair = await window.crypto.subtle.generateKey(
    {
      name: 'RSA-OAEP',
      modulusLength: 2024,
      publicExponent: new Uint8Array([1, 0, 1]),
      hash: 'SHA-256'
    },
    true,
    ['encrypt', 'decrypt']
  )

  const encoded = new TextEncoder().encode(JSON.stringify(licenseData))

  const cipher = await window.crypto.subtle
    .encrypt(
      {
        name: 'RSA-OAEP'
      },
      keyPair.publicKey,
      encoded
    )
    .then(
      (value) => {
        return value
      },
      (err) => {
        console.log('Encrpyt error', err)
      }
    )

  if (!cipher) {
    alert('Encryptin keys failed')
    throw new Error('Encryptin keys failed')
  }

  const privateKeyBytes = await window.crypto.subtle.exportKey(
    'pkcs8',
    keyPair.privateKey
  )

  const activationKey = await arrayBufferToBase64(privateKeyBytes)
  const licenseKey = await arrayBufferToBase64(cipher)

  if (resultCallback)
    resultCallback({
      activationKey,
      licenseKey
    })
}

const editTableField = (edits: EditConfigTableCells[], callback?: any) => {
  cy.get('td').then((tdNodes: any) => {
    for (let edit of edits) {
      let correctRow = false

      for (let node of tdNodes) {
        if (correctRow) {
          cy.get(node)
            .dblclick()
            .then(() => {
              // textarea update on long keys
              cy.focused().invoke('val', edit.varValue).type('{enter}')
            })

          correctRow = false
          break
        }

        if (node.innerText.includes(edit.varName)) {
          correctRow = true
        }
      }
    }

    if (callback) callback()
  })
}

const openTableFromTree = (
  libNameIncludes: string,
  tablename: string,
  callback?: any
) => {
  cy.get('.app-loading', { timeout: longerCommandTimeout })
    .should('not.exist')
    .then(() => {
      cy.get('.nav-tree clr-tree > clr-tree-node', {
        timeout: longerCommandTimeout
      }).then((treeNodes: any) => {
        let viyaLib

        for (let node of treeNodes) {
          if (node.innerText.toLowerCase().includes(libNameIncludes)) {
            viyaLib = node
            break
          }
        }

        cy.get(viyaLib).within(() => {
          cy.get('.clr-tree-node-content-container > button').click()

          cy.get('.clr-treenode-link').then((innerNodes: any) => {
            for (let innerNode of innerNodes) {
              if (innerNode.innerText.toLowerCase().includes(tablename)) {
                innerNode.click()
                if (callback) callback()
                break
              }
            }
          })
        })
      })
    })
}

const clickOnAddRow = (callback?: any) => {
  cy.get('.btnCtrl button.btn-success')
    .click()
    .then(() => {
      if (callback) callback()
    })
}

const clickOnEdit = (callback?: any) => {
  cy.get('.btnCtrl button.btn-primary', { timeout: longerCommandTimeout })
    .click()
    .then(() => {
      if (callback) callback()
    })
}

const submitTable = (callback?: any) => {
  cy.get('.btnCtrl button.btn-primary', { timout: longerCommandTimeout })
    .click()
    .then(() => {
      cy.get(".modal.ng-star-inserted button[type='submit']")
        .click()
        .then(() => {
          if (callback) callback()
        })
    })
}

const approveTable = (callback?: any) => {
  cy.get('button', { timeout: longerCommandTimeout })
    .should('contain', 'Approve')
    .then((allButtons: any) => {
      for (let approvalButton of allButtons) {
        if (approvalButton.innerText.toLowerCase().includes('approve')) {
          approvalButton.click()
          break
        }
      }

      cy.get('button#acceptBtn', { timeout: longerCommandTimeout })
        .should('exist')
        .should('not.be.disabled')
        .click()
        .then(() => {
          cy.get('app-history', { timeout: longerCommandTimeout })
            .should('exist')
            .then(() => {
              if (callback) callback()
            })
        })
    })
}

const visitPage = (url: string) => {
  cy.visit(`${hostUrl}${appLocation}/#/${url}`)
}
