import i18next from 'i18next';

import 'src/pages/dashboard/orgChart/i18n';

import { logEvent } from 'src/config/logging';
import { MILISECONDS_IN_MONTH } from 'src/constants/orgChart';
import { EventName } from 'src/types/amplitude';
import { type MeInstance } from 'src/types/instance';
import { type User as OrgChartUser, type UserNode } from 'src/types/orgChart';
import { type MeUser } from 'src/types/user';

const backgroundColorsMap = new Map<number, string>([
  [0, '#f8d76740'],
  [3, '#f8d76740'],
  [5, '#b6d48a40'],
  [10, '#74cdde40'],
  [20, '#78b3f040'],
  [30, '#7b86c640'],
  [40, '#df6b9240'],
  [400, '#f0906d40'],
]);

const borderColorsMap = new Map<number, string>([
  [0, '#F6C344'],
  [3, '#F6C344'],
  [5, '#97C15C'],
  [10, '#54B9D1'],
  [20, '#4994EC'],
  [30, '#4350AF'],
  [40, '#D63864'],
  [400, '#EC6337'],
]);

export const logOrgChartView = (comingFrom: string) =>
  logEvent(EventName.ORG_CHART_VIEW, { comingFrom });

export const getHireDateColor = (
  time: number,
  hasAnYear: boolean,
  background: true,
): string => {
  const colorArray = background ? backgroundColorsMap : borderColorsMap;

  if (!hasAnYear) return colorArray.get(0) || '';

  for (const [years, color] of colorArray) {
    if (time <= years) return color;
  }
  return '';
};

export const getYearsInCompany = (hiringDate: string) => {
  if (!hiringDate) return null;

  const now = new Date();
  const hiringDateTime = new Date(hiringDate).getTime();
  const durationInMilliseconds = now.getTime() - hiringDateTime;
  const durationInMonths = Math.floor(
    durationInMilliseconds / MILISECONDS_IN_MONTH,
  );
  const durationInYears = Math.floor(durationInMonths / 12);
  if (durationInYears >= 1) {
    return { duration: durationInYears, hasYears: true };
  } else {
    return { duration: durationInMonths, hasYears: false };
  }
};

export const addAuxClass = () => {
  const elements = document.querySelectorAll('.svg-chart-container');
  elements.forEach((element: Element) => {
    element.classList.remove('svg-chart-container');
    element.classList.add('aux-class');
  });
};

export const getRootNode = (chart: any) => {
  const state = chart.getChartState();
  const allNodes = state?.allNodes;
  return allNodes && allNodes.length > 0 ? allNodes[0] : null;
};

export const getCurrentNode = (allNodes: any[], currentId: number) =>
  allNodes.find(node => node.data.id === currentId);

export const highlightCurrentNode = (nodeId: number) => {
  const selectedNode = document.getElementById(`node-${nodeId}`);
  if (selectedNode) {
    selectedNode.style.border = '4px solid #496BE3'; // TODO: change to theme color when OrgChart will migrated to HuGo
  }
};

export const showProfilePictures = (show: boolean, chart?: any) => {
  if (!chart) return;

  const state = chart.getChartState();
  const allNodes = state.allNodes;

  allNodes.forEach((node: any) => {
    if (!show) {
      // We save the current profilePicture in a data attribute
      if (node.data.profilePicture) {
        node.data._originalProfilePicture = node.data.profilePicture;
        node.data.profilePicture = null;
      }
    } else {
      // We restore the original profilePicture if it exists
      if (node.data._originalProfilePicture) {
        node.data.profilePicture = node.data._originalProfilePicture;
        delete node.data._originalProfilePicture;
      }
    }
  });

  chart.data(state.data).render();
};

export const getHireDateString = (time: number, hasAnYear: boolean) =>
  i18next.t(
    hasAnYear ? 'ORGCHART:HIRE_DATE_YEAR' : 'ORGCHART:HIRE_DATE_MONTH',
    { count: time },
  );

const collapseNode = (state: any, parentId: number) => {
  state.data.forEach((nodeData: any) => {
    if (nodeData.parentId === parentId) {
      nodeData._expanded = false;
      collapseNode(state, nodeData.id);
    }
  });
};

export const handleNodeExpandCollapse = (
  state: any,
  node: any,
  rootNode: any,
  chart: any,
) => {
  if (node.data._totalSubordinates > 0 && node.data.id !== rootNode.data.id) {
    const isChildrenExpanded = state.data.some(
      (nodeData: any) =>
        nodeData.parentId === node.data.id && nodeData._expanded,
    );
    state.data.forEach((nodeData: any) => {
      if (nodeData.parentId === node.data.id) {
        if (isChildrenExpanded) {
          collapseNode(state, node.data.id);
        } else {
          nodeData._expanded = true;
        }
      }
    });
  } else if (node.data.id === rootNode.data.id) {
    const childRootNode = state.allNodes.find(
      (nodeElem: any) => nodeElem.data.parentId === rootNode.data.id,
    );
    if (!childRootNode) return;
    if (childRootNode.data._expanded) {
      chart.collapseAll();
    } else {
      chart.setExpanded(childRootNode.data.id, true);
    }
  }
};

export const handleUpdateChart = (chart: any, rootNode: any, node: any) => {
  chart.setUpToTheRootHighlighted(node.data.id);
  chart.layout(rootNode.data.orientation);
  chart.setCentered(node.data.id);
  chart.render();
};

export const getNodePosition = (orientation: string) => {
  let positionX = 0;
  let positionY = 0;

  if (orientation === 'left' || orientation === 'right') {
    positionX = 80;
    positionY = -60;
  } else {
    positionX = 260;
    positionY = 70;
  }

  return { positionX, positionY };
};

/**
 * Formats extra fields for a node of the org chart.
 * The extra fields are added to the root node.
 *
 * @returns an object with the extra fields
 */
export const formatExtraFields = (
  currentUser: MeUser,
  instance: MeInstance,
  showEmail = false,
) => {
  return {
    chartColor: false,
    orientation: 'top',
    orientationIndex: 0,
    currentNodeId: currentUser.id,
    showEmail,
    showDepartment: true,
    showPosition: true,
    showHireDate: instance?.allowHiringDate,
  };
};

/**
 * Formats the user node for the org chart.
 *
 * @returns a UserNode
 */
export const formatUserNode = (
  user: MeUser,
  instance: MeInstance,
  showEmail = false,
) => {
  const extraFields = formatExtraFields(user, instance, showEmail);

  return {
    id: user.id,
    parentId: '',
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    hiringDate: user.hiringDate,
    subordinatesCount: 0,
    profilePicture: user.profilePicture,
    departmentName: user.department?.name ?? '',
    //TODO: Position on /me is an object, unify this with /users/:id where it is a string
    positionName: user?.jobPosition?.name,
    ...extraFields,
  };
};

/**
 * Formats the boss node for the org chart.
 *
 * @returns a UserNode
 */
export const formatBossNode = (
  currentUser: MeUser,
  boss: OrgChartUser,
  bosses: OrgChartUser[],
  instance: MeInstance,
  showEmail = false,
) => {
  const { bossId, ...rest } = boss;

  const hasBoss = !!boss.bossId && bosses.find(({ id }) => id === bossId);
  const parentId = hasBoss ? (bossId ?? '') : '';

  const node = { parentId, ...rest };

  if (parentId === '') {
    const extraFields = formatExtraFields(currentUser, instance, showEmail);

    return { ...node, ...extraFields };
  }

  return node;
};

/**
 * Formats the subordinate node for the org chart.
 *
 * @returns a UserNode
 */
export const formatSubordinateNode = (
  subordinate: OrgChartUser,
  bossId: number,
) => {
  return {
    parentId: bossId,
    ...subordinate,
  };
};

/**
 * Formats the nodes for the org chart, including the user, bosses and subordinates.
 *
 * @returns an array of UserNode
 */
export const formatNodes = (
  user: MeUser | undefined,
  userBoss: OrgChartUser | undefined,
  bosses: OrgChartUser[],
  subordinates: OrgChartUser[],
  instance: MeInstance,
  bossId: number,
  showEmail = false,
) => {
  if (!user) return [];

  const userData: UserNode[] = [formatUserNode(user, instance, showEmail)];

  const bossesData: UserNode[] =
    bosses?.map(boss =>
      formatBossNode(user, boss, bosses, instance, showEmail),
    ) || [];

  const subordinatesData: UserNode[] =
    subordinates?.map(subordinate =>
      formatSubordinateNode(subordinate, bossId),
    ) || [];

  if (userBoss?.id) {
    return bossesData.concat(subordinatesData);
  }

  if (subordinatesData) {
    userData[0].subordinatesCount = subordinatesData?.length;
    return userData.concat(subordinatesData);
  }

  return userData;
};
