import React, { useState, useEffect, useRef } from 'react';
import { emit_update } from '../socket';
import { Button, InputGroup, Form, Collapse } from 'react-bootstrap';
import { DocStringComponent } from './DocStringComponent';
import { getIdFromFullAccessPath } from '../utils/stringUtils';

interface MethodProps {
  name: string;
  parentPath: string;
  parameters: Record<string, string>;
  docString?: string;
  hideOutput?: boolean;
  addNotification: (message: string) => void;
}

export const MethodComponent = React.memo((props: MethodProps) => {
  const { name, parentPath, docString, addNotification } = props;

  const renderCount = useRef(0);
  const [hideOutput, setHideOutput] = useState(false);
  // Add a new state variable to hold the list of function calls
  const [functionCalls, setFunctionCalls] = useState([]);
  const id = getIdFromFullAccessPath(parentPath.concat('.' + name));

  useEffect(() => {
    renderCount.current++;
    if (props.hideOutput !== undefined) {
      setHideOutput(props.hideOutput);
    }
  });

  const triggerNotification = (args: Record<string, string>) => {
    const argsString = Object.entries(args)
      .map(([key, value]) => `${key}: "${value}"`)
      .join(', ');
    let message = `Method ${parentPath}.${name} was triggered`;

    if (argsString === '') {
      message += '.';
    } else {
      message += ` with arguments {${argsString}}.`;
    }
    addNotification(message);
  };

  const execute = async (event: React.FormEvent) => {
    event.preventDefault();

    const args = {};
    Object.keys(props.parameters).forEach(
      (name) => (args[name] = event.target[name].value)
    );
    emit_update(name, parentPath, { args: args }, (ack) => {
      // Update the functionCalls state with the new call if we get an acknowledge msg
      if (ack !== undefined) {
        setFunctionCalls((prevCalls) => [...prevCalls, { name, args, result: ack }]);
      }
    });

    triggerNotification(args);
  };

  const args = Object.entries(props.parameters).map(([name, type], index) => {
    const form_name = `${name} (${type})`;
    return (
      <InputGroup key={index}>
        <InputGroup.Text className="component-label">{form_name}</InputGroup.Text>
        <Form.Control type="text" name={name} />
      </InputGroup>
    );
  });

  return (
    <div className="align-items-center methodComponent" id={id}>
      {process.env.NODE_ENV === 'development' && (
        <p>Render count: {renderCount.current}</p>
      )}
      <h5 onClick={() => setHideOutput(!hideOutput)} style={{ cursor: 'pointer' }}>
        Function: {name}
        <DocStringComponent docString={docString} />
      </h5>
      <Form onSubmit={execute}>
        {args}
        <div>
          <Button variant="primary" type="submit">
            Execute
          </Button>
        </div>
      </Form>

      <Collapse in={!hideOutput}>
        <div id="function-output">
          {functionCalls.map((call, index) => (
            <div key={index}>
              <div style={{ color: 'grey', fontSize: 'small' }}>
                {Object.entries(call.args)
                  .map(([key, val]) => `${key}=${JSON.stringify(val)}`)
                  .join(', ') +
                  ' => ' +
                  JSON.stringify(call.result)}
              </div>
            </div>
          ))}
        </div>
      </Collapse>
    </div>
  );
});