import {
  ActionGroup,
  AlertVariant,
  Button,
  ButtonVariant,
  ExpandableSection,
  FormGroup,
  InputGroup,
  PageSection,
  Split,
  SplitItem,
  Text,
  TextInput,
  ToolbarItem,
  Tooltip,
} from "@patternfly/react-core";
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import type GlobalRequestResult from "@keycloak/keycloak-admin-client/lib/defs/globalRequestResult";
import moment from "moment";
import React, { useEffect, useRef, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { useAlerts } from "../components/alert/Alerts";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { FormAccess } from "../components/form-access/FormAccess";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { ScrollForm } from "../components/scroll-form/ScrollForm";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { TimeSelector } from "../components/time-selector/TimeSelector";
import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { convertToFormValues, toUpperCase } from "../util";
import { AddHostDialog } from "./advanced/AddHostDialog";
import { AdvancedSettings } from "./advanced/AdvancedSettings";
import { AuthenticationOverrides } from "./advanced/AuthenticationOverrides";
import { FineGrainOpenIdConnect } from "./advanced/FineGrainOpenIdConnect";
import { FineGrainSamlEndpointConfig } from "./advanced/FineGrainSamlEndpointConfig";
import { OpenIdConnectCompatibilityModes } from "./advanced/OpenIdConnectCompatibilityModes";
import type { SaveOptions } from "./ClientDetails";
import { toClient } from "./routes/Client";

type AdvancedProps = {
  save: (options?: SaveOptions) => void;
  client: ClientRepresentation;
};

export const AdvancedTab = ({
  save,
  client: {
    id,
    publicClient,
    registeredNodes,
    attributes,
    protocol,
    authenticationFlowBindingOverrides,
    adminUrl,
  },
}: AdvancedProps) => {
  const { t } = useTranslation("clients");
  const adminClient = useAdminClient();
  const { realm } = useRealm();
  const { addAlert, addError } = useAlerts();
  const revocationFieldName = "notBefore";
  const openIdConnect = "openid-connect";

  const { getValues, setValue, register, control, reset } = useFormContext();
  const [expanded, setExpanded] = useState(false);
  const [selectedNode, setSelectedNode] = useState("");
  const [addNodeOpen, setAddNodeOpen] = useState(false);
  const [key, setKey] = useState(0);
  const refresh = () => setKey(new Date().getTime());
  const [nodes, setNodes] = useState(registeredNodes || {});
  const pushRevocationButtonRef = useRef<HTMLElement>();

  const setNotBefore = (time: number, messageKey: string) => {
    setValue(revocationFieldName, time);
    save({ messageKey });
  };

  const parseResult = (result: GlobalRequestResult, prefixKey: string) => {
    const successCount = result.successRequests?.length || 0;
    const failedCount = result.failedRequests?.length || 0;

    if (successCount === 0 && failedCount === 0) {
      addAlert(t("noAdminUrlSet"), AlertVariant.warning);
    } else if (failedCount > 0) {
      addAlert(
        t(prefixKey + "Success", { successNodes: result.successRequests }),
        AlertVariant.success
      );
      addAlert(
        t(prefixKey + "Fail", { failedNodes: result.failedRequests }),
        AlertVariant.danger
      );
    } else {
      addAlert(
        t(prefixKey + "Success", { successNodes: result.successRequests }),
        AlertVariant.success
      );
    }
  };

  const resetFields = (names: string[]) => {
    const values: { [name: string]: string } = {};
    for (const name of names) {
      values[`attributes.${name}`] = attributes?.[name];
    }
    reset(values);
  };

  const push = async () => {
    const result = await adminClient.clients.pushRevocation({
      id: id!,
    });
    parseResult(result, "notBeforePush");
  };

  const testCluster = async () => {
    const result = await adminClient.clients.testNodesAvailable({ id: id! });
    parseResult(result, "testCluster");
  };

  const [toggleDeleteNodeConfirm, DeleteNodeConfirm] = useConfirmDialog({
    titleKey: "clients:deleteNode",
    messageKey: t("deleteNodeBody", {
      node: selectedNode,
    }),
    continueButtonLabel: "common:delete",
    continueButtonVariant: ButtonVariant.danger,
    onConfirm: async () => {
      try {
        await adminClient.clients.deleteClusterNode({
          id: id!,
          node: selectedNode,
        });
        setNodes({
          ...Object.keys(nodes).reduce((object: any, key) => {
            if (key !== selectedNode) {
              object[key] = nodes[key];
            }
            return object;
          }, {}),
        });
        refresh();
        addAlert(t("deleteNodeSuccess"), AlertVariant.success);
      } catch (error) {
        addError("clients:deleteNodeFail", error);
      }
    },
  });

  useEffect(() => {
    register(revocationFieldName);
  }, [register]);

  const formatDate = () => {
    const date = getValues(revocationFieldName);
    if (date > 0) {
      return moment(date * 1000).format("LLL");
    } else {
      return t("common:none");
    }
  };

  const sections = [
    t("revocation"),
    t("clustering"),
    protocol === openIdConnect
      ? t("fineGrainOpenIdConnectConfiguration")
      : t("fineGrainSamlEndpointConfig"),
    t("advancedSettings"),
    t("authenticationOverrides"),
  ];
  if (protocol === openIdConnect) {
    sections.splice(3, 0, t("openIdConnectCompatibilityModes"));
  }
  if (!publicClient) {
    sections.splice(1, 1);
  }
  if (protocol !== openIdConnect) {
    sections.splice(0, 1);
  }

  return (
    <PageSection variant="light" className="pf-u-py-0">
      <ScrollForm sections={sections} borders>
        {protocol === openIdConnect && (
          <>
            <Text className="pf-u-pb-lg">
              <Trans i18nKey="clients-help:notBeforeIntro">
                In order to successfully push setup url on
                <Link to={toClient({ realm, clientId: id!, tab: "settings" })}>
                  {t("settings")}
                </Link>
                tab
              </Trans>
            </Text>
            <FormAccess role="manage-clients" isHorizontal>
              <FormGroup
                label={t("notBefore")}
                fieldId="kc-not-before"
                labelIcon={
                  <HelpItem
                    helpText="clients-help:notBefore"
                    fieldLabelId="clients:notBefore"
                  />
                }
              >
                <InputGroup>
                  <TextInput
                    type="text"
                    id="kc-not-before"
                    name="notBefore"
                    isReadOnly
                    value={formatDate()}
                  />
                  <Button
                    id="setToNow"
                    variant="control"
                    onClick={() => {
                      setNotBefore(moment.now() / 1000, "notBeforeSetToNow");
                    }}
                  >
                    {t("setToNow")}
                  </Button>
                  <Button
                    id="clear"
                    variant="control"
                    onClick={() => {
                      setNotBefore(0, "notBeforeNowClear");
                    }}
                  >
                    {t("clear")}
                  </Button>
                </InputGroup>
              </FormGroup>
              <ActionGroup>
                {!adminUrl && (
                  <Tooltip
                    reference={pushRevocationButtonRef}
                    content={t("clients-help:notBeforeTooltip")}
                  />
                )}
                <Button
                  id="push"
                  variant="secondary"
                  onClick={push}
                  isAriaDisabled={!adminUrl}
                  ref={pushRevocationButtonRef}
                >
                  {t("push")}
                </Button>
              </ActionGroup>
            </FormAccess>
          </>
        )}
        {publicClient && (
          <>
            <FormAccess role="manage-clients" isHorizontal>
              <FormGroup
                label={t("nodeReRegistrationTimeout")}
                fieldId="kc-node-reregistration-timeout"
                labelIcon={
                  <HelpItem
                    helpText="clients-help:nodeReRegistrationTimeout"
                    fieldLabelId="clients:nodeReRegistrationTimeout"
                  />
                }
              >
                <Split hasGutter>
                  <SplitItem>
                    <Controller
                      name="nodeReRegistrationTimeout"
                      defaultValue=""
                      control={control}
                      render={({ onChange, value }) => (
                        <TimeSelector value={value} onChange={onChange} />
                      )}
                    />
                  </SplitItem>
                  <SplitItem>
                    <Button
                      variant={ButtonVariant.secondary}
                      onClick={() => save()}
                    >
                      {t("common:save")}
                    </Button>
                  </SplitItem>
                </Split>
              </FormGroup>
            </FormAccess>
            <>
              <DeleteNodeConfirm />
              <AddHostDialog
                clientId={id!}
                isOpen={addNodeOpen}
                onAdded={(node) => {
                  nodes[node] = moment.now() / 1000;
                  refresh();
                }}
                onClose={() => setAddNodeOpen(false)}
              />
              <ExpandableSection
                toggleText={t("registeredClusterNodes")}
                onToggle={setExpanded}
                isExpanded={expanded}
              >
                <KeycloakDataTable
                  key={key}
                  ariaLabelKey="registeredClusterNodes"
                  loader={() =>
                    Promise.resolve(
                      Object.entries(nodes || {}).map((entry) => {
                        return { host: entry[0], registration: entry[1] };
                      })
                    )
                  }
                  toolbarItem={
                    <>
                      <ToolbarItem>
                        <Button
                          id="testClusterAvailability"
                          onClick={testCluster}
                          variant={ButtonVariant.secondary}
                          isDisabled={Object.keys(nodes).length === 0}
                        >
                          {t("testClusterAvailability")}
                        </Button>
                      </ToolbarItem>
                      <ToolbarItem>
                        <Button
                          id="registerNodeManually"
                          onClick={() => setAddNodeOpen(true)}
                          variant={ButtonVariant.tertiary}
                        >
                          {t("registerNodeManually")}
                        </Button>
                      </ToolbarItem>
                    </>
                  }
                  actions={[
                    {
                      title: t("common:delete"),
                      onRowClick: (node) => {
                        setSelectedNode(node.host);
                        toggleDeleteNodeConfirm();
                      },
                    },
                  ]}
                  columns={[
                    {
                      name: "host",
                      displayKey: "clients:nodeHost",
                    },
                    {
                      name: "registration",
                      displayKey: "clients:lastRegistration",
                      cellFormatters: [
                        (value) =>
                          value
                            ? moment(parseInt(value.toString()) * 1000).format(
                                "LLL"
                              )
                            : "",
                      ],
                    },
                  ]}
                  emptyState={
                    <ListEmptyState
                      message={t("noNodes")}
                      instructions={t("noNodesInstructions")}
                      primaryActionText={t("registerNodeManually")}
                      onPrimaryAction={() => setAddNodeOpen(true)}
                    />
                  }
                />
              </ExpandableSection>
            </>
          </>
        )}
        <>
          {protocol === openIdConnect && (
            <>
              <Text className="pf-u-pb-lg">
                {t("clients-help:fineGrainOpenIdConnectConfiguration")}
              </Text>
              <FineGrainOpenIdConnect
                save={() => save()}
                reset={() =>
                  convertToFormValues(attributes, (key, value) =>
                    setValue(`attributes.${key}`, value)
                  )
                }
              />
            </>
          )}
          {protocol !== openIdConnect && (
            <>
              <Text className="pf-u-pb-lg">
                {t("clients-help:fineGrainSamlEndpointConfig")}
              </Text>
              <FineGrainSamlEndpointConfig
                control={control}
                save={() => save()}
                reset={() =>
                  convertToFormValues(attributes, (key, value) =>
                    setValue(`attributes.${key}`, value)
                  )
                }
              />
            </>
          )}
        </>
        {protocol === openIdConnect && (
          <>
            <Text className="pf-u-pb-lg">
              {t("clients-help:openIdConnectCompatibilityModes")}
            </Text>
            <OpenIdConnectCompatibilityModes
              control={control}
              save={() => save()}
              reset={() =>
                resetFields(["exclude.session.state.from.auth.response"])
              }
            />
          </>
        )}
        <>
          <Text className="pf-u-pb-lg">
            {t("clients-help:advancedSettings" + toUpperCase(protocol || ""))}
          </Text>
          <AdvancedSettings
            protocol={protocol}
            control={control}
            save={() => save()}
            reset={() => {
              resetFields([
                "saml.assertion.lifespan",
                "access.token.lifespan",
                "tls.client.certificate.bound.access.tokens",
                "pkce.code.challenge.method",
              ]);
            }}
          />
        </>
        <>
          <Text className="pf-u-pb-lg">
            {t("clients-help:authenticationOverrides")}
          </Text>
          <AuthenticationOverrides
            protocol={protocol}
            control={control}
            save={() => save()}
            reset={() => {
              setValue(
                "authenticationFlowBindingOverrides.browser",
                authenticationFlowBindingOverrides?.browser
              );
              setValue(
                "authenticationFlowBindingOverrides.direct_grant",
                authenticationFlowBindingOverrides?.direct_grant
              );
            }}
          />
        </>
      </ScrollForm>
    </PageSection>
  );
};
