import { APIClient } from "./ApiClient";

export interface DHCPHost {
  // This field is unspecified in IPv6 configurations
  mac: string | undefined;
  name: string;
  ip: string;
}

export interface DHCPConfig {
  start: string;
  end: string;
  hosts: DHCPHost[];
}

export type NatSource =
  | { type: "interface"; name: string }
  | { type: "ip"; ip: string };

export type NatHostPort =
  | { type: "single"; port: number }
  | { type: "range"; start: number; end: number };

export interface NatEntry {
  protocol: "TCP" | "UDP" | "Both";
  host_ip: NatSource;
  host_port: NatHostPort;
  guest_ip: string;
  guest_port: number;
  comment?: string;
}

export interface IpConfig {
  bridge_address: string;
  prefix: number;
  dhcp?: DHCPConfig;
  nat?: NatEntry[];
}

export interface NetworkInfo {
  name: string;
  uuid?: string;
  title?: string;
  description?: string;
  forward_mode: "NAT" | "Isolated";
  device?: string;
  bridge_name?: string;
  dns_server?: string;
  domain?: string;
  ip_v4?: IpConfig;
  ip_v6?: IpConfig;
}

export type NetworkStatus = "Started" | "Stopped";

export function NetworkURL(n: NetworkInfo, edit = false): string {
  return `/net/${n.uuid}${edit ? "/edit" : ""}`;
}

export class NetworkApi {
  /**
   * Create a new network
   */
  static async Create(n: NetworkInfo): Promise<{ uid: string }> {
    return (
      await APIClient.exec({
        method: "POST",
        uri: "/network/create",
        jsonData: n,
      })
    ).data;
  }

  /**
   * Get the entire list of networks
   */
  static async GetList(): Promise<NetworkInfo[]> {
    return (
      await APIClient.exec({
        method: "GET",
        uri: "/network/list",
      })
    ).data;
  }

  /**
   * Get the information about a single network
   */
  static async GetSingle(uuid: string): Promise<NetworkInfo> {
    return (
      await APIClient.exec({
        method: "GET",
        uri: `/network/${uuid}`,
      })
    ).data;
  }

  /**
   * Get the source XML configuration of a network for debugging purposes
   */
  static async GetSingleXML(uuid: string): Promise<string> {
    return (
      await APIClient.exec({
        uri: `/network/${uuid}/src`,
        method: "GET",
      })
    ).data;
  }

  /**
   * Get the status of network
   */
  static async GetState(net: NetworkInfo): Promise<NetworkStatus> {
    return (
      await APIClient.exec({
        method: "GET",
        uri: `/network/${net.uuid}/status`,
      })
    ).data.status;
  }

  /**
   * Start the network
   */
  static async Start(net: NetworkInfo): Promise<void> {
    await APIClient.exec({
      method: "GET",
      uri: `/network/${net.uuid}/start`,
    });
  }

  /**
   * Stop the network
   */
  static async Stop(net: NetworkInfo): Promise<void> {
    await APIClient.exec({
      method: "GET",
      uri: `/network/${net.uuid}/stop`,
    });
  }

  /**
   * Check if autostart is enabled on a network
   */
  static async IsAutostart(net: NetworkInfo): Promise<boolean> {
    return (
      await APIClient.exec({
        uri: `/network/${net.uuid}/autostart`,
        method: "GET",
      })
    ).data.autostart;
  }

  /**
   * Set autostart status of a network
   */
  static async SetAutostart(net: NetworkInfo, enabled: boolean): Promise<void> {
    await APIClient.exec({
      uri: `/network/${net.uuid}/autostart`,
      method: "PUT",
      jsonData: { autostart: enabled },
    });
  }

  /**
   * Update an existing network
   */
  static async Update(n: NetworkInfo): Promise<{ uid: string }> {
    return (
      await APIClient.exec({
        method: "PUT",
        uri: `/network/${n.uuid}`,
        jsonData: n,
      })
    ).data;
  }

  /**
   * Delete a network
   */
  static async Delete(n: NetworkInfo): Promise<void> {
    await APIClient.exec({
      method: "DELETE",
      uri: `/network/${n.uuid}`,
    });
  }
}