import React, { ReactNode, useState } from "react";
import { RqbitDesktopConfig } from "./configuration";
import { ErrorComponent } from "rqbit-webui/src/components/ErrorComponent";
import { invokeAPI } from "./api";
import { ErrorDetails } from "rqbit-webui/src/api-types";
import { FormCheckbox } from "rqbit-webui/src/components/forms/FormCheckbox";
import { FormInput as FI } from "rqbit-webui/src/components/forms/FormInput";
import { ModalBody } from "rqbit-webui/src/components/modal/ModalBody";
import { Modal } from "rqbit-webui/src/components/modal/Modal";
import { Fieldset } from "rqbit-webui/src/components/forms/Fieldset";
import { ModalFooter } from "rqbit-webui/src/components/modal/ModalFooter";
import { Button } from "rqbit-webui/src/components/buttons/Button";
import { formatBytes } from "rqbit-webui/src/helper/formatBytes";

const FormCheck: React.FC<{
  label: string;
  name: string;
  checked: boolean;
  onChange: React.ChangeEventHandler<HTMLInputElement>;
  disabled?: boolean;
  help?: string;
}> = ({ label, name, checked, onChange, disabled, help }) => {
  return (
    <FormCheckbox
      label={label}
      name={name}
      checked={checked}
      onChange={onChange}
      disabled={disabled}
      help={help}
    />
  );
};

const FormInput: React.FC<{
  label: string;
  name: string;
  value: string | number;
  inputType: string;
  onChange: React.ChangeEventHandler<HTMLInputElement>;
  disabled?: boolean;
  help?: string;
}> = ({ label, name, value, inputType, onChange, disabled, help }) => {
  return (
    <FI
      inputType={inputType}
      name={name}
      value={value as string}
      onChange={onChange}
      disabled={disabled}
      label={label}
      help={help}
    />
  );
};

type TAB =
  | "Home"
  | "DHT"
  | "Session"
  | "Peer options"
  | "HTTP API"
  | "TCP Listen"
  | "UPnP Server";

const TABS: readonly TAB[] = [
  "Home",
  "DHT",
  "Session",
  "TCP Listen",
  "Peer options",
  "HTTP API",
  "UPnP Server",
] as const;

const Tab: React.FC<{
  name: TAB;
  currentTab: TAB;
  children: ReactNode;
}> = ({ name, currentTab, children }) => {
  const show = name === currentTab;
  if (!show) {
    return;
  }
  return <div>{children}</div>;
};

export const ConfigModal: React.FC<{
  show: boolean;
  handleStartReconfigure: () => void;
  handleConfigured: (config: RqbitDesktopConfig) => void;
  handleCancel?: () => void;
  initialConfig: RqbitDesktopConfig;
  defaultConfig: RqbitDesktopConfig;
}> = ({
  show,
  handleStartReconfigure,
  handleConfigured,
  handleCancel,
  initialConfig,
  defaultConfig,
}) => {
  let [config, setConfig] = useState<RqbitDesktopConfig>(initialConfig);
  let [loading, setLoading] = useState<boolean>(false);

  let [tab, setTab] = useState<TAB>("Home");

  const [error, setError] = useState<any | null>(null);

  const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    const name: string = e.target.name;
    let value: string | number = e.target.value;
    if (e.target.type == "number") {
      value = e.target.valueAsNumber;
    }
    console.log(value, typeof value);
    const [mainField, subField] = name.split(".", 2);

    if (subField) {
      setConfig((prevConfig: any) => ({
        ...prevConfig,
        [mainField]: {
          ...prevConfig[mainField],
          [subField]: value,
        },
      }));
    } else {
      setConfig((prevConfig) => ({
        ...prevConfig,
        [name]: value,
      }));
    }
  };

  const handleToggleChange: React.ChangeEventHandler<HTMLInputElement> = (
    e
  ) => {
    const name: string = e.target.name;
    const [mainField, subField] = name.split(".", 2);

    if (subField) {
      setConfig((prevConfig: any) => ({
        ...prevConfig,
        [mainField]: {
          ...prevConfig[mainField],
          [subField]: !prevConfig[mainField][subField],
        },
      }));
    } else {
      setConfig((prevConfig: any) => ({
        ...prevConfig,
        [name]: !prevConfig[name],
      }));
    }
  };

  const handleOkClick = () => {
    setError(null);
    handleStartReconfigure();
    setLoading(true);
    invokeAPI<{}>("config_change", { config }).then(
      () => {
        setLoading(false);
        handleConfigured(config);
      },
      (e: ErrorDetails) => {
        setLoading(false);
        setError({
          text: "Error saving configuration",
          details: e,
        });
      }
    );
  };

  return (
    <Modal
      title="Configure Rqbit desktop"
      isOpen={show}
      onClose={handleCancel}
      className="max-w-4xl"
    >
      <ModalBody>
        <ErrorComponent error={error}></ErrorComponent>
        <div className="mb-4 flex border-b">
          {TABS.map((t, i) => {
            const isActive = t === tab;
            let classNames = "text-slate-300";
            if (isActive) {
              classNames =
                "text-slate-800 border-b-2 border-blue-800 dark:border-blue-200 dark:text-white";
            }
            return (
              <button
                key={i}
                className={`p-2 ${classNames}`}
                onClick={() => setTab(t)}
              >
                {t}
              </button>
            );
          })}
        </div>

        <Tab name="Home" currentTab={tab}>
          <FormInput
            label="Default download folder"
            name="default_download_location"
            value={config.default_download_location}
            inputType="text"
            onChange={handleInputChange}
            help="Where to download torrents by default. You can override this per torrent."
          />

          {defaultConfig.disable_upload !== undefined &&
            config.disable_upload !== undefined && (
              <FormCheck
                label="Disable upload"
                name="disable_upload"
                checked={config.disable_upload}
                onChange={handleToggleChange}
                help="Disable uploading entirely. If this is set, rqbit won't share piece availability and will disconnect on download request.

Might be useful e.g. if rqbit upload consumes all your upload bandwidth and interferes with your other Internet usage."
              />
            )}
        </Tab>

        <Tab name="DHT" currentTab={tab}>
          <Fieldset>
            <FormCheck
              label="Enable DHT"
              name="dht.disable"
              checked={!config.dht.disable}
              onChange={handleToggleChange}
              help="DHT is required to read magnet links. There's no good reason to disable it, unless you know what you are doing."
            />

            <FormCheck
              label="Enable DHT persistence"
              name="dht.disable_persistence"
              checked={!config.dht.disable_persistence}
              onChange={handleToggleChange}
              disabled={config.dht.disable}
              help="Enable to store DHT state in a file periodically. If disabled, DHT will bootstrap from scratch on restart."
            />

            <FormInput
              label="Persistence filename"
              name="dht.persistence_filename"
              value={config.dht.persistence_filename}
              inputType="text"
              disabled={config.dht.disable}
              onChange={handleInputChange}
              help="The filename to store DHT state into"
            />
          </Fieldset>
        </Tab>

        <Tab name="TCP Listen" currentTab={tab}>
          <Fieldset>
            <FormCheck
              label="Listen on TCP"
              name="tcp_listen.disable"
              checked={!config.tcp_listen.disable}
              onChange={handleToggleChange}
              help="Listen for torrent requests on TCP. Required for peers to be able to connect to you, mainly for uploading."
            />

            <FormCheck
              label="Advertise TCP port over UPnP"
              name="tcp_listen.disable"
              checked={!config.tcp_listen.disable}
              onChange={handleToggleChange}
              help="Advertise your port over UPnP to your router(s). This is required for peers to be able to connect to you from the internet. Will only work if your router has a static IP."
            />

            <FormInput
              inputType="number"
              label="Min port"
              name="tcp_listen.min_port"
              value={config.tcp_listen.min_port}
              disabled={config.tcp_listen.disable}
              onChange={handleInputChange}
              help="The min port to try to listen on. First successful is taken."
            />

            <FormInput
              inputType="number"
              label="Max port"
              name="tcp_listen.max_port"
              value={config.tcp_listen.max_port}
              disabled={config.tcp_listen.disable}
              onChange={handleInputChange}
              help="The max port to try to listen on."
            />
          </Fieldset>
        </Tab>

        <Tab name="UPnP Server" currentTab={tab}>
          <Fieldset>
            <FormCheck
              label="Enable UPnP media server"
              name="upnp.enable_server"
              checked={config.upnp.enable_server}
              onChange={handleToggleChange}
              help="If enabled, rqbit will advertise the media to supported LAN devices, e.g. TVs."
            />

            <FormInput
              inputType="text"
              label="Friendly name"
              name="upnp.server_friendly_name"
              value={config.upnp.server_friendly_name}
              disabled={!config.upnp.enable_server}
              onChange={handleInputChange}
              help="The name displayed on supported devices. If not set will be generated, will look smth like <rqbit at HOSTNAME>."
            />
          </Fieldset>
        </Tab>

        <Tab name="Session" currentTab={tab}>
          <Fieldset>
            <FormCheck
              label="Enable persistence"
              name="persistence.disable"
              checked={!config.persistence.disable}
              onChange={handleToggleChange}
              help="If you disable session persistence, rqbit won't remember the torrents you had before restart."
            />

            <FormInput
              label="Persistence folder"
              name="persistence.folder"
              inputType="text"
              value={config.persistence.folder}
              onChange={handleInputChange}
              disabled={config.persistence.disable}
            />

            <FormCheck
              label="Enable fast resume (experimental)"
              name="persistence.fastresume"
              checked={config.persistence.fastresume}
              onChange={handleToggleChange}
              help="If enabled, restarting will not rehash torrents, and thus will be faster. You should not modify the downloaded files in any way if you use that."
            />

            <FormInput
              label="Download rate limit"
              name="ratelimits.download_bps"
              inputType="number"
              value={config.ratelimits.download_bps ?? ""}
              onChange={handleInputChange}
              help={`Limit total download speed to this number of bytes per second (${
                (config.ratelimits.download_bps ?? 0) > 0
                  ? "current " +
                    formatBytes(config.ratelimits.download_bps ?? 0) +
                    " per second"
                  : "currently disabled"
              })`}
            />

            <FormInput
              label="Upload rate limit"
              name="ratelimits.upload_bps"
              inputType="number"
              value={config.ratelimits.upload_bps ?? ""}
              onChange={handleInputChange}
              help={`Limit total upload speed to this number of bytes per second (${
                (config.ratelimits.upload_bps ?? 0) > 0
                  ? "current " +
                    formatBytes(config.ratelimits.upload_bps ?? 0) +
                    " per second"
                  : "currently disabled"
              })`}
            />
          </Fieldset>
        </Tab>

        <Tab name="Peer options" currentTab={tab}>
          <Fieldset>
            <FormInput
              label="Connect timeout (seconds)"
              inputType="number"
              name="peer_opts.connect_timeout"
              value={config.peer_opts.connect_timeout}
              onChange={handleInputChange}
              help="How much to wait for outgoing connections to connect. Default is low to prefer faster peers."
            />

            <FormInput
              label="Read/write timeout (seconds)"
              inputType="number"
              name="peer_opts.read_write_timeout"
              value={config.peer_opts.read_write_timeout}
              onChange={handleInputChange}
              help="Peer socket read/write timeout."
            />
          </Fieldset>
        </Tab>

        <Tab name="HTTP API" currentTab={tab}>
          <Fieldset>
            <FormCheck
              label="Enable HTTP API"
              name="http_api.disable"
              checked={!config.http_api.disable}
              onChange={handleToggleChange}
              help="If enabled you can access the HTTP API at the address below"
            />

            <FormCheck
              label="Read only"
              name="http_api.read_only"
              checked={config.http_api.read_only}
              disabled={config.http_api.disable}
              onChange={handleToggleChange}
              help="If enabled, only GET requests will be allowed through the API"
            />

            <FormInput
              label="Listen address"
              inputType="text"
              name="http_api.listen_addr"
              value={config.http_api.listen_addr}
              disabled={config.http_api.disable}
              onChange={handleInputChange}
              help={`You'll access the API at http://${config.http_api.listen_addr}`}
            />
          </Fieldset>
        </Tab>
      </ModalBody>
      <ModalFooter>
        {!!handleCancel && (
          <Button variant="cancel" onClick={handleCancel}>
            Cancel
          </Button>
        )}
        <Button variant="secondary" onClick={() => setConfig(defaultConfig)}>
          Reset to defaults
        </Button>
        <Button variant="primary" onClick={handleOkClick} disabled={loading}>
          OK
        </Button>
      </ModalFooter>
    </Modal>
  );
};
