import Helper from "components/form-components/Helper";
import InputRow from "components/form-components/InputRow";
import SelectRow from "components/form-components/SelectRow";
import SaveButton from "components/SaveButton";
import { OFState } from "main/home/onboarding/state";
import { DOProvisionerConfig } from "main/home/onboarding/types";
import React, { useEffect, useState } from "react";
import api from "shared/api";
import styled from "styled-components";
import { useSnapshot } from "valtio";
import Loading from "components/Loading";
import { readableDate } from "shared/string_utils";
import { Infrastructure } from "shared/types";

const tierOptions = [
  { value: "basic", label: "Basic" },
  { value: "professional", label: "Professional" },
];

const regionOptions = [
  { value: "ams3", label: "Amsterdam 3" },
  { value: "blr1", label: "Bangalore 1" },
  { value: "fra1", label: "Frankfurt 1" },
  { value: "lon1", label: "London 1" },
  { value: "nyc1", label: "New York 1" },
  { value: "nyc3", label: "New York 3" },
  { value: "sfo2", label: "San Francisco 2" },
  { value: "sfo3", label: "San Francisco 3" },
  { value: "sgp1", label: "Singapore 1" },
  { value: "tor1", label: "Toronto 1" },
];

/**
 * This will redirect to DO, and we should pass the redirection URI to be /onboarding/provision?provider=do
 *
 * After the oauth flow comes back, the first render will go and check if it exists a integration_id for DO in the
 * current onboarding project, after getting it, the CredentialsForm will use nextFormStep to save the onboarding state.
 *
 * If it happens to be an error, it will be shown with the default error handling through the modal.
 */
export const CredentialsForm: React.FC<{
  nextFormStep: (data: Partial<DOProvisionerConfig>) => void;
  project: any;
}> = ({ nextFormStep, project }) => {
  const snap = useSnapshot(OFState);

  const [isLoading, setIsLoading] = useState(true);
  const [connectedAccount, setConnectedAccount] = useState(null);

  useEffect(() => {
    api.getOAuthIds("<token>", {}, { project_id: project?.id }).then((res) => {
      let integrations = res.data.filter((integration: any) => {
        return integration.client === "do";
      });

      if (Array.isArray(integrations) && integrations.length) {
        // Sort decendant
        integrations.sort((a, b) => b.id - a.id);
        let lastUsed = integrations.find((i) => {
          i.id === snap.StateHandler?.provision_resources?.credentials?.id;
        });
        if (!lastUsed) {
          lastUsed = integrations[0];
        }
        setConnectedAccount(lastUsed);
      }
      setIsLoading(false);
    });
  }, []);

  const submit = (integrationId: number) => {
    nextFormStep({
      credentials: {
        id: integrationId,
      },
    });
  };

  const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;

  const encoded_redirect_uri = encodeURIComponent(url);

  if (isLoading) {
    return <Loading />;
  }

  let content = "Project name: n/a";

  if (connectedAccount?.target_email) {
    content = `${connectedAccount?.target_email}`;
  }

  if (connectedAccount?.target_id) {
    content = `${connectedAccount?.target_id}`;
  }

  return (
    <>
      {connectedAccount !== null && (
        <>
          <Helper>Connected account:</Helper>
          <PreviewRow>
            <Flex>
              <i className="material-icons">account_circle</i>
              {content}
            </Flex>
            <Right>
              Connected at {readableDate(connectedAccount.created_at)}
            </Right>
          </PreviewRow>
        </>
      )}
      {connectedAccount !== null ? (
        <Helper>
          Want to use a different account?{" "}
          <A
            href={`/api/projects/${project?.id}/oauth/digitalocean?redirect_uri=${encoded_redirect_uri}`}
          >
            Sign in to DigitalOcean
          </A>
          .
        </Helper>
      ) : (
        <ConnectDigitalOceanButton
          href={`/api/projects/${project?.id}/oauth/digitalocean?redirect_uri=${encoded_redirect_uri}`}
        >
          Sign In to DigitalOcean
        </ConnectDigitalOceanButton>
      )}

      <Br height="5px" />
      {connectedAccount !== null && (
        <SaveButton
          text="Continue"
          disabled={false}
          onClick={() => submit(connectedAccount.id)}
          makeFlush={true}
          clearPosition={true}
          status={""}
          statusPosition={"right"}
        />
      )}
    </>
  );
};

export const SettingsForm: React.FC<{
  nextFormStep: (data: Partial<DOProvisionerConfig>) => void;
  project: any;
}> = ({ nextFormStep, project }) => {
  const snap = useSnapshot(OFState);
  const [buttonStatus, setButtonStatus] = useState("");
  const [tier, setTier] = useState("basic");
  const [region, setRegion] = useState("nyc1");
  const [clusterName, setClusterName] = useState(`${project.name}-cluster`);
  const [currDOKSInfra, setCurrDOKSInfra] = useState<Infrastructure>();
  const [currDOCRInfra, setCurrDOCRInfra] = useState<Infrastructure>();

  useEffect(() => {
    if (!project) {
      return;
    }

    api
      .getInfra<Infrastructure[]>("<token>", {}, { project_id: project.id })
      .then(({ data }) => {
        let sortFunc = (a: Infrastructure, b: Infrastructure) => {
          return b.id < a.id ? -1 : b.id > a.id ? 1 : 0;
        };

        const matchedDOKSInfras = data
          .filter((infra) => infra.kind == "doks")
          .sort(sortFunc);
        const matchedDOCRInfras = data
          .filter((infra) => infra.kind == "docr")
          .sort(sortFunc);

        if (matchedDOKSInfras.length > 0) {
          // get the infra with latest operation details from the API
          api
            .getInfraByID(
              "<token>",
              {},
              { project_id: project.id, infra_id: matchedDOKSInfras[0].id }
            )
            .then(({ data }) => {
              setCurrDOKSInfra(data);
            })
            .catch((err) => {
              console.error(err);
            });
        }

        if (matchedDOCRInfras.length > 0) {
          api
            .getInfraByID(
              "<token>",
              {},
              { project_id: project.id, infra_id: matchedDOCRInfras[0].id }
            )
            .then(({ data }) => {
              setCurrDOCRInfra(data);
            })
            .catch((err) => {
              console.error(err);
            });
        }
      })
      .catch((err) => {});
  }, [project]);

  const validate = () => {
    if (!clusterName) {
      return {
        hasError: true,
        error: "Cluster name cannot be empty",
      };
    }
    if (clusterName.length > 25) {
      return {
        hasError: true,
        error: "Cluster name cannot be longer than 25 characters",
      };
    }
    return {
      hasError: false,
      error: "",
    };
  };

  const catchError = (error: any) => {
    console.error(error);
  };

  const hasRegistryProvisioned = (
    infras: { kind: string; status: string }[]
  ) => {
    return !!infras.find(
      (i) => ["docr", "docr", "ecr"].includes(i.kind) && i.status === "created"
    );
  };

  const hasClusterProvisioned = (
    infras: { kind: string; status: string }[]
  ) => {
    return !!infras.find(
      (i) => ["doks", "gks", "eks"].includes(i.kind) && i.status === "created"
    );
  };

  const provisionDOCR = async (integrationId: number, tier: string) => {
    // console.log("Provisioning DOCR...");

    // See if there's an infra for DOKS that is in an errored state and the last operation
    // was an attempt at creation. If so, re-use that infra.
    if (
      currDOCRInfra?.latest_operation?.type == "create" ||
      currDOCRInfra?.latest_operation?.type == "retry_create"
    ) {
      try {
        const res = await api.retryCreateInfra(
          "<token>",
          {
            do_integration_id: integrationId,
            values: {
              docr_name: project.name,
              docr_subscription_tier: tier,
            },
          },
          { project_id: project.id, infra_id: currDOCRInfra.id }
        );
        return res?.data;
      } catch (error) {
        return catchError(error);
      }
    } else {
      try {
        return await api
          .provisionInfra(
            "<token>",
            {
              kind: "docr",
              do_integration_id: integrationId,
              values: {
                docr_name: project.name,
                docr_subscription_tier: tier,
              },
            },
            {
              project_id: project.id,
            }
          )
          .then((res) => res?.data);
      } catch (error) {
        catchError(error);
      }
    }
  };

  const provisionDOKS = async (
    integrationId: number,
    region: string,
    clusterName: string
  ) => {
    // console.log("Provisioning DOKS...");

    // See if there's an infra for DOKS that is in an errored state and the last operation
    // was an attempt at creation. If so, re-use that infra.
    if (
      currDOKSInfra?.latest_operation?.type == "create" ||
      currDOKSInfra?.latest_operation?.type == "retry_create"
    ) {
      try {
        const res = await api.retryCreateInfra(
          "<token>",
          {
            do_integration_id: integrationId,
            values: {
              cluster_name: clusterName,
              do_region: region,
              issuer_email: snap.StateHandler.user_email,
            },
          },
          { project_id: project.id, infra_id: currDOKSInfra.id }
        );
        return res?.data;
      } catch (error) {
        return catchError(error);
      }
    } else {
      try {
        return await api
          .provisionInfra(
            "<token>",
            {
              kind: "doks",
              do_integration_id: integrationId,
              values: {
                cluster_name: clusterName,
                do_region: region,
                issuer_email: snap.StateHandler.user_email,
              },
            },
            {
              project_id: project.id,
            }
          )
          .then((res) => res?.data);
      } catch (error) {
        catchError(error);
      }
    }
  };

  const submit = async () => {
    const validation = validate();

    if (validation.hasError) {
      setButtonStatus(validation.error);
      return;
    }

    let infras = [];
    try {
      infras = await api
        .getInfra("<token>", {}, { project_id: project?.id })
        .then((res) => res?.data);
    } catch (error) {
      setButtonStatus("Something went wrong, try again later");
      return;
    }

    const integrationId = snap.StateHandler.provision_resources.credentials.id;
    let registryProvisionResponse = null;
    let clusterProvisionResponse = null;

    if (snap.StateHandler.connected_registry.skip) {
      if (!hasRegistryProvisioned(infras)) {
        registryProvisionResponse = await provisionDOCR(integrationId, tier);
      }
    }

    if (!hasClusterProvisioned(infras)) {
      clusterProvisionResponse = await provisionDOKS(
        integrationId,
        region,
        clusterName
      );
    }

    nextFormStep({
      settings: {
        region,
        tier,
        cluster_name: clusterName,
        registry_infra_id: registryProvisionResponse?.id,
        cluster_infra_id: clusterProvisionResponse?.id,
      },
    });
  };

  return (
    <>
      <SelectRow
        options={tierOptions}
        width="100%"
        value={tier}
        setActiveValue={(x: string) => {
          setTier(x);
        }}
        label="💰 Subscription Tier"
      />
      <SelectRow
        options={regionOptions}
        width="100%"
        dropdownMaxHeight="240px"
        value={region}
        setActiveValue={(x: string) => {
          setRegion(x);
        }}
        label="📍 DigitalOcean Region"
      />
      <InputRow
        type="text"
        value={clusterName}
        setValue={(x: string) => {
          setClusterName(x);
        }}
        label="Cluster Name"
        placeholder="ex: porter-cluster"
        width="100%"
        isRequired={true}
      />
      <Br />
      <SaveButton
        text="Provision resources"
        disabled={false}
        onClick={submit}
        makeFlush={true}
        clearPosition={true}
        status={buttonStatus}
        statusPosition={"right"}
      />
    </>
  );
};

const Right = styled.div`
  text-align: right;
  margin-left: 10px;
`;

const A = styled.a`
  cursor: pointer;
`;

const Flex = styled.div`
  display: flex;
  color: #ffffff;
  align-items: center;
  > i {
    color: #aaaabb;
    font-size: 20px;
    margin-right: 10px;
  }
`;

const PreviewRow = styled.div`
  display: flex;
  align-items: center;
  padding: 12px 15px;
  color: #ffffff55;
  background: #ffffff11;
  border: 1px solid #aaaabb;
  justify-content: space-between;
  font-size: 13px;
  border-radius: 5px;
`;

const Br = styled.div<{ height?: string }>`
  width: 100%;
  height: ${(props) => props.height || "15px"};
`;

const CodeBlock = styled.span`
  display: inline-block;
  background-color: #1b1d26;
  color: white;
  border-radius: 5px;
  font-family: monospace;
  padding: 2px 3px;
  margin-top: -2px;
  user-select: text;
`;

const ConnectDigitalOceanButton = styled.a`
  width: 200px;
  justify-content: center;
  border-radius: 5px;
  display: flex;
  flex-direction: row;
  align-items: center;
  font-size: 13px;
  margin-top: 22px;
  cursor: pointer;
  font-family: "Work Sans", sans-serif;
  color: white;
  font-weight: 500;
  padding: 10px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  box-shadow: 0 5px 8px 0px #00000010;
  cursor: ${(props: { disabled?: boolean }) =>
    props.disabled ? "not-allowed" : "pointer"};

  background: ${(props: { disabled?: boolean }) =>
    props.disabled ? "#aaaabbee" : "#616FEEcc"};
  :hover {
    background: ${(props: { disabled?: boolean }) =>
      props.disabled ? "" : "#505edddd"};
  }

  > i {
    color: white;
    width: 18px;
    height: 18px;
    font-weight: 600;
    font-size: 12px;
    border-radius: 20px;
    display: flex;
    align-items: center;
    margin-right: 5px;
    justify-content: center;
  }
`;
