import { ofType } from 'redux-observable';
import { concat, of } from 'rxjs';
import { debounceTime, filter, switchMap } from 'rxjs/operators';

import { configSelector } from '../../../filterContainer/redux/selectors';
import { keywordTypeSelector } from '../selectors';
import {
  feedDescriptionListAction,
  feedDescriptionListLoadingAction,
  feedDescriptionSearchPrefixAction,
  keywordListAction,
  keywordListLoadingAction,
  keywordSearchPrefixAction,
  labelListAction,
  labelListLoadingAction,
  labelSearchPrefixAction,
} from '../slices';
import { gqlRequest } from '@/modules/@common/request';
import { getToken } from '@/modules/@common/token/helpers';
import { getUserAccountIdSelector, getUserContentOwnersSelector } from '@/modules/@common/user/redux/selectors';

const queryGQLKeywords = `
  query autocompleteKeyword(
    $contentOwner: [Float],
    $category: String!,
    $prefix: String,
    $excludeOrigin: String,
    $checkBrandSafetyPermission: Boolean
  ) {
    autocompleteKeyword(
      contentOwner: $contentOwner,
      category: $category,
      prefix: $prefix,
      excludeOrigin: $excludeOrigin,
      checkBrandSafetyPermission: $checkBrandSafetyPermission
    ) {
      value
    }
  }
`;

const queryGQLLabels = `
  query autocompleteLabelGrouped(
    $accountId: Int
    $prefix: String!,
    $excludeOrigin: String
  ) {
    autocompleteLabelGrouped(
      accountId: $accountId,
      prefix: $prefix,
      excludeOrigin: $excludeOrigin
    ) {
      labelGrouped {
        name
        values {
          value
        }
        color
        contentOwnerId
        accountId
        labelId
      }
    }
  }
`;

const queryGQLFeedDescriptionGrouped = `
  query autocompleteFeedDescriptionGrouped(
    $contentOwners: [Float]!,
    $contentOwnersOwnContent: [Float],
    $prefix: String!,
    $excludeOrigin: String
  ) {
    autocompleteFeedDescriptionGrouped(
      contentOwners: $contentOwners,
      contentOwnersOwnContent: $contentOwnersOwnContent,
      prefix: $prefix, 
      excludeOrigin: $excludeOrigin
    ) {
      grouped {
        ids
        values {
          value
        }
      }
    }
  }
`;

const getKeywordsResponse = ({ autocompleteKeyword }) =>
  autocompleteKeyword.map(({ value }) => ({ value, label: value }));

const getLabelsResponse = ({ autocompleteLabelGrouped: { labelGrouped } }) =>
  labelGrouped.reduce((acc, curr) => {
    const labels = curr.values
      ? curr.values.map((label) => ({
          label: label.value,
          value: label.value,
          name: curr.name,
          color: curr.color,
          contentOwner: curr.contentOwner,
          groupBy: `${curr.name}|${curr.labelId}|${curr.color}`,
          labelId: curr.labelId,
        }))
      : [];

    return [...acc, ...labels];
  }, []);

const getFeedDescriptionResponse = ({ autocompleteFeedDescriptionGrouped: { grouped } }) =>
  grouped.reduce((acc, curr) => {
    const sources =
      curr.values?.map((feed) => ({
        label: feed.value,
        value: feed.value,
      })) ?? [];

    return [...acc, ...sources];
  }, []);

export default (action$, state$) =>
  action$.pipe(
    ofType(keywordSearchPrefixAction.type, labelSearchPrefixAction.type, feedDescriptionSearchPrefixAction.type),
    debounceTime(+process.env.APP_CLEAR_TIMEOUT),
    filter(() => !!getToken()),
    switchMap((action) => {
      const state = state$.value;
      const userAccountId = getUserAccountIdSelector(state);

      const config = configSelector(state);

      const accountOwnersFromFilter = config.account.filters?.[0].value?.[0]?.owners ?? [];
      const accountIdFromFilter = config.account.filters?.[0].value?.[0]?.value ?? null;

      const keywordType = keywordTypeSelector(state);

      let category;
      let prefix;
      let loadingAction;
      let optionsAction;
      let promiseOptions;
      let owner;
      let getResponse;
      let contentOwnersOwnContent;

      switch (action.type) {
        case keywordSearchPrefixAction.type:
          ({ prefix, owner } = action.payload);
          category = keywordType;
          loadingAction = keywordListLoadingAction;
          optionsAction = keywordListAction;
          promiseOptions = {
            query: queryGQLKeywords,
            variables: {
              contentOwner: accountOwnersFromFilter.length ? accountOwnersFromFilter : owner,
              category,
              prefix,
              excludeOrigin: 'RSS',
              checkBrandSafetyPermission: false,
            },
          };
          getResponse = getKeywordsResponse;
          break;
        case labelSearchPrefixAction.type:
          ({ prefix } = action.payload);
          loadingAction = labelListLoadingAction;
          optionsAction = labelListAction;
          promiseOptions = {
            query: queryGQLLabels,
            variables: {
              accountId: accountIdFromFilter || parseInt(userAccountId, 10),
              prefix,
              excludeOrigin: 'RSS',
            },
          };
          getResponse = getLabelsResponse;
          break;
        case feedDescriptionSearchPrefixAction.type:
          ({ prefix, owner } = action.payload);
          loadingAction = feedDescriptionListLoadingAction;
          optionsAction = feedDescriptionListAction;

          contentOwnersOwnContent = getUserContentOwnersSelector(state$.value)
            .filter((own) => own.publisherOwnsContent)
            .map((own) => own.contentOwnerId);

          promiseOptions = {
            query: queryGQLFeedDescriptionGrouped,
            variables: {
              contentOwners: accountOwnersFromFilter.length
                ? accountOwnersFromFilter
                : owner.filter((own) => !contentOwnersOwnContent.includes(own)),
              contentOwnersOwnContent,
              prefix,
              excludeOrigin: 'RSS',
            },
          };
          getResponse = getFeedDescriptionResponse;
          break;
        default:
          throw new Error('Wrong action type!');
      }

      const stream$ = gqlRequest(promiseOptions).pipe(
        switchMap(({ data, errors }) => {
          const actions = [];

          if (!errors.length) {
            const responseData = getResponse(data);

            actions.push(of(optionsAction(responseData)));
          }

          return concat(...actions);
        }),
      );

      const actions = [];

      actions.push(of(loadingAction(true)));
      actions.push(stream$);
      actions.push(of(loadingAction(false)));

      return concat(...actions);
    }),
  );
