import React, { useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "typesafe-actions";
import { actions as nsGroupActions } from "../store/nameservers";
import {
  Button,
  Col,
  Switch,
  Form,
  FormListFieldData,
  Input,
  InputNumber,
  message,
  Modal,
  Row,
  Select,
  Space,
  Typography,
} from "antd";
import {
  CloseOutlined,
  MinusCircleOutlined,
  PlusOutlined,
} from "@ant-design/icons";
import { Header } from "antd/es/layout/layout";
import { RuleObject } from "antd/lib/form";
import cidrRegex from "cidr-regex";
import {
  NameServer,
  NameServerGroup,
  NameServerGroupToSave,
} from "../store/nameservers/types";
import { useGetGroupTagHelpers } from "../utils/groups";
import { useGetTokenSilently } from "../utils/token";
import { domainValidator } from "../utils/common";

const { Paragraph, Text } = Typography;

interface formNSGroup extends NameServerGroup {}

const NameServerGroupAdd = () => {
  const {
    blueTagRender,
    handleChangeTags,
    dropDownRender,
    optionRender,
    tagGroups,
    getExistingAndToCreateGroupsLists,
    getGroupNamesFromIDs,
    selectValidator,
  } = useGetGroupTagHelpers();
  const dispatch = useDispatch();
  const { getTokenSilently } = useGetTokenSilently();
  const { Option } = Select;
  const nsGroup = useSelector(
    (state: RootState) => state.nameserverGroup.nameserverGroup
  );
  const setupNewNameServerGroupVisible = useSelector(
    (state: RootState) => state.nameserverGroup.setupNewNameServerGroupVisible
  );
  const savedNSGroup = useSelector(
    (state: RootState) => state.nameserverGroup.savedNameServerGroup
  );
  const nsGroupData = useSelector(
    (state: RootState) => state.nameserverGroup.data
  );

  const [formNSGroup, setFormNSGroup] = useState({} as formNSGroup);
  const [form] = Form.useForm();
  const [editName, setEditName] = useState(false);
  const [isPrimary, setIsPrimary] = useState(false);
  const [editDescription, setEditDescription] = useState(false);
  const inputNameRef = useRef<any>(null);
  const inputDescriptionRef = useRef<any>(null);
  const [selectCustom, setSelectCustom] = useState(false);
  const [matchDomains, setMatchDomains] = useState(0);

  useEffect(() => {
    if (editName)
      inputNameRef.current!.focus({
        cursor: "end",
      });
  }, [editName]);

  useEffect(() => {
    if (editDescription)
      inputDescriptionRef.current!.focus({
        cursor: "end",
      });
  }, [editDescription]);
  useEffect(() => {
    if (!nsGroup) return;

    let newFormGroup = {
      ...nsGroup,
      groups: getGroupNamesFromIDs(nsGroup.groups),
    } as formNSGroup;
    setFormNSGroup(newFormGroup);
    form.setFieldsValue(newFormGroup);
    if (nsGroup.id) {
      setSelectCustom(true);
    }
    if (nsGroup.primary !== undefined) {
      setIsPrimary(nsGroup.primary);
    }
  }, [nsGroup]);

  const onCancel = () => {
    dispatch(nsGroupActions.setSetupNewNameServerGroupVisible(false));
    dispatch(
      nsGroupActions.setNameServerGroup({
        id: "",
        name: "",
        description: "",
        primary: false,
        domains: [],
        nameservers: [] as NameServer[],
        groups: [],
        enabled: false,
        search_domains_enabled: false,
      } as NameServerGroup)
    );
    setEditName(false);
    setSelectCustom(false);
    setIsPrimary(false);
  };

  const onChange = (changedValues: any) => {

    if (changedValues.primary !== undefined) {
      setIsPrimary(changedValues.primary);
    }
    setFormNSGroup({ ...formNSGroup, ...changedValues });
  };

  let googleChoice = "Google DNS";
  let cloudflareChoice = "Cloudflare DNS";
  let quad9Choice = "Quad9 DNS";
  let customChoice = "Add custom nameserver";

  let defaultDNSOptions: NameServerGroup[] = [
    {
      name: googleChoice,
      description: "Google DNS servers",
      domains: [],
      primary: true,
      nameservers: [
        {
          ip: "8.8.8.8",
          ns_type: "udp",
          port: 53,
        },
        {
          ip: "8.8.4.4",
          ns_type: "udp",
          port: 53,
        },
      ],
      groups: [],
      enabled: true,
      search_domains_enabled: false,
    },
    {
      name: cloudflareChoice,
      description: "Cloudflare DNS servers",
      domains: [],
      primary: true,
      nameservers: [
        {
          ip: "1.1.1.1",
          ns_type: "udp",
          port: 53,
        },
        {
          ip: "1.0.0.1",
          ns_type: "udp",
          port: 53,
        },
      ],
      groups: [],
      enabled: true,
      search_domains_enabled: false,
    },
    {
      name: quad9Choice,
      description: "Quad9 DNS servers",
      domains: [],
      primary: true,
      nameservers: [
        {
          ip: "9.9.9.9",
          ns_type: "udp",
          port: 53,
        },
        {
          ip: "149.112.112.112",
          ns_type: "udp",
          port: 53,
        },
      ],
      groups: [],
      enabled: true,
      search_domains_enabled: false,
    },
  ];

  const handleSelectChange = (value: string) => {
    let nsGroupLocal = {} as NameServerGroup;
    if (value === customChoice) {
      nsGroupLocal = nsGroup;
      nsGroupLocal.domains = [];
    } else {
      defaultDNSOptions.forEach((nsg) => {
        if (value === nsg.name) {
          nsGroupLocal = nsg;
        }
      });
    }
    let newFormGroup = {
      ...nsGroupLocal,
      groups: getGroupNamesFromIDs(nsGroupLocal.groups),
    } as formNSGroup;
    setFormNSGroup(newFormGroup);
    form.setFieldsValue(newFormGroup);
    setSelectCustom(true);
  };

  const handleFormSubmit = () => {
    form
      .validateFields()
      .then((values) => {
        const nsGroupToSave = createNSGroupToSave(values as NameServerGroup);
        dispatch(
          nsGroupActions.saveNameServerGroup.request({
            getAccessTokenSilently: getTokenSilently,
            payload: nsGroupToSave,
          })
        );
      })
      .then(() => onCancel())
      .catch((errorInfo) => {
        let msg = "please check the fields and try again";
        if (errorInfo.errorFields) {
          msg = errorInfo.errorFields[0].errors[0];
        }
        message.error({
          content: msg,
          duration: 1,
        });
      });
  };

  const createNSGroupToSave = (
    values: NameServerGroup
  ): NameServerGroupToSave => {
    let [existingGroups, newGroups] = getExistingAndToCreateGroupsLists(
      values.groups
    );
    return {
      id: formNSGroup.id || null,
      name: values.name ? values.name : formNSGroup.name,
      description: values.description
        ? values.description
        : formNSGroup.description,
      primary: values.domains.length ? false : true,
      domains: values.primary ? [] : values.domains,
      nameservers: values.nameservers,
      groups: existingGroups,
      groupsToCreate: newGroups,
      enabled: values.enabled,
      search_domains_enabled:
        matchDomains > 0 ? formNSGroup.search_domains_enabled : false,
    } as NameServerGroupToSave;
  };

  const toggleEditName = (status: boolean) => {
    setEditName(status);
  };

  const toggleEditDescription = (status: boolean) => {
    setEditDescription(status);
  };

  const nameValidator = (_: RuleObject, value: string) => {
    const found = nsGroupData.find(
      (u) => u.name == value && u.id !== formNSGroup.id
    );
    if (found) {
      return Promise.reject(
        new Error(
          "Please enter a unique name for your nameserver configuration"
        )
      );
    }

    return Promise.resolve();
  };

  const ipValidator = (_: RuleObject, value: string) => {
    if (!cidrRegex().test(value + "/32")) {
      return Promise.reject(
        new Error("Please enter a valid IP, e.g. 192.168.1.1 or 8.8.8.8")
      );
    }

    return Promise.resolve();
  };

  // @ts-ignore
  const formListValidator = (_: RuleObject, names) => {
    if (names.length >= 3) {
      return Promise.reject(
        new Error("Exceeded maximum number of Nameservers. (Max is 2)")
      );
    }
    if (names.length < 1) {
      return Promise.reject(new Error("You should add at least 1 Nameserver"));
    }
    return Promise.resolve();
  };

  // @ts-ignore
  const renderNSList = (
    fields: FormListFieldData[],
    { add, remove }: any,
    { errors }: any
  ) => (
    <div style={{ width: "100%", maxWidth: "360px" }}>
      <label
        style={{
          color: "rgba(0, 0, 0, 0.88)",
          fontSize: "14px",
          fontWeight: "500",
          marginBottom: "10px",
          display: "block",
        }}
      >
        Nameservers
      </label>

      {!!fields.length && (
        <Row align="middle">
          <Col span={4} style={{ textAlign: "left" }}>
            <Typography.Text
              style={{ color: "#818183", paddingLeft: "5px" }}
            ></Typography.Text>
          </Col>
          <Col span={10} style={{ textAlign: "left" }}>
            <Typography.Text style={{ color: "#818183", paddingLeft: "5px" }}>
              Nameserver IP
            </Typography.Text>
          </Col>
          <Col span={4} style={{ textAlign: "left" }}>
            <Typography.Text style={{ color: "#818183", paddingLeft: "5px" }}>
              Port
            </Typography.Text>
          </Col>
          <Col span={4} />
        </Row>
      )}
      {fields.map((field, index) => {
        return (
          <Row key={index}>
            <Col span={4} style={{ textAlign: "left" }}>
              <Form.Item
                style={{ margin: "3px" }}
                name={[field.name, "ns_type"]}
                rules={[{ required: true, message: "Missing first protocol" }]}
                initialValue={"udp"}
              >
                <Select
                  disabled
                  style={{ width: "100%" }}
                  className="style-like-text"
                >
                  <Option value="udp">UDP</Option>
                </Select>
              </Form.Item>
            </Col>
            <Col span={10} style={{ margin: "1px" }}>
              <Form.Item
                style={{ margin: "1px" }}
                name={[field.name, "ip"]}
                rules={[{ validator: ipValidator }]}
              >
                <Input
                  placeholder="e.g. X.X.X.X"
                  style={{ width: "100%" }}
                  autoComplete="off"
                />
              </Form.Item>
            </Col>
            <Col span={4} style={{ textAlign: "center" }}>
              <Form.Item
                style={{ margin: "1px" }}
                name={[field.name, "port"]}
                rules={[{ required: true, message: "Missing port" }]}
                initialValue={53}
              >
                <InputNumber placeholder="Port" style={{ width: "100%" }} />
              </Form.Item>
            </Col>
            <Col
              span={2}
              style={{
                textAlign: "center",
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              <MinusCircleOutlined onClick={() => remove(field.name)} />
            </Col>
          </Row>
        );
      })}

      <Row>
        <Col span={20}>
          <Form.Item>
            <Button
              type="dashed"
              onClick={() => add()}
              block
              style={{
                maxWidth: "270px",
                marginTop: "5px",
              }}
              disabled={fields.length > 1}
              icon={<PlusOutlined />}
            >
              Add nameserver
            </Button>
            <Form.ErrorList errors={errors} />
          </Form.Item>
        </Col>
      </Row>
    </div>
  );

  // @ts-ignore
  const renderDomains = (
    fields: FormListFieldData[],
    { add, remove }: any,
    { errors }: any
  ) => (
    <>
      <Row>
        <Space>
          <Col>
            <label
              style={{
                color: "rgba(0, 0, 0, 0.88)",
                fontSize: "14px",
                fontWeight: "500",
              }}
            >
              Match domains
            </label>
            <Paragraph
              type={"secondary"}
              style={{
                marginTop: "-4px",
                fontWeight: "400",
                marginBottom: "4px",
              }}
            >
              Add domain if you want to have a specific one
            </Paragraph>
          </Col>
        </Space>
      </Row>
      {setMatchDomains(fields.length)}
      {fields.map((field, index) => {
        return (
          <Row key={index} style={{ marginBottom: "5px" }}>
            <Col span={22}>
              <Form.Item
                style={{ margin: "0" }}
                {...field}
                rules={[{ validator: domainValidator }]}
              >
                <Input
                  placeholder="e.g. example.com"
                  style={{ width: "100%" }}
                  autoComplete="off"
                />
              </Form.Item>
            </Col>
            <Col
              span={2}
              style={{
                textAlign: "center",
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              <MinusCircleOutlined
                className="dynamic-delete-button"
                onClick={() => remove(field.name)}
              />
            </Col>
          </Row>
        );
      })}

      <Row>
        <Col span={24} style={{ margin: "1px" }}>
          <Form.Item>
            <Button
              type="dashed"
              onClick={() => add()}
              block
              icon={<PlusOutlined />}
              style={{ marginTop: "5px" }}
            >
              Add Domain
            </Button>
          </Form.Item>
        </Col>
      </Row>
      <Form.ErrorList errors={errors} />
    </>
  );

  const handleChangeDisabled = (checked: boolean) => {
    setFormNSGroup({
      ...formNSGroup,
      enabled: checked,
    });
  };

  const handleChangeMarkDomain = (checked: boolean) => {
    setFormNSGroup({
      ...formNSGroup,
      search_domains_enabled: checked,
    });
  };

  return (
    <>
      {nsGroup && (
        <Modal
          forceRender={true}
          footer={false}
          onCancel={onCancel}
          open={setupNewNameServerGroupVisible}
        >
          <Row gutter={16}>
            <Col span={24}>
              <Paragraph
                style={{
                  textAlign: "start",
                  whiteSpace: "pre-line",
                  fontSize: "18px",
                  fontWeight: "500",
                }}
              >
                Add nameserver
              </Paragraph>
              <Paragraph
                type={"secondary"}
                style={{
                  textAlign: "start",
                  whiteSpace: "pre-line",
                  marginTop: "-20px",
                  fontSize: "14px",
                  paddingBottom: "25px",
                  marginBottom: "4px",
                }}
              >
                Use this nameserver to resolve domains in your network
              </Paragraph>
            </Col>
          </Row>
          {selectCustom ? (
            <Form
              layout="vertical"
              requiredMark={false}
              form={form}
              onValuesChange={onChange}
            >
              <Row gutter={16}>
                <Col span={24}>
                  <Header
                    style={{
                      border: "none",
                    }}
                  >
                    <Row align="top">
                      <Col flex="none" style={{ display: "flex" }}>
                        {!editName && !editDescription && formNSGroup.id && (
                          <button
                            type="button"
                            aria-label="Close"
                            className="ant-drawer-close"
                            style={{ paddingTop: 3 }}
                            onClick={onCancel}
                          >
                            <span
                              role="img"
                              aria-label="close"
                              className="anticon anticon-close"
                            >
                              <CloseOutlined size={16} />
                            </span>
                          </button>
                        )}
                      </Col>
                      <Col flex="auto">
                        {!editName && formNSGroup.id ? (
                          <div
                            className={
                              "access-control input-text ant-drawer-title"
                            }
                            onClick={() => toggleEditName(true)}
                          >
                            {formNSGroup.id
                              ? formNSGroup.name
                              : "New nameserver group"}
                          </div>
                        ) : (
                          <div style={{ lineHeight: "15px" }}>
                            <label
                              style={{
                                color: "rgba(0, 0, 0, 0.88)",
                                fontSize: "14px",
                                fontWeight: "500",
                              }}
                            >
                              Name
                            </label>
                            <Form.Item
                              name="name"
                              rules={[
                                {
                                  required: true,
                                  message:
                                    "Please add an identifier for this nameserver group",
                                  whitespace: true,
                                },
                                {
                                  validator: nameValidator,
                                },
                              ]}
                              style={{
                                marginBottom: "10px",
                                marginTop: "10px",
                              }}
                            >
                              <Input
                                placeholder="e.g. Public DNS"
                                ref={inputNameRef}
                                onPressEnter={() => toggleEditName(false)}
                                onBlur={() => toggleEditName(false)}
                                autoComplete="off"
                                maxLength={40}
                              />
                            </Form.Item>
                          </div>
                        )}
                        {!editDescription ? (
                          <div
                            className={
                              "access-control input-text ant-drawer-subtitle"
                            }
                            style={{ marginTop: "0" }}
                            onClick={() => toggleEditDescription(true)}
                          >
                            {formNSGroup.description &&
                            formNSGroup.description.trim() !== ""
                              ? formNSGroup.description
                              : "Add description"}
                          </div>
                        ) : (
                          <div
                            style={{ lineHeight: "15px", marginTop: "24px" }}
                          >
                            <label
                              style={{
                                color: "rgba(0, 0, 0, 0.88)",
                                fontSize: "14px",
                                fontWeight: "500",
                              }}
                            >
                              Description
                            </label>
                            <Form.Item
                              name="description"
                              style={{ marginTop: "8px" }}
                            >
                              <Input
                                placeholder="Add description..."
                                ref={inputDescriptionRef}
                                onPressEnter={() =>
                                  toggleEditDescription(false)
                                }
                                onBlur={() => toggleEditDescription(false)}
                                autoComplete="off"
                              />
                            </Form.Item>
                          </div>
                        )}
                      </Col>
                    </Row>
                  </Header>
                </Col>
                <Col span={24}>
                  <Form.List
                    name="nameservers"
                    rules={[{ validator: formListValidator }]}
                  >
                    {renderNSList}
                  </Form.List>
                </Col>
                <Col span={24}>
                  <Form.List name="domains">{renderDomains}</Form.List>
                </Col>

                {matchDomains > 0 && (
                  <Col span={24}>
                    <Form.Item name="search_domains_enabled" label="">
                      <div
                        style={{
                          display: "flex",
                          gap: "15px",
                        }}
                      >
                        <Switch
                          onChange={handleChangeMarkDomain}
                          size="small"
                        />
                        <div>
                          <label
                            style={{
                              color: "rgba(0, 0, 0, 0.88)",
                              fontSize: "14px",
                              fontWeight: "500",
                            }}
                          >
                            Mark match domains as search domains
                          </label>
                          <Paragraph
                            type={"secondary"}
                            style={{
                              marginTop: "-2",
                              fontWeight: "400",
                              marginBottom: "0",
                            }}
                          >
                            E.g., "peer.example.com" will be accessible with
                            "peer"
                          </Paragraph>
                        </div>
                      </div>
                    </Form.Item>
                  </Col>
                )}
                <Col span={24}>
                  <label
                    style={{
                      color: "rgba(0, 0, 0, 0.88)",
                      fontSize: "14px",
                      fontWeight: "500",
                    }}
                  >
                    Distribution groups
                  </label>
                  <Paragraph
                    type={"secondary"}
                    style={{
                      marginTop: "-4px",
                      fontWeight: "400",
                      marginBottom: "4px",
                    }}
                  >
                    Advertise this route to peers that belong to the following
                    groups
                  </Paragraph>
                  <Form.Item
                    name="groups"
                    rules={[{ validator: selectValidator }]}
                  >
                    <Select
                      mode="tags"
                      style={{ width: "100%" }}
                      placeholder="Associate groups with the NS group"
                      tagRender={blueTagRender}
                      onChange={handleChangeTags}
                      dropdownRender={dropDownRender}
                      optionFilterProp="serchValue"
                    >
                      {tagGroups.map((m, index) => (
                        <Option key={index} value={m.id} serchValue={m.name}>
                          {optionRender(m.name, m.id)}
                        </Option>
                      ))}
                    </Select>
                  </Form.Item>
                </Col>
                <Col span={24}>
                  <Form.Item name="enabled" label="">
                    <div
                      style={{
                        display: "flex",
                        gap: "15px",
                      }}
                    >
                      <Switch
                        onChange={handleChangeDisabled}
                        defaultChecked={formNSGroup.enabled}
                        size="small"
                      />
                      <div>
                        <label
                          style={{
                            color: "rgba(0, 0, 0, 0.88)",
                            fontSize: "14px",
                            fontWeight: "500",
                          }}
                        >
                          Enabled
                        </label>
                        <Paragraph
                          type={"secondary"}
                          style={{
                            marginTop: "-2",
                            fontWeight: "400",
                            marginBottom: "0",
                          }}
                        >
                          {formNSGroup.enabled
                            ? "Disable this server if you don't want it to apply immediately"
                            : " Enable this server if you want it to apply immediately"}
                        </Paragraph>
                      </div>
                    </div>
                  </Form.Item>
                </Col>
                <Col
                  span={24}
                  style={{ marginTop: "10px", marginBottom: "24px" }}
                >
                  <Text type={"secondary"}>
                    Learn more about
                    <a
                      target="_blank"
                      rel="noreferrer"
                      href="https://docs.netbird.io/how-to/manage-dns-in-your-network"
                    >
                      {" "}
                      DNS
                    </a>
                  </Text>
                </Col>
                <Col
                  style={{
                    width: "100%",
                  }}
                >
                  <Space
                    style={{
                      display: "flex",
                      justifyContent: "end",
                      width: "100%",
                    }}
                  >
                    <Button onClick={onCancel} disabled={savedNSGroup.loading}>
                      Cancel
                    </Button>
                    <Button
                      type="primary"
                      onClick={handleFormSubmit}
                      disabled={savedNSGroup.loading}
                    >
                      Create nameserver
                    </Button>
                  </Space>
                </Col>
              </Row>
            </Form>
          ) : (
            <>
              <Space direction={"vertical"} style={{ width: "100%" }}>
                <Row align="middle">
                  <Col span={24} style={{ textAlign: "left" }}>
                    <span className="ant-form-item font-500">
                      Select a predefined one
                    </span>
                  </Col>
                </Row>
                <Row align="middle">
                  <Col span={24} style={{ textAlign: "center" }}>
                    <Select
                      style={{ width: "100%" }}
                      onChange={handleSelectChange}
                      options={[
                        {
                          value: googleChoice,
                          label: googleChoice,
                        },
                        {
                          value: cloudflareChoice,
                          label: cloudflareChoice,
                        },
                        {
                          value: quad9Choice,
                          label: quad9Choice,
                        },
                        {
                          value: customChoice,
                          label: customChoice,
                        },
                      ]}
                    />
                  </Col>
                </Row>
                <Row align="middle">
                  <Col span={24} style={{ textAlign: "left" }}>
                    <Col span={24} style={{ textAlign: "left" }}>
                      <span className="ant-form-item blue-color">
                        <Typography.Link
                          onClick={() => handleSelectChange(customChoice)}
                        >
                          or create custom
                        </Typography.Link>
                      </span>
                    </Col>
                  </Col>
                </Row>
              </Space>
              <Space
                style={{
                  display: "flex",
                  justifyContent: "end",
                  marginTop: "25px",
                }}
              >
                <Button onClick={onCancel} type="primary">
                  Cancel
                </Button>
              </Space>
            </>
          )}
        </Modal>
      )}
    </>
  );
};

export default NameServerGroupAdd;
