import { memo, useRef } from "react";
import { useTranslation } from "react-i18next";
import {
  CloseCircleOutlined as CloseCircleOutlinedIcon,
  EllipsisOutlined as EllipsisOutlinedIcon,
  FormOutlined as FormOutlinedIcon,
  MoreOutlined as MoreOutlinedIcon,
} from "@ant-design/icons";
import { useControllableValue } from "ahooks";
import { Button, Card, Drawer, Dropdown, Input, type InputRef, Modal, Popover, Space } from "antd";
import { produce } from "immer";
import { isEqual } from "radash";

import { type WorkflowNode, WorkflowNodeType } from "@/domain/workflow";
import { useZustandShallowSelector } from "@/hooks";
import { useWorkflowStore } from "@/stores/workflow";

import AddNode from "./AddNode";

export type SharedNodeProps = {
  node: WorkflowNode;
  disabled?: boolean;
};

// #region Title
type SharedNodeTitleProps = SharedNodeProps & {
  className?: string;
  style?: React.CSSProperties;
};

const SharedNodeTitle = ({ className, style, node, disabled }: SharedNodeTitleProps) => {
  const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"]));

  const handleBlur = (e: React.FocusEvent<HTMLDivElement>) => {
    const oldName = node.name;
    const newName = e.target.innerText.trim().substring(0, 64) || oldName;
    if (oldName === newName) {
      return;
    }

    updateNode(
      produce(node, (draft) => {
        draft.name = newName;
      })
    );
  };

  return (
    <div className="w-full cursor-text overflow-hidden text-center">
      <div className={className} style={style} contentEditable={!disabled} suppressContentEditableWarning onBlur={handleBlur}>
        {node.name}
      </div>
    </div>
  );
};
// #endregion

// #region Menu
type SharedNodeMenuProps = SharedNodeProps & {
  branchId?: string;
  branchIndex?: number;
  trigger: React.ReactNode;
  afterUpdate?: () => void;
  afterDelete?: () => void;
};

const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => {
  const { t } = useTranslation();

  const { updateNode, removeNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch"]));

  const [modalApi, ModelContextHolder] = Modal.useModal();

  const nameInputRef = useRef<InputRef>(null);
  const nameRef = useRef<string>();

  const handleRenameConfirm = async () => {
    const oldName = node.name;
    const newName = nameRef.current?.trim()?.substring(0, 64) || oldName;
    if (oldName === newName) {
      return;
    }

    await updateNode(
      produce(node, (draft) => {
        draft.name = newName;
      })
    );

    afterUpdate?.();
  };

  const handleDeleteClick = async () => {
    if (node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition) {
      await removeBranch(branchId!, branchIndex!);
    } else {
      await removeNode(node.id);
    }

    afterDelete?.();
  };

  return (
    <>
      {ModelContextHolder}

      <Dropdown
        menu={{
          items: [
            {
              key: "rename",
              disabled: disabled,
              label:
                node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition
                  ? t("workflow_node.action.rename_branch")
                  : t("workflow_node.action.rename_node"),
              icon: <FormOutlinedIcon />,
              onClick: () => {
                nameRef.current = node.name;

                const dialog = modalApi.confirm({
                  title:
                    node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition
                      ? t("workflow_node.action.rename_branch")
                      : t("workflow_node.action.rename_node"),
                  content: (
                    <div className="pb-2 pt-4">
                      <Input
                        ref={nameInputRef}
                        autoFocus
                        defaultValue={node.name}
                        onChange={(e) => (nameRef.current = e.target.value)}
                        onPressEnter={async () => {
                          await handleRenameConfirm();
                          dialog.destroy();
                        }}
                      />
                    </div>
                  ),
                  icon: null,
                  okText: t("common.button.save"),
                  onOk: handleRenameConfirm,
                });
                setTimeout(() => nameInputRef.current?.focus(), 1);
              },
            },
            {
              type: "divider",
            },
            {
              key: "remove",
              disabled: disabled || node.type === WorkflowNodeType.Start,
              label:
                node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition
                  ? t("workflow_node.action.remove_branch")
                  : t("workflow_node.action.remove_node"),
              icon: <CloseCircleOutlinedIcon />,
              danger: true,
              onClick: handleDeleteClick,
            },
          ],
        }}
        trigger={["click"]}
      >
        {trigger}
      </Dropdown>
    </>
  );
};
// #endregion

// #region Wrapper
type SharedNodeBlockProps = SharedNodeProps & {
  children: React.ReactNode;
  onClick?: (e: React.MouseEvent) => void;
};

const SharedNodeBlock = ({ children, node, disabled, onClick }: SharedNodeBlockProps) => {
  const handleNodeClick = (e: React.MouseEvent) => {
    onClick?.(e);
  };

  return (
    <>
      <Popover
        arrow={false}
        content={<SharedNodeMenu node={node} disabled={disabled} trigger={<Button color="primary" icon={<MoreOutlinedIcon />} variant="text" />} />}
        overlayClassName="shadow-md"
        overlayInnerStyle={{ padding: 0 }}
        placement="rightTop"
      >
        <Card className="relative w-[256px] overflow-hidden shadow-md" styles={{ body: { padding: 0 } }} hoverable>
          <div className="bg-primary flex h-[48px] flex-col items-center justify-center truncate px-4 py-2 text-white">
            <SharedNodeTitle
              className="focus:bg-background focus:text-foreground overflow-hidden outline-none focus:rounded-sm"
              node={node}
              disabled={disabled}
            />
          </div>

          <div className="flex cursor-pointer flex-col justify-center px-4 py-2" onClick={handleNodeClick}>
            <div className="overflow-hidden text-sm">{children}</div>
          </div>
        </Card>
      </Popover>

      <AddNode node={node} disabled={disabled} />
    </>
  );
};
// #endregion

// #region EditDrawer
type SharedNodeEditDrawerProps = SharedNodeProps & {
  children: React.ReactNode;
  footer?: boolean;
  loading?: boolean;
  open?: boolean;
  pending?: boolean;
  onOpenChange?: (open: boolean) => void;
  onConfirm: () => void | Promise<unknown>;
  getFormValues: () => NonNullable<unknown>;
};

const SharedNodeConfigDrawer = ({
  children,
  node,
  disabled,
  footer = true,
  loading,
  pending,
  onConfirm,
  getFormValues,
  ...props
}: SharedNodeEditDrawerProps) => {
  const { t } = useTranslation();

  const [modalApi, ModelContextHolder] = Modal.useModal();

  const [open, setOpen] = useControllableValue<boolean>(props, {
    valuePropName: "open",
    defaultValuePropName: "defaultOpen",
    trigger: "onOpenChange",
  });

  const handleConfirmClick = async () => {
    await onConfirm();
    setOpen(false);
  };

  const handleCancelClick = () => {
    if (pending) return;

    setOpen(false);
  };

  const handleClose = () => {
    if (pending) return;

    const oldValues = Object.fromEntries(Object.entries(node.config ?? {}).filter(([_, value]) => value !== null && value !== undefined));
    const newValues = Object.fromEntries(Object.entries(getFormValues()).filter(([_, value]) => value !== null && value !== undefined));
    const changed = !isEqual(oldValues, newValues);

    const { promise, resolve, reject } = Promise.withResolvers();
    if (changed) {
      modalApi.confirm({
        title: t("common.text.operation_confirm"),
        content: t("workflow_node.unsaved_changes.confirm"),
        onOk: () => resolve(void 0),
        onCancel: () => reject(),
      });
    } else {
      resolve(void 0);
    }

    promise.then(() => setOpen(false));
  };

  return (
    <>
      {ModelContextHolder}

      <Drawer
        afterOpenChange={(open) => setOpen(open)}
        destroyOnClose
        extra={
          <SharedNodeMenu
            node={node}
            disabled={disabled}
            trigger={<Button icon={<EllipsisOutlinedIcon />} type="text" />}
            afterDelete={() => {
              setOpen(false);
            }}
          />
        }
        footer={
          !!footer && (
            <Space className="w-full justify-end">
              <Button onClick={handleCancelClick}>{t("common.button.cancel")}</Button>
              <Button disabled={disabled} loading={pending} type="primary" onClick={handleConfirmClick}>
                {t("common.button.save")}
              </Button>
            </Space>
          )
        }
        loading={loading}
        open={open}
        title={<div className="max-w-[480px] truncate">{node.name}</div>}
        width={640}
        onClose={handleClose}
      >
        {children}
      </Drawer>
    </>
  );
};
// #endregion

export default {
  Title: memo(SharedNodeTitle),
  Menu: memo(SharedNodeMenu),
  Block: memo(SharedNodeBlock),
  ConfigDrawer: memo(SharedNodeConfigDrawer),
};