import { Spinner, SpinnerSize } from "@fluentui/react";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import * as ko from "knockout";
import * as React from "react";
import FirewallRuleScreenshot from "../../../images/firewallRule.png";
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { userContext } from "../../UserContext";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { NotebookTerminalComponent } from "../Controls/Notebook/NotebookTerminalComponent";
import Explorer from "../Explorer";
import { useNotebook } from "../Notebook/useNotebook";
import { CloudShellTerminalComponent } from "./CloudShellTab/CloudShellTabComponent";
import TabsBase from "./TabsBase";


export interface TerminalTabOptions extends ViewModels.TabOptions {
  account: DataModels.DatabaseAccount;
  container: Explorer;
  kind: ViewModels.TerminalKind;
  username?: string;
}

/**
 * Notebook terminal tab
 */
class NotebookTerminalComponentAdapter implements ReactAdapter {
  // parameters: true: show, false: hide
  public parameters: ko.Computed<boolean>;
  constructor(
    private getNotebookServerInfo: () => DataModels.NotebookWorkspaceConnectionInfo,
    private getDatabaseAccount: () => DataModels.DatabaseAccount,
    private getTabId: () => string,
    private getUsername: () => string,
    private isAllPublicIPAddressesEnabled: ko.Observable<boolean>,
    private kind: ViewModels.TerminalKind,
  ) {}

  public renderComponent(): JSX.Element {
    if (!this.isAllPublicIPAddressesEnabled()) {
      return (
        <QuickstartFirewallNotification
          messageType={MessageTypes.OpenPostgresNetworkingBlade}
          screenshot={FirewallRuleScreenshot}
          shellName={getShellNameForDisplay(this.kind)}
        />
      );
    }
    return this.parameters() ? (
      <NotebookTerminalComponent
        notebookServerInfo={this.getNotebookServerInfo()}
        databaseAccount={this.getDatabaseAccount()}
        tabId={this.getTabId()}
        username={this.getUsername()}
      />): (
      <Spinner styles={{ root: { marginTop: 10 } }} size={SpinnerSize.large}></Spinner>
    );
  }
}

/**
 * CloudShell terminal tab
 */
class CloudShellTerminalComponentAdapter implements ReactAdapter {
  // parameters: true: show, false: hide
  public parameters: ko.Computed<boolean>;
  constructor(
    private isAllPublicIPAddressesEnabled: ko.Observable<boolean>,
    private kind: ViewModels.TerminalKind,
  ) {}

  public renderComponent(): JSX.Element {
    if (!this.isAllPublicIPAddressesEnabled()) {
      return (
        <QuickstartFirewallNotification
          messageType={MessageTypes.OpenPostgresNetworkingBlade}
          screenshot={FirewallRuleScreenshot}
          shellName={getShellNameForDisplay(this.kind)}
        />
      );
    }
    return this.parameters() ? (
      <CloudShellTerminalComponent 
        shellType={this.kind}/>
    ) : (
      <Spinner styles={{ root: { marginTop: 10 } }} size={SpinnerSize.large}></Spinner>
    );
  }
}

export const getShellNameForDisplay = (terminalKind: ViewModels.TerminalKind): string => {
  switch (terminalKind) {
    case ViewModels.TerminalKind.Postgres:
      return "PostgreSQL";
    case ViewModels.TerminalKind.Mongo:
    case ViewModels.TerminalKind.VCoreMongo:
      return "MongoDB";
    default:
      return "";
  }
}

export default class TerminalTab extends TabsBase {
  public readonly html = '<div style="height: 100%" data-bind="react: terminalComponentAdapter"></div>';
  private container: Explorer;
  private terminalComponentAdapter: any;
  private isAllPublicIPAddressesEnabled: ko.Observable<boolean>;

  constructor(options: TerminalTabOptions) {
    super(options);
    this.container = options.container;
    this.isAllPublicIPAddressesEnabled = ko.observable(true);
    
    if (options.kind === ViewModels.TerminalKind.Postgres) {
      checkFirewallRules(
        "2022-11-08",
        (rule) => rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255",
        this.isAllPublicIPAddressesEnabled,
      );
    }

    if (options.kind === ViewModels.TerminalKind.VCoreMongo) {
      checkFirewallRules(
        "2023-03-01-preview",
        (rule) =>
          rule.name.startsWith("AllowAllAzureServicesAndResourcesWithinAzureIps") ||
          (rule.properties.startIpAddress === "0.0.0.0" && rule.properties.endIpAddress === "255.255.255.255"),
        this.isAllPublicIPAddressesEnabled,
      );
    }
    
    this.initializeNotebookTerminalAdapter(options);

  }

  private initializeNotebookTerminalAdapter(options: TerminalTabOptions): void {
    if (userContext.features.enableCloudShell) {
      this.terminalComponentAdapter = new CloudShellTerminalComponentAdapter(
        this.isAllPublicIPAddressesEnabled,
        options.kind
      );
    }
    else {
      this.terminalComponentAdapter = new NotebookTerminalComponentAdapter(
        () => this.getNotebookServerInfo(options),
        () => userContext?.databaseAccount,
        () => this.tabId,
        () => this.getUsername(),
        this.isAllPublicIPAddressesEnabled,
        options.kind
      );
    }
    
    this.terminalComponentAdapter.parameters = ko.computed<boolean>(() =>
      this.isTemplateReady() &&
      (userContext.features.enableCloudShell ||
        (useNotebook.getState().isNotebookEnabled &&
          useNotebook.getState().notebookServerInfo?.notebookServerEndpoint)) &&
      this.isAllPublicIPAddressesEnabled()
    );
  }

  public getContainer(): Explorer {
    return this.container;
  }

  protected getTabsButtons(): CommandButtonComponentProps[] {
    const buttons: CommandButtonComponentProps[] = [];
    return buttons;
  }
  protected buildCommandBarOptions(): void {
    this.updateNavbarWithTabsButtons();
  }

  private getNotebookServerInfo(options: TerminalTabOptions): DataModels.NotebookWorkspaceConnectionInfo {
    let endpointSuffix: string;

    switch (options.kind) {
      case ViewModels.TerminalKind.Default:
        endpointSuffix = "";
        break;

      case ViewModels.TerminalKind.Mongo:
        endpointSuffix = "mongo";
        break;

      case ViewModels.TerminalKind.Cassandra:
        endpointSuffix = "cassandra";
        break;

      case ViewModels.TerminalKind.Postgres:
        endpointSuffix = "postgresql";
        break;

      case ViewModels.TerminalKind.VCoreMongo:
        endpointSuffix = "mongovcore";
        break;

      default:
        throw new Error(`Terminal kind: ${options.kind} not supported`);
    }

    const info: DataModels.NotebookWorkspaceConnectionInfo = useNotebook.getState().notebookServerInfo;
    return {
      authToken: info.authToken,
      notebookServerEndpoint: `${info.notebookServerEndpoint.replace(/\/+$/, "")}/${endpointSuffix}`,
      forwardingId: info.forwardingId,
    };
  }

  private getUsername(): string {
    if (userContext.apiType !== "VCoreMongo" || !userContext?.vcoreMongoConnectionParams?.adminLogin) {
      return undefined;
    }

    return userContext.vcoreMongoConnectionParams.adminLogin;
  }
}
