/* Copyright CloudLinux Zug GmbH 2010-2017 All Rights Reserved
 *
 * Licensed under CLOUD LINUX LICENSE AGREEMENT
 * http://cloudlinux.com/docs/LICENSE.TXT
 */

import { Injectable } from '@angular/core';
import {
    AppStatus, NodeJSInformation,
    SelectorStatus, VersionStatus
} from '../app/nodejs/model';
import { SelectorRepo } from '../app/repository/selector_repo';
import { ConfigRepo } from '../app/repository/config_repo';


@Injectable()
export class AppService {

    public inProgress: Promise<void> = null;

    private _data: NodeJSInformation = null;
    private _domainList: Array<string> = null;

    public fetchData() {
        return this._data;
    }

    /**
     * User domain list
     */
    public async updateDomains() {
        this._domainList = await this.configRepo.getDomains();
    }

    public async getDomains(): Promise<Array<string>> {
        if (!this._domainList) {
            await this.updateDomains();
        }
        return this._domainList;
    }

    /**
     * Application methods
     */
    public async checkExistingApplication(
        versionNumber: string, userName: string, appName: string
    ) {
        let data = await this.data(userName);
        if (!data.hasVersion(versionNumber)
            && !data.versions[versionNumber].hasUser(userName)
            && !data.versions[versionNumber].users[userName].hasApplication(appName)
        ) {
            throw new Error;
        }
    }

    public async changeApplicationStatus(
        status: AppStatus, versionNumber: string,
        userName: string, appName: string
    ) {
        await this.checkExistingApplication(versionNumber, userName, appName);
        this._data
            .versions[versionNumber]
            .users[userName]
            .applications[appName].app_status = status;
    }

    public async moveApplication(
        versionNumber: string, newVersion: string,
        userName: string, appName: string
    ) {
        await this.checkExistingApplication(versionNumber, userName, appName);
        let app = this._data
            .versions[versionNumber]
            .users[userName]
            .applications[appName];
        this._data
            .versions[newVersion]
            .users[userName]
            .applications[appName] = app;
        this.deleteApplication(versionNumber, userName, appName);
    }

    public async deleteApplication(
        versionNumber: string, userName: string, appName: string
    ) {
        await this.checkExistingApplication(versionNumber, userName, appName);
        delete this._data
            .versions[versionNumber]
            .users[userName]
            .applications[appName];
    }

    public async changeNodeJsStatus(selectorEnabled: boolean) {
        let selectorStatus: SelectorStatus = selectorEnabled ? 'enabled' : 'disabled';
        await this.selectorRepo.changeNodeJsStatus(selectorStatus);
        this._data.selector_enabled = selectorEnabled;
    }

    public async switchStatusOfNodeJsVersion(
        fullVersion: string, versionEnabled: boolean
    ) {
        let versionStatus: VersionStatus = versionEnabled ? 'enabled' : 'disabled';
        let majorNumber = this._data.versions[fullVersion].majorNumber;
        await this.selectorRepo.switchStatusOfNodeJsVersion(majorNumber, versionEnabled);
        this._data.versions[fullVersion].status = versionStatus;
    }

    public async setDefaultNodeJsVersion(version: string) {
        await this.selectorRepo.setDefaultNodeJsVersion(version);
        this._data.default_version = version;
    }

    public async installNodeJsVersion(fullVersion: string) {
        this._data.versions[fullVersion].status = 'installing';
        let majorNumber = this._data.versions[fullVersion].majorNumber;
        await this.selectorRepo.installNodeJsVersion(majorNumber);
    }

    public async removeNodeJsVersion(fullVersion: string) {
        this._data.versions[fullVersion].status = 'removing';
        let majorNumber = this._data.versions[fullVersion].majorNumber;
        await this.selectorRepo.removeNodeJsVersion(majorNumber);
    }

    public hasVersions(): boolean {
        return !!this._data ? Object.keys(this._data.versions)
            .some(version => this._data.versions[version].enabled) : false;
    }

    constructor (
        private selectorRepo: SelectorRepo,
        private configRepo: ConfigRepo
    ) {}

    public async update(userName?: string): Promise<void> {
        await this.updateWithBlocking(userName);
    }

    public async data(userName?: string): Promise<NodeJSInformation> {
        await this.bindData(userName);
        return this._data;
    }

    public async bindData(userName?: string): Promise<void> {
        if (!this._data) {
            await this.updateWithBlocking(userName);
        }
    }

    public async updateWithBlocking(userName?: string): Promise<void> {
        if (!this.inProgress) {
            this.inProgress = this._update(userName);
        }
        await this.inProgress;
        this.inProgress = null;
    }

    private async _update(userName?: string): Promise<void> {
        this._data = userName
            ? await this.selectorRepo.getListOfApplicationsByUser(userName)
            : await this.selectorRepo.getNodeJsInformation();
    }
}
