/*
 * Copyright (C) 2019-2022 Open StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * unknown WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {SCTranslations} from './general/i18n.js';
import {SCBulkResponse} from './protocol/routes/bulk-request.js';
import {SCSearchResponse} from './protocol/routes/search.js';
import {SCMultiSearchResponse} from './protocol/routes/search-multi.js';
import {
  SCThing,
  SCThingTranslatableProperties,
  SCThingType,
  SCThingWithoutReferences,
} from './things/abstract/thing.js';

/**
 * Type guard to check if something is a SCThing
 * @param something Something to check
 */
export function isThing(something: unknown): something is SCThing {
  if (typeof something !== 'object' || something === null) {
    return false;
  }

  if (!('type' in something)) {
    return false;
  }

  const type = (something as {type: unknown}).type;

  if (typeof type !== 'string') {
    return false;
  }

  return Object.values(SCThingType).includes(type as SCThingType);
}

/**
 * Type guard to check if translations exist
 * @param thing Thing to check
 */
export function isThingWithTranslations(
  thing: SCThingWithoutReferences,
): thing is SCThingWithoutReferences & {translations: SCTranslations<SCThingTranslatableProperties>} {
  return thing.translations !== undefined;
}

/**
 * Type guard to check if something is a bulk response
 * @param something Something to check
 */
export function isBulkResponse(something: unknown): something is SCBulkResponse {
  if (typeof something !== 'object' || something === null) {
    return false;
  }

  if (
    !('expiration' in something) ||
    !('source' in something) ||
    !('state' in something) ||
    !('type' in something) ||
    !('uid' in something)
  ) {
    return false;
  }

  const {expiration, source, state, type, uid} = something as {
    expiration: unknown;
    source: unknown;
    state: unknown;
    type: unknown;
    uid: unknown;
  };

  return (
    typeof expiration === 'string' &&
    typeof source === 'string' &&
    typeof state === 'string' &&
    typeof type === 'string' &&
    typeof uid === 'string'
  );
}

/**
 * Type guard to check if something is a search response
 * @param something Something to check
 */
export function isSearchResponse(something: unknown): something is SCSearchResponse {
  if (!(typeof something === 'object') || something === null) {
    return false;
  }
  const somethingObject = something as {[key: string]: {[key: string]: string}};

  return (
    Array.isArray(somethingObject.data) &&
    Array.isArray(somethingObject.facets) &&
    somethingObject.pagination !== undefined &&
    typeof somethingObject.pagination.count === 'number' &&
    typeof somethingObject.pagination.offset === 'number' &&
    typeof somethingObject.pagination.total === 'number' &&
    somethingObject.stats !== undefined &&
    typeof somethingObject.stats.time === 'number'
  );
}

/**
 * Type guard to check if something is a multi search response
 * @param something Something to check
 */
export function isMultiSearchResponse(something: unknown): something is SCMultiSearchResponse {
  const initialValue = Object.keys(something as {[key: string]: string}).length > 0 ? true : false;

  // eslint-disable-next-line unicorn/no-array-reduce
  return Object.keys(something as {[key: string]: string}).reduce((previousOnesAreSearchResponses, key) => {
    return previousOnesAreSearchResponses && isSearchResponse((something as {[key: string]: string})[key]);
  }, initialValue as boolean);
}
