import React, { useCallback, useEffect, useState } from "react";
import { LazyLoad } from "@/components/LazyLoad";
import { GlobalContext, setGLobalProperty, showToast } from "@/context/Global";
import { RouteEditorMenu } from "./RouteEditorMenu";
import {
  APIRouteConfigTabs,
  DragSegmentsTypes,
  WorkflowTypes,
} from "@/utils/constants";
import { RouteEditorWorkflow } from "./RouteEditorWorkflow";
import { generateUUID } from "@/utils/utils";
import { buildWorkflowCode } from "@/utils/options/DataTypes";

const RouteEditor = ({ handleEndpointSave, saveRef }) => {
  const {
    dispatch: globalDispatch,
    state: { selectedRoute, selectedWorkflow, isRouteSaved },
  } = React.useContext(GlobalContext);

  const [collapse, setCollapse] = useState(false);
  const [ready, setReady] = useState(false);
  const [variables, setVariables] = useState({
    var: [],
    actions: [],
  });

  const [workflows, setWorkflows] = useState([
    {
      id: generateUUID(),
      name: WorkflowTypes.APIRoute,
      configurations: {
        [APIRouteConfigTabs.General]: {
          name: "",
          description: "",
          route: "",
          method: "GET",
          folder: "",
        },
        [APIRouteConfigTabs.Authentication]: {
          protected: false,
          authorizedRoles: [],
        },
        [APIRouteConfigTabs.Api_Input]: { inputs: [] },
      },
    },
  ]);

  const handleColumns = (response) => {
    const columns = {}
    response?.forEach((item) => columns[item.name] = item.dataType)
    return columns;
  }

  const handleSave = useCallback(() => {
    // transform workflow to endpoint
    // const tempWorkflows = workflows;
    // const defaultConfig = tempWorkflows[0];
    const result = buildWorkflowCode(selectedRoute);

    const endpoint = {
      ...selectedRoute,
      code: result.join("\n"),
    };
    handleEndpointSave(endpoint);
  }, [workflows, selectedRoute]);

  // Add Workflow on drop
  const setWorkflowData = useCallback(
    (workflowId, segment) => {
      const tempWorkflows = workflows;
      const position = tempWorkflows.findIndex(
        (workflow) => workflow.id === workflowId
      );
      if (position === -1) {
        return;
      }
      if (!segment) {
        const responseFlow = tempWorkflows?.find(
          (workflow) => workflow?.name === WorkflowTypes.SuccessResponse
        );

        if (
          selectedWorkflow?.name === WorkflowTypes.SuccessResponse &&
          responseFlow
        ) {
          showToast(
            globalDispatch,
            `Cannot have Multiple Responses on main tree`,
            4000,
            "error"
          );
        } else {
          tempWorkflows.splice(position + 1, 0, {
            id: generateUUID(),
            ...selectedWorkflow,
          });
          setWorkflows(() => [...tempWorkflows]);
        }
        updateSelectedRoute(tempWorkflows);
        setGLobalProperty(globalDispatch, null, "selectedWorkflow");
        if (isRouteSaved) {
          setGLobalProperty(globalDispatch, false, "isRouteSaved");
        }
        return;
      }
      const workflow = tempWorkflows[position];

      switch (segment) {
        case DragSegmentsTypes.TRUE:
          workflow["action"][DragSegmentsTypes.TRUE] = {
            ...selectedWorkflow,
          };
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.FALSE:
          workflow["action"][DragSegmentsTypes.FALSE] = {
            ...selectedWorkflow,
          };
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.NEXT:
          tempWorkflows.splice(position + 1, 0, {
            id: generateUUID(),
            ...selectedWorkflow,
          });
          setWorkflows(() => [...tempWorkflows]);
          break;
      }
      updateSelectedRoute(tempWorkflows);
      setGLobalProperty(globalDispatch, null, "selectedWorkflow");
      if (isRouteSaved) {
        setGLobalProperty(globalDispatch, false, "isRouteSaved");
      }
    },
    [selectedWorkflow, workflows, isRouteSaved]
  );

  // Update Workflow
  const updateWorkflow = useCallback(
    (workflowId, data, parentId = null, segment = null) => {
      const tempWorkflows = workflows;
      if ((parentId && !segment) || (!parentId && segment)) {
        return;
      }

      const position = parentId
        ? tempWorkflows.findIndex((workflow) => workflow.id === parentId)
        : tempWorkflows.findIndex((workflow) => workflow.id === workflowId);
      if (position === -1) {
        return;
      }
      const workflow = tempWorkflows[position];
      if (!segment) {
        tempWorkflows.splice(position, 1, data);
        setWorkflows(() => [...tempWorkflows]);
        updateSelectedRoute(tempWorkflows);
        if (isRouteSaved) {
          setGLobalProperty(globalDispatch, false, "isRouteSaved");
        }
        showToast(globalDispatch, `${workflow?.name} updated`, 4000, "success");
        return;
      }
      switch (segment) {
        case DragSegmentsTypes.TRUE:
          workflow["action"][DragSegmentsTypes.TRUE] = data;
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.FALSE:
          workflow["action"][DragSegmentsTypes.FALSE] = data;
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.NEXT:
          tempWorkflows.splice(position, 1, data);
          setWorkflows(() => [...tempWorkflows]);
          break;
      }
      updateSelectedRoute(tempWorkflows);
      
      if (isRouteSaved) {
        setGLobalProperty(globalDispatch, false, "isRouteSaved");
      }
      showToast(globalDispatch, `${workflow?.name} updated`, 4000, "success");
    },
    [selectedWorkflow, workflows, isRouteSaved]
  );

  // Delete Workflow Data
  const deleteWorkflowData = useCallback(
    (workflowId, segment = null, parentId = null) => {
      const tempWorkflows = workflows;
      const position = parentId
        ? tempWorkflows.findIndex((workflow) => workflow.id === parentId)
        : tempWorkflows.findIndex((workflow) => workflow.id === workflowId);
      if (position === -1) {
        return;
      }
      if (parentId && !segment) {
        return;
      }

      if (!segment) {
        tempWorkflows.splice(position, 1);
        setWorkflows(() => [...tempWorkflows]);
        updateSelectedRoute(tempWorkflows);
        if (isRouteSaved) {
          setGLobalProperty(globalDispatch, false, "isRouteSaved");
        }
        return;
      }
      const workflow = tempWorkflows[position];
      switch (segment) {
        case DragSegmentsTypes.TRUE:
          workflow["action"][DragSegmentsTypes.TRUE] = null;
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.FALSE:
          workflow["action"][DragSegmentsTypes.FALSE] = null;
          tempWorkflows.splice(position, 1, workflow);
          setWorkflows(() => [...tempWorkflows]);
          break;
        case DragSegmentsTypes.NEXT:
          tempWorkflows.splice(position, 1);
          setWorkflows(() => [...tempWorkflows]);
          break;
      }
      updateSelectedRoute(tempWorkflows);
      if (isRouteSaved) {
        setGLobalProperty(globalDispatch, false, "isRouteSaved");
      }
    },
    [selectedWorkflow, workflows, isRouteSaved]
  );

  // Add new if condition comparison
  const addNewCondition = useCallback(
    (workflowId) => {
      const tempWorkflows = workflows;
      const position = tempWorkflows.findIndex(
        (workflow) => workflow?.id === workflowId
      );
      if (position === -1) return;

      const workflow = tempWorkflows[position];

      workflow.conditions.push({
        id: generateUUID(),
        left_operand: "", // identifier for 'a'
        operator: "", // identifier for '==='
        right_operand: "",
      });

      tempWorkflows.splice(position, 1, workflow);
      setWorkflows(() => [...tempWorkflows]);
      updateSelectedRoute(tempWorkflows);
      if (isRouteSaved) {
        setGLobalProperty(globalDispatch, false, "isRouteSaved");
      }
    },
    [workflows, isRouteSaved]
  );

  // @Deprecated
  const addVariable = useCallback(
    (data) => {
      const tempVariables = variables;
      tempVariables.push({
        id: generateUUID(),
        ...data,
      });
    },
    [workflows]
  );

  // Update default workflow - the route configuration
  const updateRouteConfig = useCallback(
    (type, field, value) => {
      const tempWorkflows = workflows;
      const apiConfig = tempWorkflows[0];

      apiConfig.configurations[type][field] = value;

      tempWorkflows.splice(0, 1, apiConfig);

      setWorkflows(() => [...tempWorkflows]);
      updateSelectedRoute(tempWorkflows);
      if (isRouteSaved) {
        setGLobalProperty(globalDispatch, false, "isRouteSaved");
      }
    },
    [workflows, isRouteSaved]
  );

  const onSave = useCallback(() => {}, [workflows]);

  // Update selectedRoute - global state
  const updateSelectedRoute = useCallback(
    (workflows) => {
      const tempWorkflows = workflows;
      const apiConfig = tempWorkflows[0];
      const responseFlow = workflows?.find(
        (workflow) => workflow?.name === WorkflowTypes.SuccessResponse
      );


      const status_code =
        responseFlow && responseFlow?.status_code
          ? responseFlow?.status_code
          : selectedRoute?.status_code;
      const response =
        responseFlow && responseFlow?.response && responseFlow?.response?.length
          ? responseFlow?.response
          : selectedRoute?.response;
      const response_model =
        responseFlow && responseFlow?.response_model && responseFlow?.response_model?.length
          ? responseFlow?.response_model
          : selectedRoute?.response_model;
      const route = {
        ...selectedRoute,
        ...apiConfig.configurations[APIRouteConfigTabs.General],
        ...apiConfig.configurations[APIRouteConfigTabs.Authentication],
        ...apiConfig.configurations[APIRouteConfigTabs.Api_Input],
        status_code,
        response,
        response_model,
        columns: handleColumns(response_model),
        workflows: tempWorkflows,
      };

      setGLobalProperty(
        globalDispatch,
        route,
        "selectedRoute",
        "RouteEditor updateSelectedRoute"
      );
    },
    [selectedRoute]
  );

  // Route Config initial population - General|Auth|Validation
  useEffect(() => {
    if (selectedRoute) {
      const tempWorkflows = workflows;

      const apiConfig = tempWorkflows[0];
      if (!selectedRoute?.workflows) {
        apiConfig.configurations[APIRouteConfigTabs.General] = {
          name: selectedRoute?.name,
          description: selectedRoute?.description,
          route: selectedRoute?.route,
          method: selectedRoute?.method,
          folder: selectedRoute?.folder,
        };
        apiConfig.configurations[APIRouteConfigTabs.Authentication] = {
          protected: selectedRoute?.protected,
          authorizedRoles: selectedRoute?.authorizedRoles,
        };
        apiConfig.configurations[APIRouteConfigTabs.Api_Input] = {
          inputs: selectedRoute?.inputs,
        };

        tempWorkflows.splice(0, 1, apiConfig);
        setWorkflows(() => [...tempWorkflows]);
        setReady(true);

        return;
      }
      setWorkflows(() => [...selectedRoute?.workflows]);
      setReady(true);
    }
  }, []);

  // Variable Collection
  useEffect(() => {
    const tempWorkflows = workflows;
    const tempVariables = variables;

    let accummulatedVariables = tempWorkflows.filter(
      (workflow) => workflow?.name === WorkflowTypes.Variables
    );
    accummulatedVariables = accummulatedVariables.filter(
      (variable) => !!variable?.action_name
    );
    tempVariables.var = accummulatedVariables;

    setVariables(() => ({ ...tempVariables }));
  }, [workflows]);

  // Actions Variable Collection
  useEffect(() => {
    const tempWorkflows = workflows;
    const tempVariables = variables;

    let accummulatedVariables = tempWorkflows.filter((workflow) =>
      [WorkflowTypes.DBQuery, WorkflowTypes.CustomCode].includes(workflow?.name)
    );
    accummulatedVariables = accummulatedVariables.filter(
      (variable) => !!variable?.action_name
    );
    tempVariables.actions = accummulatedVariables;

    setVariables(() => ({ ...tempVariables }));
  }, [workflows]);

  return (
    <div className="mt-5 flex h-screen min-h-screen w-full min-w-full max-w-full px-8 pb-20">
      <div className="scrollbar-hide relative flex h-full max-h-full grow flex-col items-center justify-start overflow-y-auto py-20 ">
        <LazyLoad>
          {ready && workflows?.length
            ? workflows?.map((workflow, workflowIndex) => (
                <RouteEditorWorkflow
                  key={`${workflow.name}_${workflowIndex}`}
                  onSave={onSave}
                  workflow={workflow}
                  index={workflowIndex}
                  updateWorkflow={updateWorkflow}
                  addNewCondition={addNewCondition}
                  setWorkflowData={setWorkflowData}
                  addVariable={addVariable}
                  variables={variables}
                  onDeleteWorkflowData={deleteWorkflowData}
                  totalFlow={workflows?.length}
                  updateRouteConfig={updateRouteConfig}
                />
              ))
            : null}
        </LazyLoad>
      </div>

      <div
        className={`${
          collapse
            ? "h-5 w-5 items-center p-5 "
            : "h-fit w-[21.875rem] items-start"
        } relative flex justify-center transition-all`}
      >
        <LazyLoad count={5}>
          <RouteEditorMenu
            collapse={collapse}
            setCollapse={setCollapse}
            addVariable={addVariable}
            variables={[]}
          />
        </LazyLoad>
      </div>
      <button hidden onClick={handleSave} ref={saveRef}>
        save
      </button>
    </div>
  );
};

export default RouteEditor;
