// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

import {
    window,
    Disposable,
    StatusBarItem,
    StatusBarAlignment,
    version as vscodeVersion,
} from "vscode";
import * as semver from "semver";
import * as nls from "vscode-nls";
import { SettingsHelper } from "./settingsHelper";

nls.config({
    messageFormat: nls.MessageFormat.bundle,
    bundleFormat: nls.BundleFormat.standalone,
})();
const localize = nls.loadMessageBundle();

/**
 * Updates the Status bar with the status of React Native Packager.
 */

export enum PackagerStatus {
    PACKAGER_STOPPED,
    PACKAGER_STOPPING,
    PACKAGER_STARTED,
    PACKAGER_STARTING,
}

export class PackagerStatusIndicator implements Disposable {
    private togglePackagerItem: StatusBarItem;
    private restartPackagerItem: StatusBarItem;
    private displayVersion!: string;
    private projectRoot?: string;
    private packagerPort?: number;
    private pendingPackagerPort?: number;
    private packagerStatus: PackagerStatus = PackagerStatus.PACKAGER_STOPPED;
    private isShowIndicator: boolean = true;

    private static PACKAGER_NAME: string = localize("ReactNativePackager", "React Native Packager");
    private static STOP_TOOLTIP: string = localize("StopPackager", "Stop Packager");
    private static START_TOOLTIP: string = localize("StartPackager", "Start Packager");
    private static RESTART_TOOLTIP: string = localize("RestartPackager", "Restart Packager");
    private static STARTING_TOOLTIP: string = localize("StartingPackager", "Starting Packager");
    private static STOPPING_TOOLTIP: string = localize("StoppingPackager", "Stopping Packager");

    private static START_ICON = "$(play)";
    private static NOT_SHOW_INDICATOR = "$(combine)";
    private static STOP_ICON = "$(primitive-square)";
    private static ACTIVITY_ICON = "$(loading~spin)";
    private static RESTART_ICON = "$(sync)";

    private static START_COMMAND = "reactNative.startPackager";
    private static RESTART_COMMAND = "reactNative.restartPackager";
    private static STOP_COMMAND = "reactNative.stopPackager";

    public static FULL_VERSION = "Full";
    public static SHORT_VERSION = "Short";

    public constructor(projectRoot?: string) {
        this.projectRoot = projectRoot;

        this.isShowIndicator = SettingsHelper.getShowIndicator();
        // Remove after updating supported VS Code engine version to 1.57.0
        if (semver.gte(vscodeVersion, "1.57.0")) {
            this.restartPackagerItem = (window as any).createStatusBarItem(
                "restartPackagerItem",
                StatusBarAlignment.Left,
                10,
            );
            (this.restartPackagerItem as any).name = PackagerStatusIndicator.RESTART_TOOLTIP;

            this.togglePackagerItem = (window as any).createStatusBarItem(
                "togglePackagerItem",
                StatusBarAlignment.Left,
                10,
            );
            (this.togglePackagerItem as any).name = PackagerStatusIndicator.PACKAGER_NAME;
        } else {
            this.restartPackagerItem = window.createStatusBarItem(StatusBarAlignment.Left, 10);
            this.togglePackagerItem = window.createStatusBarItem(StatusBarAlignment.Left, 10);
        }
        this.restartPackagerItem.text = PackagerStatusIndicator.RESTART_ICON;
        this.restartPackagerItem.command = PackagerStatusIndicator.RESTART_COMMAND;
        this.restartPackagerItem.tooltip = PackagerStatusIndicator.RESTART_TOOLTIP;

        this.setupPackagerStatusIndicatorItems(
            PackagerStatusIndicator.START_ICON,
            PackagerStatusIndicator.START_COMMAND,
            PackagerStatusIndicator.START_TOOLTIP,
        );
    }

    public updateDisplayVersion(): void {
        this.displayVersion = PackagerStatusIndicator.FULL_VERSION;
        try {
            if (this.projectRoot) {
                this.displayVersion = SettingsHelper.getPackagerStatusIndicatorPattern(
                    this.projectRoot,
                );
            }
        } catch (e) {
            // We are trying to read the configuration from settings.json.
            // If this cannot be done, ignore the error and set the default value.
        }
    }

    public dispose(): void {
        this.togglePackagerItem.dispose();
        this.restartPackagerItem.dispose();
    }

    private setupPackagerStatusIndicatorItems(
        icon: string,
        command?: string,
        tooltip: string = "",
    ): void {
        this.updateDisplayVersion();
        this.togglePackagerItem.command = command;
        this.togglePackagerItem.tooltip = this.getTooltip(tooltip);
        if (!this.isShowIndicator) {
            icon = PackagerStatusIndicator.NOT_SHOW_INDICATOR;
        }
        const portSuffix = this.packagerPort ? ` :${this.packagerPort}` : "";
        const pendingPortSuffix = this.pendingPackagerPort ? " $(warning)" : "";
        switch (this.displayVersion) {
            case PackagerStatusIndicator.FULL_VERSION:
                this.togglePackagerItem.text = `${icon} ${PackagerStatusIndicator.PACKAGER_NAME}${portSuffix}${pendingPortSuffix}`;
                this.togglePackagerItem.show();
                this.restartPackagerItem.show();
                break;
            case PackagerStatusIndicator.SHORT_VERSION:
                this.togglePackagerItem.text = `${icon}${portSuffix}${pendingPortSuffix}`;
                this.togglePackagerItem.show();
                this.restartPackagerItem.show();
                break;
        }
    }

    private getTooltip(tooltip: string): string {
        if (this.pendingPackagerPort && this.packagerPort) {
            return `${tooltip}\n\nRunning on port ${this.packagerPort}.\nPort setting changed to ${this.pendingPackagerPort}. It will be reset on next start.`;
        }
        return tooltip;
    }

    public setPendingPackagerPort(port?: number): void {
        this.pendingPackagerPort = port;
        this.updatePackagerStatus(this.packagerStatus);
    }

    public updatePackagerStatus(status: PackagerStatus, port?: number): void {
        this.packagerStatus = status;
        if (port !== undefined) {
            this.packagerPort = port;
            if (this.pendingPackagerPort === port) {
                this.pendingPackagerPort = undefined;
            }
        }
        switch (status) {
            case PackagerStatus.PACKAGER_STOPPED:
                this.packagerPort = undefined;
                this.pendingPackagerPort = undefined;
                this.setupPackagerStatusIndicatorItems(
                    PackagerStatusIndicator.START_ICON,
                    PackagerStatusIndicator.START_COMMAND,
                    PackagerStatusIndicator.START_TOOLTIP,
                );
                break;
            case PackagerStatus.PACKAGER_STOPPING:
                this.setupPackagerStatusIndicatorItems(
                    PackagerStatusIndicator.ACTIVITY_ICON,
                    undefined,
                    PackagerStatusIndicator.STOPPING_TOOLTIP,
                );
                break;
            case PackagerStatus.PACKAGER_STARTED:
                this.setupPackagerStatusIndicatorItems(
                    PackagerStatusIndicator.STOP_ICON,
                    PackagerStatusIndicator.STOP_COMMAND,
                    PackagerStatusIndicator.STOP_TOOLTIP,
                );
                break;
            case PackagerStatus.PACKAGER_STARTING:
                this.setupPackagerStatusIndicatorItems(
                    PackagerStatusIndicator.ACTIVITY_ICON,
                    undefined,
                    PackagerStatusIndicator.STARTING_TOOLTIP,
                );
                break;
            default:
                break;
        }
    }
}
