// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import {
  type NodeProps,
  Position,
  type Node,
} from '@xyflow/react';
import { Toolbar } from '../components/NodeComponents.tsx';
import styles from '../../VisProg.module.css';
import {MultiConnectionHandle} from "../components/RuleBasedHandle.tsx";
import {allowOnlyConnectionsFromHandle} from "../HandleRules.ts";
import useFlowStore from '../VisProgStores.tsx';
import { TextField } from '../../../../components/TextField.tsx';
import { MultilineTextField } from '../../../../components/MultilineTextField.tsx';
import {noMatchingLeftRightBelief} from "./BeliefGlobals.ts";

/**
 * The default data structure for a BasicBelief node
 *
 * Represents configuration for a node that activates when a specific condition is met,
 * such as keywords being spoken or emotions detected.
 *
 * @property label: the display label of this BasicBelief node.
 * @property droppable: Whether this node can be dropped from the toolbar (default: true).
 * @property BasicBeliefType - The type of BasicBelief ("keywords" or a custom string).
 * @property BasicBeliefs - The list of keyword BasicBeliefs (if applicable).
 * @property hasReduce - Whether this node supports reduction logic.
 */
export type BasicBeliefNodeData = {
  label: string;
  droppable: boolean;
  belief: BasicBeliefType;
  hasReduce: boolean;
};

// These are all the types a basic belief could be.
export type BasicBeliefType = Keyword | Semantic | DetectedObject | Emotion
type Keyword = { type: "keyword", id: string, value: string, label: "Keyword said:"};
type Semantic = { type: "semantic", id: string, value: string, description: string, label: "Detected with LLM:"};
type DetectedObject = { type: "object", id: string, value: string, label: "Object found:"};
type Emotion = { type: "emotion", id: string, value: string, label: "Emotion recognised:"};

export type BasicBeliefNode = Node<BasicBeliefNodeData>

// update the tooltip to reflect newly added connection options for a belief
export const BasicBeliefTooltip = `
  A belief describes a condition that must be met
  in order for a connected norm to be activated`;

/**
 * This function is called whenever a connection is made with this node type as the target
 * @param _thisNode the node of this node type which function is called
 * @param _sourceNodeId the source of the received connection
 */
export function BasicBeliefConnectionTarget(_thisNode: Node, _sourceNodeId: string) {
  // no additional connection logic exists yet
}

/**
 * This function is called whenever a connection is made with this node type as the source
 * @param _thisNode the node of this node type which function is called
 * @param _targetNodeId the target of the created connection
 */
export function BasicBeliefConnectionSource(_thisNode: Node, _targetNodeId: string) {
  // no additional connection logic exists yet
}

/**
 * This function is called whenever a connection is disconnected with this node type as the target
 * @param _thisNode the node of this node type which function is called
 * @param _sourceNodeId the source of the disconnected connection
 */
export function BasicBeliefDisconnectionTarget(_thisNode: Node, _sourceNodeId: string) {
  // no additional connection logic exists yet
}

/**
 * This function is called whenever a connection is disconnected with this node type as the source
 * @param _thisNode the node of this node type which function is called
 * @param _targetNodeId the target of the diconnected connection
 */
export function BasicBeliefDisconnectionSource(_thisNode: Node, _targetNodeId: string) {
  // no additional connection logic exists yet
}

/**
 * Defines how a BasicBelief node should be rendered
 * @param props - Node properties provided by React Flow, including `id` and `data`.
 * @returns The rendered BasicBeliefNode React element (React.JSX.Element).
 */
export default function BasicBeliefNode(props: NodeProps<BasicBeliefNode>) {
  const data = props.data;
  const {updateNodeData} = useFlowStore();
  const updateValue = (value: string) => updateNodeData(props.id, {...data, belief: {...data.belief, value: value}});
  const label_input_id = `basic_belief_${props.id}_label_input`;

  type BeliefString = BasicBeliefType["type"];

  function updateBeliefType(newType: BeliefString) {
    updateNodeData(props.id, {
      ...data,
      belief: {
        ...data.belief,
        type: newType,
        value:
          newType === "emotion"
            ? emotionOptions[0] 
            : data.belief.value,               
      },
    });
  }


  const setBeliefDescription = (value: string) => {
    updateNodeData(props.id, {...data, belief: {...data.belief, description: value}});
  }

  const emotionOptions = ["sad", "angry", "surprise", "fear", "happy", "disgust", "neutral"];

  let placeholder = ""
  let wrapping = ""
  switch (props.data.belief.type) {
    case ("keyword"):
      placeholder = "keyword..."
      wrapping = '"'
      break; 
    case ("semantic"):
      placeholder = "short description..."
      wrapping = '"'
      break; 
    case ("object"):
      placeholder = "object..."
      break; 
    case ("emotion"):
      // TODO: emotion should probably be a drop-down menu rather than a string
      // So this placeholder won't hold for always
      placeholder = "emotion..."
      break;
    default:
      break;
  }

  return (
    <>
      <Toolbar nodeId={props.id} allowDelete={true}/>
      <div className={`${styles.defaultNode} ${styles.nodeBasicBelief /*TODO: Change this*/}`}>
        <div className={"flex-center-x gap-sm"}>
          <label htmlFor={label_input_id}>Belief:</label>
        </div>
        <div className={"flex-row gap-sm"}>
          <select
            value={data.belief.type}
            onChange={(e) => updateBeliefType(e.target.value as BeliefString)}
            >
            <option value="keyword">Keyword said:</option>
            <option value="semantic">Detected with LLM:</option>
            <option value="object">Object found:</option>
            <option value="emotion">Emotion recognised:</option>
          </select>
          {wrapping}
          {data.belief.type === "emotion" && (
            <select
              value={data.belief.value}
              onChange={(e) => updateValue(e.target.value)}
            >
              {emotionOptions.map((emotion) => (
                <option key={emotion} value={emotion.toLowerCase()}>
                  {emotion}
                </option>
              ))}
            </select>
          )}

          {data.belief.type !== "emotion" && 
          (<TextField
            id={label_input_id}
            value={data.belief.value}
            setValue={updateValue}
            placeholder={placeholder}
          />)}
          {wrapping}
        </div>
        {data.belief.type === "semantic" && (
          <div className={"flex-wrap padding-sm"}>
            <MultilineTextField
                        value={data.belief.description}
                        setValue={setBeliefDescription}
                        placeholder={"Describe a detailed desciption of this LLM belief..."}
                      />
          </div>
          )}
        <MultiConnectionHandle type="source" position={Position.Right} id="source" rules={[
          noMatchingLeftRightBelief,
          allowOnlyConnectionsFromHandle([{nodeType:"trigger",handleId:"TriggerBeliefs"}, {nodeType:"norm",handleId:"NormBeliefs"},{nodeType:"InferredBelief",handleId:"inferred_belief"}]),
        ]}  title="Connect to any number of trigger and/or normNode(-s)"/>
      </div>
    </>
  );
};

/**
 * Reduces each BasicBelief, including its children down into its core data.
 * @param node - The BasicBelief node to reduce.
 * @param _nodes - The list of all nodes in the current flow graph.
 * @returns A simplified object containing the node label and its list of BasicBeliefs.
 */
export function BasicBeliefReduce(node: Node, _nodes: Node[]) {
  const data = node.data as BasicBeliefNodeData;
  const result: Record<string, unknown> = {
        id: node.id, 
  };

  switch (data.belief.type) {
    case "emotion":
      result["emotion"] = data.belief.value;
      break;
    case "keyword":
      result["keyword"] = data.belief.value;
      break;
    case "object":
      result["object"] = data.belief.value;
      break;
    case "semantic":
      result["name"] = data.belief.value;
      result["description"] = data.belief.description;
      break;
    default:
      break;
  }
  return result
}