import { rootSelector } from '@/redux/root/root.selectors';
import { ONE, SIZE_OF_EMPTY_ARRAY, ZERO } from '@/constants/common';
import { BioEntityWithPinType } from '@/types/bioEntity';
import { ElementIdTabObj } from '@/types/elements';
import { MultiSearchData } from '@/types/fetchDataState';
import { BioEntity, BioEntityContent, Comment, MapModel } from '@/types/models';
import { createSelector } from '@reduxjs/toolkit';
import {
  allCommentsSelectorOfCurrentMap,
  commentElementSelector,
} from '@/redux/comment/comment.selectors';
import { currentDrawerReactionSelector } from '@/redux/reactions/reactions.selector';
import {
  allChemicalsBioEntitesOfAllMapsSelector,
  allChemicalsBioEntitesOfCurrentMapSelector,
  allChemicalsIdTabSelectorOfCurrentMap,
  chemicalsBioEntitiesForSelectedSearchElementSelector,
  searchedChemicalsBioEntitesOfCurrentMapSelector,
} from '../chemicals/chemicals.selectors';
import { currentSelectedBioEntityIdSelector } from '../contextMenu/contextMenu.selector';
import {
  currentSearchedBioEntityId,
  currentSelectedSearchElement,
} from '../drawer/drawer.selectors';
import {
  allDrugsBioEntitesOfAllMapsSelector,
  allDrugsBioEntitesOfCurrentMapSelector,
  allDrugsIdTabSelectorOfCurrentMap,
  drugsBioEntitiesForSelectedSearchElementSelector,
  searchedDrugsBioEntitesOfCurrentMapSelector,
} from '../drugs/drugs.selectors';
import { currentModelIdSelector, modelsDataSelector } from '../models/models.selectors';

export const bioEntitySelector = createSelector(rootSelector, state => state.bioEntity);

export const bioEntityDataSelector = createSelector(bioEntitySelector, bioEntity => bioEntity.data);

export const bioEntityLoadingSelector = createSelector(
  bioEntitySelector,
  bioEntity => bioEntity.loading,
);

export const bioEntityDataListSelector = createSelector(bioEntityDataSelector, bioEntityData =>
  bioEntityData.map(b => b.data || []).flat(),
);

export const allSubmapConnectionsBioEntitySelector = createSelector(
  bioEntitySelector,
  (bioEntityData): BioEntity[] =>
    (bioEntityData?.submapConnections?.data || []).map(({ bioEntity }) => bioEntity),
);

export const allSubmapConnectionsBioEntityOfCurrentSubmapSelector = createSelector(
  allSubmapConnectionsBioEntitySelector,
  currentModelIdSelector,
  (submapConnectionsBioEntity, currentModel): BioEntity[] =>
    submapConnectionsBioEntity.filter(({ model }) => model === currentModel),
);

export const bioEntitiesForSelectedSearchElement = createSelector(
  bioEntitySelector,
  currentSelectedSearchElement,
  (bioEntitiesState, currentSearchElement): MultiSearchData<BioEntityContent[]> | undefined =>
    bioEntitiesState.data.find(
      ({ searchQueryElement }) => searchQueryElement === currentSearchElement,
    ),
);

export const searchedFromMapBioEntityElement = createSelector(
  bioEntitiesForSelectedSearchElement,
  currentSearchedBioEntityId,
  (bioEntitiesState, currentBioEntityId): BioEntity | undefined =>
    bioEntitiesState &&
    bioEntitiesState.data?.find(({ bioEntity }) => bioEntity.id === currentBioEntityId)?.bioEntity,
);

export const searchedBioEntityElementForContextMapSelector = createSelector(
  bioEntitySelector,
  currentSelectedBioEntityIdSelector,
  (bioEntitiesState, currentBioEntityId): BioEntity | undefined => {
    return bioEntitiesState.data
      .find(({ searchQueryElement }) => searchQueryElement === currentBioEntityId.toString())
      ?.data?.find(({ bioEntity }) => bioEntity.id === currentBioEntityId)?.bioEntity;
  },
);

export const searchedBioEntityElementUniProtIdSelector = createSelector(
  searchedBioEntityElementForContextMapSelector,
  (bioEntitiesState): string | undefined => {
    return bioEntitiesState?.references.find(({ type }) => type === 'UNIPROT')?.resource;
  },
);

export const searchedFromMapBioEntityElementRelatedSubmapSelector = createSelector(
  searchedFromMapBioEntityElement,
  modelsDataSelector,
  (bioEntity, models): MapModel | undefined =>
    models.find(({ idObject }) => idObject === bioEntity?.submodel?.mapId),
);

export const loadingBioEntityStatusSelector = createSelector(
  bioEntitiesForSelectedSearchElement,
  state => state?.loading,
);

export const searchedBioEntitesSelectorOfCurrentMap = createSelector(
  bioEntitySelector,
  currentSelectedSearchElement,
  currentModelIdSelector,
  (bioEntities, currentSearchElement, currentModelId): BioEntity[] => {
    if (!bioEntities) {
      return [];
    }

    return (bioEntities?.data || [])
      .filter(({ searchQueryElement }) =>
        currentSearchElement ? searchQueryElement === currentSearchElement : true,
      )
      .map(({ data }) => data || [])
      .flat()
      .filter(({ bioEntity }) => bioEntity.model === currentModelId)
      .map(({ bioEntity }) => bioEntity);
  },
);

export const allBioEntitesSelectorOfCurrentMap = createSelector(
  bioEntitySelector,
  currentModelIdSelector,
  (bioEntities, currentModelId): BioEntity[] => {
    if (!bioEntities) {
      return [];
    }

    return (bioEntities?.data || [])
      .map(({ data }) => data || [])
      .flat()
      .filter(({ bioEntity }) => bioEntity.model === currentModelId)
      .map(({ bioEntity }) => bioEntity);
  },
);

export const allBioEntitesIdTabSelectorOfCurrentMap = createSelector(
  bioEntitySelector,
  currentModelIdSelector,
  (bioEntities, currentModelId): ElementIdTabObj => {
    if (!bioEntities) {
      return {};
    }

    return Object.fromEntries(
      (bioEntities?.data || [])
        .map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement])
        .map(([data, tab]) =>
          (data || [])
            .flat()
            .filter(({ bioEntity }) => bioEntity.model === currentModelId)
            .map(d => [d.bioEntity.id, tab]),
        )
        .flat(),
    );
  },
);

export const numberOfBioEntitiesSelector = createSelector(
  bioEntitiesForSelectedSearchElement,
  state => (state?.data ? state.data.length : SIZE_OF_EMPTY_ARRAY),
);

export const bioEntitiesPerModelSelector = createSelector(
  bioEntitiesForSelectedSearchElement,
  modelsDataSelector,
  (bioEntities, models) => {
    const bioEntitiesPerModelPerSearchElement = (models || []).map(model => {
      const bioEntitiesInGivenModel = (bioEntities?.data || []).filter(
        entity => model.idObject === entity.bioEntity.model,
      );

      return {
        modelName: model.name,
        modelId: model.idObject,
        numberOfEntities: bioEntitiesInGivenModel.length,
        bioEntities: bioEntitiesInGivenModel,
      };
    });

    return bioEntitiesPerModelPerSearchElement.filter(
      model => model.numberOfEntities !== SIZE_OF_EMPTY_ARRAY,
    );
  },
);

export const allVisibleBioEntitiesSelector = createSelector(
  searchedBioEntitesSelectorOfCurrentMap,
  searchedChemicalsBioEntitesOfCurrentMapSelector,
  searchedDrugsBioEntitesOfCurrentMapSelector,
  (content, chemicals, drugs): BioEntity[] => {
    return [content, chemicals, drugs].flat();
  },
);

export const allElementsForSearchElementSelector = createSelector(
  bioEntitiesForSelectedSearchElement,
  chemicalsBioEntitiesForSelectedSearchElementSelector,
  drugsBioEntitiesForSelectedSearchElementSelector,
  (content, chemicals, drugs): BioEntity[] => {
    const contentBioEntities = (content?.data || []).map(({ bioEntity }) => bioEntity);

    return [contentBioEntities, chemicals || [], drugs || []].flat();
  },
);

export const allElementsForSearchElementNumberByModelId = createSelector(
  allElementsForSearchElementSelector,
  (elements): Record<number, number> => {
    return elements.reduce(
      (acc, { model }) => ({
        ...acc,
        [model]: (acc?.[model] || ZERO) + ONE,
      }),
      {} as Record<number, number>,
    );
  },
);

export const allContentBioEntitesSelectorOfAllMaps = createSelector(
  bioEntitySelector,
  (bioEntities): BioEntity[] => {
    if (!bioEntities) {
      return [];
    }

    return (bioEntities?.data || [])
      .map(({ data }) => data || [])
      .flat()
      .map(({ bioEntity }) => bioEntity);
  },
);

export const allBioEntitiesSelector = createSelector(
  allContentBioEntitesSelectorOfAllMaps,
  allChemicalsBioEntitesOfAllMapsSelector,
  allDrugsBioEntitesOfAllMapsSelector,
  allSubmapConnectionsBioEntitySelector,
  (content, chemicals, drugs, submapConnections): BioEntity[] => {
    return [content, chemicals, drugs, submapConnections].flat();
  },
);

export const allBioEntitiesElementsIdsSelector = createSelector(
  allBioEntitesIdTabSelectorOfCurrentMap,
  allChemicalsIdTabSelectorOfCurrentMap,
  allDrugsIdTabSelectorOfCurrentMap,
  (content, chemicals, drugs): ElementIdTabObj => {
    return {
      ...content,
      ...chemicals,
      ...drugs,
    };
  },
);

export const currentDrawerBioEntitySelector = createSelector(
  allBioEntitiesSelector,
  commentElementSelector,
  currentSearchedBioEntityId,
  (bioEntities, commentElement, currentBioEntityId): BioEntity | undefined => {
    if (commentElement && commentElement.id === currentBioEntityId) {
      return commentElement;
    }
    return bioEntities.find(({ id }) => id === currentBioEntityId);
  },
);

export const currentDrawerBioEntityRelatedSubmapSelector = createSelector(
  currentDrawerBioEntitySelector,
  modelsDataSelector,
  (bioEntity, models): MapModel | undefined =>
    models.find(({ idObject }) => idObject === bioEntity?.submodel?.mapId),
);

export const allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector =
  createSelector(
    allSubmapConnectionsBioEntityOfCurrentSubmapSelector,
    allElementsForSearchElementNumberByModelId,
    (submapConnectionsBioEntity, modelElementsNumber): BioEntity[] => {
      return submapConnectionsBioEntity.filter(
        ({ submodel }) => submodel && modelElementsNumber?.[submodel.mapId] > ZERO,
      );
    },
  );

export const allBioEntitiesWithTypeOfCurrentMapSelector = createSelector(
  allBioEntitesSelectorOfCurrentMap,
  allChemicalsBioEntitesOfCurrentMapSelector,
  allDrugsBioEntitesOfCurrentMapSelector,
  allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector,
  (content, chemicals, drugs, submapConnections): BioEntityWithPinType[] => {
    return [
      content.map(v => ({ ...v, type: 'bioEntity' as const })),
      chemicals.map(v => ({ ...v, type: 'chemicals' as const })),
      drugs.map(v => ({ ...v, type: 'drugs' as const })),
      submapConnections.map(v => ({ ...v, type: 'bioEntity' as const })),
    ].flat();
  },
);

export const allVisibleBioEntitiesIdsSelector = createSelector(
  allVisibleBioEntitiesSelector,
  allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector,
  (elements, submapConnections): (string | number)[] => {
    return [...elements, ...submapConnections].map(e => e.id);
  },
);

export const currentDrawerElementCommentsSelector = createSelector(
  currentDrawerBioEntitySelector,
  allCommentsSelectorOfCurrentMap,
  (element, comments): Comment[] => {
    if (element) {
      return comments.filter(
        comment =>
          comment.type === 'ALIAS' &&
          comment.modelId === element.model &&
          Number(comment.elementId) === element.id,
      );
    }
    return [];
  },
);

export const currentDrawerReactionCommentsSelector = createSelector(
  currentDrawerReactionSelector,
  allCommentsSelectorOfCurrentMap,
  (reaction, comments): Comment[] => {
    if (reaction) {
      return comments.filter(
        comment =>
          comment.type === 'REACTION' &&
          comment.modelId === reaction.modelId &&
          Number(comment.elementId) === reaction.id,
      );
    }
    return [];
  },
);