import { isConstructorDeclaration } from "typescript";
import {
  IAddtionalDot,
  IAddtionalEdge,
  IAddtionalTriage,
  IAddtionalElements,
  IAddtionalCloud,
  IAddtionalBase,
} from "../../definitions/additional";
import {
  IElementType,
  IExtendElementType,
  IReferenceElement,
} from "../../definitions/element";
import { IJointType, getJointDefinition } from "../../definitions/joint";
import { setNewPointName } from "../../helpers/pdfHelpers";
import { ListSectionProps } from "../../types/sectionTypes";
import { ModalProps, SiteProps } from "./Section";
import {
  emptyDetailsProps,
  emptyElement,
  emptyMapElement,
  emptyModalProps,
} from "./sectionConst";
import { IListConfing } from "../../definitionsNew/configs";
import { fields } from "../../definitionsNew/fields";
import { getValueForSource } from "../../definitionsNew/setFieldsHelper";
import { IProject } from "../../store/interface/projects";
import { IProjectDetailsObj } from "../../store/interface/projectDetails";
import { ISpoolCanvas, ISpoolElement } from "../../definitions/spools";
import { UserContract, UserContractSimple } from "../../store/interface/user";

export const isJoint = (element: IElementType | IAddtionalElements) => {
  return element.type === "joint";
};

export const isReference = (element: IElementType) => {
  return !!element.sourceElementID;
};

export const getElement = (
  id: string,
  elements: IElementType[],
  references?: IReferenceElement[],
  pageNumber?: number,
): IElementType => {
  const selectedElement = elements.find((item) => item.id === id);
  if (selectedElement) {
    return selectedElement;
  } else if (!selectedElement && references && pageNumber !== undefined) {
    const ref = references.find(
      (item) => item.id === id && item.page === pageNumber,
    );
    if (ref) {
      const source = elements.find((item) => item.id === ref.sourceElementID);
      if (source) {
        return {
          ...source,
          x: ref.x,
          y: ref.y,
          label_x: ref.label_x,
          label_y: ref.label_y,
          lock: ref.lock,
        };
      }
    }
  }

  return emptyElement;
};

export const getMapElement = (
  id: string,
  elements: IAddtionalElements[],
): IAddtionalElements => {
  const selectedElement =
    elements.find((item) => item.id === id) || emptyMapElement;
  return selectedElement;
};
export const getReferenceElement = (
  id: string,
  references: IReferenceElement[],
  elements: IElementType[],
): IElementType => {
  const ref = references.find((item) => item.id === id);
  if (ref) {
    const source = elements.find((item) => item.id === ref.sourceElementID);
    if (source) {
      return {
        ...source,
        ...ref,
      };
    }
  }
  return emptyElement;
};

export const serializeNewElement = (
  element: IElementType,
  info: ModalProps,
  sectionID: string,
  pageNumber: number,
  mode: string,
): IElementType => {
  const isDuplicate = mode === "duplicate";
  element.joint_name = encodeURIComponent(element.joint_name.trim());
  return {
    ...element,
    x: isDuplicate ? element.x : info.x!,
    y: isDuplicate ? element.y : info.y!,
    type: isDuplicate ? element.type : info.type,
    sectionID: sectionID,
    page: pageNumber,
    label_x: 30,
    label_y: 30,
    lock: false,
    createdDate: Date.now(),
  };
};
export const serializeNewSpool = (
  info: ModalProps,
  sectionID: string,
  pageNumber: number,
) => {
  return {
    x: info.x!,
    y: info.y!,
    sectionID: sectionID,
    page: pageNumber,
    label_x: 50,
    label_y: 50,
    lock: false,
    createdDate: Date.now(),
  };
};

interface ISetOptionAddElement {
  element: IElementType;
  orgID: string;
  projectID: string;
}
export const setOptionAddElement = (
  element: IElementType,
  info: ModalProps,
  projectID: string,
  sectionID: string,
  pageNumber: number,
  orgID: string,
  mode: string,
): ISetOptionAddElement => {
  return {
    element: serializeNewElement(element, info, sectionID, pageNumber, mode),
    orgID: orgID,
    projectID: projectID,
  };
};

export const setOptionAddSpool = (
  element: string,
  info: ModalProps,
  projectID: string,
  sectionID: string,
  pageNumber: number,
  orgID: string,
) => {
  return {
    spool: serializeNewSpool(info, sectionID, pageNumber),
    orgID: orgID,
    projectID: projectID,
    spoolName: element.trim(),
    sectionID: sectionID,
  };
};

export const setOptionAddSpoolInstance = (
  info: ModalProps,
  projectID: string,
  sectionID: string,
  spoolID: string,
  pageNumber: number,
  orgID: string,
) => {
  return {
    spool: serializeNewSpool(info, sectionID, pageNumber),
    orgID: orgID,
    projectID: projectID,
    sectionID: sectionID,
    spoolID: spoolID,
  };
};

export const serializeNewReferenceElement = (
  sourceId: string,
  info: ModalProps,
  sectionID: string,
  pageNumber: number,
): IReferenceElement => {
  return {
    id: "",
    sourceElementID: sourceId,
    sectionID: sectionID,
    page: pageNumber,
    x: info.x!,
    y: info.y!,
    label_x: 30,
    label_y: 30,
    lock: false,
    createdDate: Date.now(),
  };
};

interface ISetOptionAddRefernceElement {
  referenceElement: IReferenceElement;
  orgID: string;
  projectID: string;
}
export const setOptionAddReference = (
  sourceId: string,
  info: ModalProps,
  projectID: string,
  sectionID: string,
  pageNumber: number,
  orgID: string,
): ISetOptionAddRefernceElement => {
  return {
    referenceElement: serializeNewReferenceElement(
      sourceId,
      info,
      sectionID,
      pageNumber,
    ),
    orgID: orgID,
    projectID: projectID,
  };
};

export const serializeSaveElement = (element: IJointType) => {
  return {
    ...element,
    joint_name: encodeURIComponent(element.joint_name.trim()),
  };
};
export const serializeSaveSpool = (spool: ISpoolCanvas) => {
  const excludeField = ["spoolID", "spoolName"];

  const filteredSpool = Object.entries(spool).reduce(
    (acc, [key, value]) => {
      if (!excludeField.includes(key)) {
        acc[key as keyof typeof spool] = value;
      }
      return acc;
    },
    {} as Partial<typeof spool>,
  );

  return filteredSpool;
};

export const serializeSaveSourceElement = (
  element: IJointType,
  source: IJointType,
) => {
  return {
    ...element,
    createdDate: source.CreatedDate,
    joint_name: encodeURIComponent(element.joint_name.trim()),
    id: source.id,
    sectionID: source.sectionID,
    page: source.page,
    x: source.x,
    y: source.y,
    label_x: source.label_x,
    label_y: source.label_y,
  };
};

export const setOptionsSaveElement = (
  element: IElementType,
  orgID: string,
  projectID: string,
) => {
  return {
    element: serializeSaveElement(element as IJointType),
    orgID,
    projectID,
  };
};

export const setOptionsSaveSourceElement = (
  element: IElementType,
  source: IElementType,
  orgID: string,
  projectID: string,
) => {
  return {
    element: serializeSaveSourceElement(
      element as IJointType,
      source as IJointType,
    ),
    orgID,
    projectID,
  };
};

export const serializeSaveReference = (element: IJointType) => {
  return {
    id: element.id,
    sectionID: element.sectionID,
    page: element.page,
    x: element.x,
    y: element.y,
    label_x: element.label_x,
    label_y: element.label_y,
    lock: element.lock,
    sourceElementID: element.sourceElementID,
  };
};
export const setOptionsSaveReferencElement = (
  element: IReferenceElement,
  userID: string,
  projectID: string,
) => {
  return {
    element: serializeSaveReference(element as IJointType),
    userID,
    projectID,
  };
};

export const updateStateShowEdit = (
  currentState: SiteProps,
  elements: IElementType[],
  references: IReferenceElement[],
  id: string,
  isReference: boolean,
): SiteProps => {
  const element = isReference
    ? getReferenceElement(id, references, elements)
    : getElement(id, elements);
  return {
    ...currentState,
    modal: {
      show: true,
      x: element.x,
      y: element.y,
      type: element.type,
      mode: "edit",
      data: element,
    },
    details: {
      ...currentState.details,
    },
  };
};

export const updateStateRemoveElement = (
  currentState: SiteProps,
  id: string,
): SiteProps => {
  return {
    ...currentState,
    buttonLoader: false,
    referal: currentState.referal === id ? "" : currentState.referal,
    modal: {
      ...emptyModalProps,
    },
    details: {
      ...emptyDetailsProps,
    },
    delete: {
      show: false,
      id: "",
    },
  };
};

export const updateElements = (
  allElements: IElementType[],
  element: IElementType,
) => {
  const id = element.sourceElementID ? element.sourceElementID : element.id;
  const index = allElements.findIndex((item) => item.id === id);
  if (index !== -1) {
    const sourceElement: IJointType = element as IJointType;
    const newElement = allElements[index] as IJointType;
    newElement.joint_name = encodeURIComponent(element.joint_name.trim());
    newElement.type = sourceElement.type;
    newElement.joint_type = sourceElement.joint_type;
    newElement.element_type = sourceElement.element_type;
    newElement.diameter_1 = sourceElement.diameter_1;
    newElement.diameter_2 = sourceElement.diameter_2;
    newElement.thickness_1 = sourceElement.thickness_1;
    newElement.thickness_2 = sourceElement.thickness_2;
    newElement.material_1 = sourceElement.material_1;
    newElement.material_2 = sourceElement.material_2;
  }
  return allElements;
};

export const updateRemoveReference = (
  allElements: IReferenceElement[],
  id: string,
) => {
  return allElements.filter((element) => element.id !== id);
};
export const updateRemoveReferenceSource = (
  allElements: IReferenceElement[],
  id: string,
) => {
  return allElements.filter((element) => element.sourceElementID !== id);
};

export const getItemName = (allElements: IElementType[], id: string) => {
  return getElement(id, allElements).joint_name;
};
export const getSpoolName = (allSpools: ISpoolElement[], id: string) => {
  return (
    allSpools.find((_spool) =>
      _spool.instances.find((_instance) => _instance.instanceID === id),
    )?.spoolName || ""
  );
};

export const toggleElementLock = (
  allElements: IElementType[],
  id: string,
  lock: boolean,
): IElementType[] => {
  const updated = [...allElements];
  updated.forEach((element) => {
    if (element.id === id) {
      element.lock = lock;
    }
  });
  return updated;
};

// export const checkIdForReference = (id: string,elements: IElementType[], references: IReferenceElement[]) => {

// };

export const generateDuplicateElement = (
  sourcePointId: string,
  elements: IElementType[],
  references: IReferenceElement[],
  pageNumber: number,
  x?: number,
  y?: number,
): IElementType => {

  const selectedElement = getElement(
    sourcePointId,
    elements,
    references,
    pageNumber,
  );

  return {
    ...selectedElement,
    x: x ? x : selectedElement.x + 60,
    y: y ? y : selectedElement.y - 20,
    lock: false,
    createdDate: Date.now(),
    joint_name: setNewPointName(elements, selectedElement.sectionID || ""),
    id: "",
  };
};

export const clearSelection = (currentState: SiteProps): SiteProps => {
  return {
    ...currentState,
    referal: "",
    details: {
      ...emptyDetailsProps,
    },
  };
};
//ToDo refacto ? join with others
export const setDotElement = (
  x: number,
  y: number,
  projectID: string,
  sectionID: string,
  page: number,
): IAddtionalElements => {
  return {
    id: "",
    type: "dot",
    sectionID,
    page,
    x,
    y,
    lock: false,
    size: 20,
    rotation: 0,
    height: 0,
    end_x: 0,
    end_y: 0,
  };
};

export const setTriageElement = (
  x: number,
  y: number,
  projectID: string,
  sectionID: string,
  page: number,
): IAddtionalElements => {
  return {
    id: "",
    type: "triage",
    sectionID,
    page,
    x,
    y,
    lock: false,
    size: 20,
    rotation: 0,
    height: 0,
    end_x: 0,
    end_y: 0,
  };
};

export const setEdgeElement = (
  x: number,
  y: number,
  end_x: number,
  end_y: number,
  projectID: string,
  sectionID: string,
  page: number,
): IAddtionalElements => {
  return {
    id: "",
    type: "edge",
    sectionID,
    page,
    x,
    y,
    end_x,
    end_y,
    lock: false,
    height: 4,
    size: 0,
    rotation: 0,
  };
};

export const setCloudElement = (
  x: number,
  y: number,
  size: number,
  height: number,
  projectID: string,
  sectionID: string,
  page: number,
): IAddtionalElements => {
  return {
    id: "",
    type: "cloud",
    sectionID,
    page,
    x,
    y,
    lock: false,
    height,
    size,
    rotation: 0,
    end_x: 0,
    end_y: 0,
  };
};

export const updateMapElements = (
  allElements: IAddtionalElements[],
  element: IAddtionalElements,
) => {
  const index = allElements.findIndex((item) => item.id === element.id);
  allElements[index] = element;
  return allElements;
};

export const getDeatilsData = (
  detailsType: string,
  config: IListConfing[],
  detailsData?: IElementType | IAddtionalElements,
): IElementType | IAddtionalElements | undefined => {
  if (detailsType !== "") {
    if (detailsType !== "joint") {
      return detailsData;
    } else {
      return getJointDefinition(config, detailsData as IJointType);
    }
  }
};
//
export const setOptionMapElement = (
  element: IAddtionalElements,
  orgID: string,
  projectID: string,
) => {
  let mapElement = {};
  switch (element.type) {
    case "dot":
      mapElement = serializeDotElement(element);
      break;
    case "triage":
      mapElement = serializeTriageElement(element);
      break;
    case "edge":
      mapElement = serializeEdgeElement(element);
      break;
    case "cloud":
      mapElement = serializeCloudElement(element);
      break;
  }

  return { element: mapElement, orgID, projectID };
};

const serializeElement = <T extends IAddtionalBase>(
  element: T,
  type: string,
): T & { type: string; lock: boolean } => {
  return {
    ...element,
    type,
    lock: false,
  };
};

export const serializeDotElement = (
  element: IAddtionalElements,
): IAddtionalDot & { type: string } => serializeElement(element, "dot");

export const serializeTriageElement = (
  element: IAddtionalElements,
): IAddtionalTriage & { type: string } => serializeElement(element, "triage");

export const serializeEdgeElement = (
  element: IAddtionalElements,
): IAddtionalEdge & { type: string } => serializeElement(element, "edge");

export const serializeCloudElement = (
  element: IAddtionalElements,
): IAddtionalCloud & { type: string } => serializeElement(element, "cloud");

export const generatePoint = (
  element: IJointType,
  section: ListSectionProps,
  project: IProject,
  contract: UserContractSimple,
) => {
  return {
    ...element,
    ...section,
    ...project,
    ...contract,
    id: element.id,
  };
};
export const setShowListPoints = (
  elements: IElementType[],
  sections: ListSectionProps[],
  projectID: string,
  config: IListConfing[],
  projects: IProject[],
  projectDetails: IProjectDetailsObj,
  contracts: UserContractSimple[],
) => {
  const points: IExtendElementType[] = [];
  if (sections.length === 0 || elements.length === 0) {
    return points;
  }
  elements.forEach((element) => {
    let newPoint: { [key: string]: any } = { ...element };

    const sectionDetails = sections.find(
      (section) => section.sectionID === element.sectionID,
    );
    const projecDetails = projects.find(
      (_project) => _project.projectID === projectID,
    );
    if (newPoint.spoolID !== "") {
      newPoint.spoolName =
        projectDetails?.spools.find(
          (_spool) => _spool.spoolID === element.spoolID,
        )?.spoolName || "";
    }
    const contractDetails = contracts.find(
      (_contract) => _contract.contractID === projecDetails?.contractID,
    );
    if (sectionDetails && projecDetails && contractDetails) {
      newPoint = generatePoint(
        newPoint as IJointType,
        sectionDetails,
        projecDetails,
        contractDetails,
      );
    }
    config.forEach((_item) => {
      if (!(_item.id in element)) {
        const fieldDef = fields.find((_field) => _field.id === _item.id);
        if (fieldDef && fieldDef.type === "source") {
          const value = getValueForSource(
            fieldDef.name,
            newPoint[fieldDef.sourceFiled as keyof typeof element] as string,
            fieldDef.sourceScope as string,
            projectDetails,
            projects,
            contracts,
          );
          newPoint[fieldDef.id] = value;
        }
      }
    });

    points.push(newPoint as IExtendElementType);
  });

  return points;
};

export const setOptionsSaveSpool = (
  spool: ISpoolCanvas,
  orgID: string,
  projectID: string,
) => {
  return {
    spool: serializeSaveSpool(spool),
    orgID,
    projectID,
    spoolID: spool.spoolID,
    instanceID: spool.instanceID,
  };
};
