import * as vscode from "vscode";
import { State } from "vscode-languageclient";

import { Command, STATUS_EMITTER, WorkspaceInterface, SUPPORTED_LANGUAGE_IDS } from "./common";

const STOPPED_SERVER_OPTIONS = [
  { label: "Ruby LSP: Start", description: Command.Start },
  { label: "Ruby LSP: Restart", description: Command.Restart },
];

const STARTED_SERVER_OPTIONS = [
  { label: "Ruby LSP: Stop", description: Command.Stop },
  { label: "Ruby LSP: Restart", description: Command.Restart },
  {
    label: "Ruby LSP: Open server changelog in browser",
    description: Command.ShowServerChangelog,
  },
];

export abstract class StatusItem {
  public item: vscode.LanguageStatusItem;

  constructor(id: string) {
    const documentSelector = SUPPORTED_LANGUAGE_IDS.map((language) => ({
      language,
    }));

    this.item = vscode.languages.createLanguageStatusItem(id, documentSelector);
  }

  abstract refresh(workspace: WorkspaceInterface): void;

  dispose(): void {
    this.item.dispose();
  }
}

export class RubyVersionStatus extends StatusItem {
  constructor() {
    super("rubyVersion");

    this.item.name = "Ruby Version";
    this.item.command = {
      title: "Configure",
      command: Command.SelectVersionManager,
    };

    this.item.text = "Activating Ruby environment";
    this.item.severity = vscode.LanguageStatusSeverity.Information;
  }

  refresh(workspace: WorkspaceInterface): void {
    if (workspace.ruby.error) {
      this.item.text = "Failed to activate Ruby";
      this.item.severity = vscode.LanguageStatusSeverity.Error;
    } else {
      this.item.text = `Using Ruby ${workspace.ruby.rubyVersion} with ${workspace.ruby.versionManager.identifier}`;
      this.item.severity = vscode.LanguageStatusSeverity.Information;
    }
  }
}

export class ServerStatus extends StatusItem {
  constructor() {
    super("server");

    this.item.name = "Ruby LSP Server Status";
    this.item.text = "Ruby LSP: Starting";
    this.item.severity = vscode.LanguageStatusSeverity.Information;
    this.item.command = {
      title: "Configure",
      command: Command.ServerOptions,
      arguments: [STARTED_SERVER_OPTIONS],
    };
  }

  refresh(workspace: WorkspaceInterface): void {
    if (workspace.error) {
      this.item.text = "Ruby LSP: Error";
      this.item.command!.arguments = [STOPPED_SERVER_OPTIONS];
      this.item.severity = vscode.LanguageStatusSeverity.Error;
      return;
    }

    if (!workspace.lspClient) {
      return;
    }

    switch (workspace.lspClient.state) {
      case State.Running: {
        this.item.text = workspace.lspClient.serverVersion
          ? `Ruby LSP server v${workspace.lspClient.serverVersion}: Running`
          : "Ruby LSP: Running";

        if (workspace.lspClient.degraded) {
          this.item.text += " (degraded)";
          this.item.severity = vscode.LanguageStatusSeverity.Warning;
        } else {
          this.item.severity = vscode.LanguageStatusSeverity.Information;
        }

        this.item.command!.arguments = [STARTED_SERVER_OPTIONS];
        break;
      }
      case State.Starting: {
        this.item.text = "Ruby LSP: Starting";
        this.item.command!.arguments = [STARTED_SERVER_OPTIONS];
        this.item.severity = vscode.LanguageStatusSeverity.Information;
        break;
      }
      case State.Stopped: {
        this.item.text = "Ruby LSP: Stopped";
        this.item.command!.arguments = [STOPPED_SERVER_OPTIONS];
        this.item.severity = vscode.LanguageStatusSeverity.Information;
        break;
      }
    }
  }
}

export class FeaturesStatus extends StatusItem {
  constructor() {
    super("features");
    this.item.name = "Ruby LSP Features";
    this.item.command = {
      title: "Manage",
      command: Command.ToggleFeatures,
    };
    this.item.text = "Fetching feature information";
  }

  refresh(_workspace: WorkspaceInterface): void {
    const configuration = vscode.workspace.getConfiguration("rubyLsp");
    const features: Record<string, boolean> = configuration.get("enabledFeatures")!;
    const enabledFeatures = Object.keys(features).filter((key) => features[key]);

    this.item.text = `${enabledFeatures.length}/${Object.keys(features).length} features enabled`;
  }
}

export class FormatterStatus extends StatusItem {
  constructor() {
    super("formatter");

    this.item.name = "Ruby LSP Formatter";
    this.item.command = {
      title: "Help",
      command: Command.FormatterHelp,
    };
    this.item.text = "Fetching formatter information";
  }

  refresh(workspace: WorkspaceInterface): void {
    if (workspace.lspClient) {
      if (workspace.lspClient.formatter) {
        this.item.text = `Formatter: ${workspace.lspClient.formatter}`;
      } else {
        this.item.text = "Formatter: requires server to be v0.12.4 or higher to display this field";
      }
    }
  }
}

export class AddonsStatus extends StatusItem {
  constructor() {
    super("addons");

    this.item.name = "Ruby LSP Add-ons";
    this.item.text = "Fetching add-on information";
  }

  refresh(workspace: WorkspaceInterface): void {
    if (!workspace.lspClient) {
      return;
    }
    if (workspace.lspClient.addons === undefined) {
      this.item.text = "Addons: requires server to be v0.17.4 or higher to display this field";
    } else if (workspace.lspClient.addons.length === 0) {
      this.item.text = "Addons: none";
    } else {
      this.item.text = `Addons: ${workspace.lspClient.addons.length}`;
      this.item.command = {
        title: "Details",
        command: Command.DisplayAddons,
      };
    }
  }
}

export class StatusItems {
  private readonly items: StatusItem[] = [];

  constructor() {
    this.items = [
      new RubyVersionStatus(),
      new ServerStatus(),
      new FeaturesStatus(),
      new FormatterStatus(),
      new AddonsStatus(),
    ];

    STATUS_EMITTER.event((workspace) => {
      if (workspace) {
        this.items.forEach((item) => item.refresh(workspace));
      }
    });
  }

  dispose() {
    this.items.forEach((item) => item.dispose());
  }
}
