import { useAppDispatch, useQuerybuilderGraph, useQuerybuilderHash } from '@graphpolaris/shared/lib/data-access';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import { Handle, Position } from 'reactflow';
import { AllLogicTypes, Handles, LogicNodeAttributes, SchemaReactflowLogicNode, toHandleId } from '../../../model';
import { GeneralDescription, InputNode, InputNodeTypeTypes } from '../../../model/logic/general';
import { styleHandleMap } from '../../utils';
import { setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
import { LogicInput } from './LogicInput';
import { Button, Input, LogicPill } from '@graphpolaris/shared/lib/components';
import { QueryBuilderDispatcherContext } from '../../../panel/QueryBuilderDispatcher';
import { DropdownTrigger, DropdownContainer, DropdownItemContainer, DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns';

export function QueryLogicPill(node: SchemaReactflowLogicNode) {
  const dispatch = useAppDispatch();
  const data = node.data;
  const logic = data.logic;
  const { openLogicPillUpdate: openLogicPillChooser } = useContext(QueryBuilderDispatcherContext);

  const output = data.logic.output;
  const inputReference = useRef<HTMLInputElement>(null);

  const graph = useQuerybuilderGraph();
  const graphologyHash = useQuerybuilderHash();
  const graphologyGraph = useMemo(() => toQuerybuilderGraphology(graph), [graph]);
  const connectionsToLeft = useMemo(() => graph.edges.filter((edge) => edge.target === node.id), [graph]);
  const connectionsToRight = useMemo(() => graph.edges.filter((edge) => edge.source === node.id), [graph]);
  const graphologyNodeAttributes = useMemo<LogicNodeAttributes | undefined>(
    () => (graphologyGraph.hasNode(node.id) ? { ...(graphologyGraph.getNodeAttributes(node.id) as LogicNodeAttributes) } : undefined),
    [node.id],
  );
  const [localInputCache, setLocalInputCache] = useState<Record<string, InputNodeTypeTypes>>({ ...graphologyNodeAttributes?.inputs });
  const [openDropdown, setOpenDropdown] = useState(false);

  if (!data.id) throw new Error('LogicPill: data.id is undefined');
  const defaultHandleData = {
    nodeId: data.id,
    nodeName: data.name,
    nodeType: data.type,
  };

  const onInputUpdated = (value: string, input: InputNode, idx: number) => {
    let logicNode = { ...graphologyNodeAttributes };
    if (!logicNode) throw new Error('LogicPill: logicNode is undefined');

    let logicNodeInputs = { ...logicNode.inputs };
    if (data.inputs[input.name] !== value) {
      logicNodeInputs[input.name] = value;
      logicNode.inputs = logicNodeInputs;
      graphologyGraph.setNodeAttribute<any>(node.id, 'inputs', logicNodeInputs); // FIXME: I'm not sure why TS requires <any> to work here
      dispatch(setQuerybuilderGraphology(graphologyGraph));
    }
  };

  function removeNode() {
    graphologyGraph.dropNode(node.id);
    dispatch(setQuerybuilderGraphology(graphologyGraph));
  }

  useEffect(() => {
    if (inputReference?.current) inputReference.current.focus();
  }, [node.id]);

  // FIXME: This is a temporary fix to prevent the logic pill from rendering when the input is not set
  if (!logic.input) {
    console.error('LogicPill: logic.input is undefined', logic.input);
    return null;
  }

  console.log('node', node);

  return (
    <LogicPill
      title={
        <div className="flex flex-row justify-between items-center">
          <span>{connectionsToLeft[0]?.attributes?.sourceHandleData.attributeName}</span>
          <DropdownContainer>
            <DropdownTrigger size="md">
              <Button
                variantType="secondary"
                variant="ghost"
                size="2xs"
                iconComponent={openDropdown ? 'icon-[ic--baseline-arrow-drop-up]' : 'icon-[ic--baseline-arrow-drop-down]'}
                className={openDropdown ? 'border-secondary-200' : ''}
              />
            </DropdownTrigger>

            <DropdownItemContainer>
              <DropdownItem value="Remove" className="text-danger" onClick={(e) => removeNode()} />
            </DropdownItemContainer>
          </DropdownContainer>
        </div>
      }
      handleLeft={
        <Handle
          className={'!rounded-none !bg-transparent !w-full !h-full !border-0 !right-0 !left-0'}
          type="target"
          position={Position.Left}
          id={toHandleId({
            ...defaultHandleData,
            attributeName: logic.input.name,
            attributeType: logic.input.type,
            handleType: Handles.LogicLeft,
          })}
        ></Handle>
      }
    >
      <div className={`py-1 h-fit border-[1px] border-secondary-200 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-100'}`}>
        {/* <div className="m-1 mx-2 text-left">{output.name}</div> */}
        <DropdownTrigger
          popover={false}
          title={output.name}
          variant="ghost"
          size="xs"
          onClick={() => {
            openLogicPillChooser(node);
          }}
        />
        {node.data.logic.inputs.map((input, i) => {
          const connection = connectionsToLeft.find(
            (edge) =>
              edge?.attributes?.targetHandleData.nodeId === data.id && edge?.attributes?.targetHandleData.attributeName === input.name,
          );

          return (
            <div key={i}>
              <div className="w-full flex">
                {!connection ? (
                  <LogicInput
                    value={localInputCache?.[input.name] as string}
                    type={input.type}
                    onChange={(value: string) => {
                      setLocalInputCache({ ...localInputCache, [input.name]: value });
                    }}
                    onEnter={() => onInputUpdated(localInputCache?.[input.name] as string, input, i)}
                    onBlur={() => onInputUpdated(localInputCache?.[input.name] as string, input, i)}
                  />
                ) : (
                  <span className="px-1 m-0 mx-1 p-0 h-5 w-full rounded-sm border-[1px]">
                    {connection?.attributes?.sourceHandleData.attributeName}
                  </span>
                )}
              </div>
              <Handle
                type={'target'}
                position={Position.Left}
                id={toHandleId({
                  ...defaultHandleData,
                  attributeName: input.name,
                  attributeType: input.type,
                  handleType: Handles.LogicLeft,
                })}
                key={input.name + input.type}
                style={{ top: `${((i + 1.05) / (node.data.logic.inputs.length + 0.4)) * 100}%` }}
                className={styleHandleMap[input.type] + ''}
              ></Handle>
            </div>
          );
        })}
        {['upper_case', 'lower_case'].includes(node.data.logic.output.name) && (
          <Handle
            type={'source'}
            position={Position.Right}
            id={toHandleId({
              ...defaultHandleData,
              attributeName: output.name,
              attributeType: output.type,
              handleType: Handles.LogicRight,
            })}
            className={styleHandleMap?.[output.type]}
          ></Handle>
        )}
      </div>
    </LogicPill>
  );
}