const { DistributionAPI, HeliosDistribution } = require('helios-core/common')

const ConfigManager = require('./configmanager')

const DEFAULT_REMOTE_DISTRO_URL = 'https://cdn.mysticred.space/launcher/distribution.json'

let remoteDistroUrl = DEFAULT_REMOTE_DISTRO_URL
let activeApi = createApi(remoteDistroUrl)

function sanitizeServer(rawServer){
    const nextServer = {
        ...rawServer
    }

    if(typeof nextServer.address !== 'string' || nextServer.address.trim().length === 0){
        nextServer.address = '127.0.0.1:25565'
    } else {
        nextServer.address = nextServer.address.trim()
    }

    if(!Array.isArray(nextServer.modules)){
        nextServer.modules = []
    }

    if(typeof nextServer.minecraftVersion !== 'string' || nextServer.minecraftVersion.trim().length === 0){
        nextServer.minecraftVersion = typeof nextServer.version === 'string' && nextServer.version.trim().length > 0
            ? nextServer.version.trim()
            : '1.20.1'
    }

    return nextServer
}

function sanitizeDistribution(rawDistribution){
    const nextDistribution = rawDistribution == null
        ? { version: '1.0.0', rss: '', servers: [] }
        : { ...rawDistribution }

    nextDistribution.servers = Array.isArray(nextDistribution.servers)
        ? nextDistribution.servers.map((server) => sanitizeServer(server))
        : []

    return nextDistribution
}

function buildDistribution(api, rawDistribution){
    const sanitizedDistribution = sanitizeDistribution(rawDistribution)
    api.rawDistribution = sanitizedDistribution
    api.distribution = new HeliosDistribution(sanitizedDistribution, api.commonDir, api.instanceDir)
    return api.distribution
}

function patchDistributionApi(api){
    api.getDistribution = async function(){
        if(this.rawDistribution == null || this.distribution == null){
            const rawDistribution = await this.loadDistribution()
            return buildDistribution(this, rawDistribution)
        }
        return this.distribution
    }

    api.getDistributionLocalLoadOnly = async function(){
        if(this.rawDistribution == null || this.distribution == null){
            const rawDistribution = await this.pullLocal()
            if(rawDistribution == null){
                throw new Error('FATAL: Unable to load distribution from local disk.')
            }
            return buildDistribution(this, rawDistribution)
        }
        return this.distribution
    }

    api.refreshDistributionOrFallback = async function(){
        const rawDistribution = await this._loadDistributionNullable()
        if(rawDistribution == null){
            return this.distribution
        }
        return buildDistribution(this, rawDistribution)
    }

    return api
}

function createApi(url) {
    const api = new DistributionAPI(
        ConfigManager.getLauncherDirectory(),
        null,
        null,
        url,
        false
    )

    return patchDistributionApi(api)
}

function replaceApi(url) {
    const nextApi = createApi(url)

    if(activeApi != null){
        if(typeof activeApi.commonDir !== 'undefined'){
            nextApi.commonDir = activeApi.commonDir
        }
        if(typeof activeApi.instanceDir !== 'undefined'){
            nextApi.instanceDir = activeApi.instanceDir
        }
        if(typeof activeApi.isDevMode === 'function' && activeApi.isDevMode()){
            nextApi.toggleDevMode(true)
        }
    }

    activeApi = nextApi
    remoteDistroUrl = url
}

exports.DEFAULT_REMOTE_DISTRO_URL = DEFAULT_REMOTE_DISTRO_URL

exports.getRemoteDistributionUrl = function() {
    return remoteDistroUrl
}

exports.setRemoteDistributionUrl = function(url) {
    if(url != null && url.trim().length > 0 && url !== remoteDistroUrl){
        replaceApi(url)
    }
    return remoteDistroUrl
}

exports.resetRemoteDistributionUrl = function() {
    replaceApi(DEFAULT_REMOTE_DISTRO_URL)
    return remoteDistroUrl
}

exports.DistroAPI = new Proxy({}, {
    get(_target, prop) {
        const value = activeApi[prop]
        if(typeof value === 'function'){
            return value.bind(activeApi)
        }
        return value
    },
    set(_target, prop, value) {
        activeApi[prop] = value
        return true
    },
    has(_target, prop) {
        return prop in activeApi
    }
})
