import request = require('request')
import axios from 'axios'
import DockerApi from '../../docker/DockerApi'
import CaptainConstants from '../../utils/CaptainConstants'
import Logger from '../../utils/Logger'
import DockerRegistryHelper from '../DockerRegistryHelper'

class VersionManager {
    private dockerApi: DockerApi

    constructor() {
        const dockerApi = DockerApi.get()
        this.dockerApi = dockerApi
    }
    private getCaptainImageTagsFromOfficialApi(
        currentVersion: string
    ): Promise<{
        currentVersion: string
        latestVersion: string
        changeLogMessage: string
        canUpdate: boolean
    }> {
        // reach out to api.v2.caprover.com/v2/versionInfo?currentVersion=1.5.3
        // response should be currentVersion, latestVersion, canUpdate, and changeLogMessage

        return Promise.resolve() //
            .then(function () {
                return axios.get('https://api-v1.caprover.com/v2/versionInfo', {
                    params: {
                        currentVersion: currentVersion,
                    },
                })
            })
            .then(function (responseObj) {
                const resp = responseObj.data

                if (resp.status !== 100) {
                    throw new Error(
                        `Bad response from the upstream version info: ${resp.status}`
                    )
                }

                const data = resp.data

                return {
                    currentVersion: data.currentVersion + '',
                    latestVersion: data.latestVersion + '',
                    changeLogMessage: data.changeLogMessage + '',
                    canUpdate: !!data.canUpdate,
                }
            })
            .catch(function (error) {
                Logger.e(error)
                return Promise.resolve({
                    currentVersion: currentVersion + '',
                    latestVersion: currentVersion + '',
                    changeLogMessage: '',
                    canUpdate: false,
                })
            })
    }

    getCaptainImageTags() {
        if (
            'caprover/caprover' ===
            CaptainConstants.configs.publishedNameOnDockerHub
        ) {
            // For the official image use our official API.
            return this.getCaptainImageTagsFromOfficialApi(
                CaptainConstants.configs.version
            )
        }

        // Fallback for unofficial images to DockerHub, knowing that:
        // - The API contract is not guaranteed to always be the same, it might break in the future
        // - This method does not return the changeLogMessage

        const url = `https://hub.docker.com/v2/repositories/${CaptainConstants.configs.publishedNameOnDockerHub}/tags`

        return new Promise<string[]>(function (resolve, reject) {
            request(
                url,

                function (error, response, body) {
                    if (CaptainConstants.isDebug) {
                        resolve(['v0.0.1'])
                        return
                    }

                    if (error) {
                        reject(error)
                    } else if (!body || !JSON.parse(body).results) {
                        reject(
                            new Error(
                                'Received empty body or no result for version list on docker hub.'
                            )
                        )
                    } else {
                        const results = JSON.parse(body).results
                        const tags = []
                        for (let idx = 0; idx < results.length; idx++) {
                            tags.push(results[idx].name)
                        }
                        resolve(tags)
                    }
                }
            )
        }).then(function (tagList) {
            const currentVersion = CaptainConstants.configs.version.split('.')
            let latestVersion = CaptainConstants.configs.version.split('.')

            let canUpdate = false

            for (let i = 0; i < tagList.length; i++) {
                const tag = tagList[i].split('.')

                if (tag.length !== 3) {
                    continue
                }

                if (Number(tag[0]) > Number(currentVersion[0])) {
                    canUpdate = true
                    latestVersion = tag
                    break
                } else if (
                    Number(tag[0]) === Number(currentVersion[0]) &&
                    Number(tag[1]) > Number(currentVersion[1])
                ) {
                    canUpdate = true
                    latestVersion = tag
                    break
                } else if (
                    Number(tag[0]) === Number(currentVersion[0]) &&
                    Number(tag[1]) === Number(currentVersion[1]) &&
                    Number(tag[2]) > Number(currentVersion[2])
                ) {
                    canUpdate = true
                    latestVersion = tag
                    break
                }
            }

            return {
                currentVersion: currentVersion.join('.'),
                latestVersion: latestVersion.join('.'),
                canUpdate: canUpdate,
                changeLogMessage: '',
            }
        })
    }

    updateCaptain(
        versionTag: string,
        dockerRegistryHelper: DockerRegistryHelper
    ) {
        const self = this
        const providedImageName = `${CaptainConstants.configs.publishedNameOnDockerHub}:${versionTag}`
        return Promise.resolve()
            .then(function () {
                return dockerRegistryHelper.getDockerAuthObjectForImageName(
                    providedImageName
                )
            })
            .then(function (authObj) {
                return self.dockerApi.pullImage(providedImageName, authObj)
            })
            .then(function () {
                return self.dockerApi.updateService(
                    CaptainConstants.captainServiceName,
                    providedImageName,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined,
                    undefined
                )
            })
    }

    private static captainManagerInstance: VersionManager | undefined

    static get(): VersionManager {
        if (!VersionManager.captainManagerInstance) {
            VersionManager.captainManagerInstance = new VersionManager()
        }
        return VersionManager.captainManagerInstance
    }
}

export default VersionManager
