import {omit, uniqBy} from 'lodash';

import {
    DATASET_ID_HEADER,
    TIMEOUT_60_SEC,
    TIMEOUT_90_SEC,
    WORKBOOK_ID_HEADER,
} from '../../../../constants';
import {getEntryNameByKey, normalizeDestination} from '../../../../modules';
import {Feature} from '../../../../types/feature';
import {createAction} from '../../../gateway-utils';
import {filterUrlFragment} from '../../../utils';
import type {
    CopyEntriesToWorkbookArgs,
    CopyEntriesToWorkbookResponse,
    CopyEntryArgs,
    CopyEntryResponse,
    CopyWorkbookEntryArgs,
    CopyWorkbookEntryResponse,
    CreateFolderArgs,
    CreateFolderResponse,
    DeleteSharedEntriesArgs,
    DeleteSharedEntriesResponse,
    DeleteUSEntryArgs,
    DeleteUSEntryResponse,
    EntityBindingsArgs,
    EntityBindingsResponse,
    GetEntriesAnnotationArgs,
    GetEntriesAnnotationResponse,
    GetEntriesByKeyPatternArgs,
    GetEntriesByKeyPatternResponse,
    GetEntryByKeyArgs,
    GetEntryByKeyResponse,
    GetEntryMetaArgs,
    GetEntryMetaResponse,
    GetRelationsArgs,
    GetRelationsGraphArgs,
    GetRelationsGraphResponse,
    GetRelationsOutput,
    GetRelationsResponse,
    GetRevisionsArgs,
    GetRevisionsOutput,
    GetRevisionsResponse,
    GetSharedEntryBindingsArgs,
    GetSharedEntryBindingsResponse,
    GetSharedEntryInfoArgs,
    GetSharedEntryInfoResponse,
    GetSharedEntryWorkbookRelationsArgs,
    GetSharedEntryWorkbookRelationsResponse,
    MoveEntryArgs,
    MoveEntryResponse,
    MoveSharedEntriesArgs,
    MoveSharedEntriesResponse,
    MoveSharedEntryArgs,
    RenameEntryArgs,
    RenameEntryResponse,
    SwitchPublicationStatusArgs,
    SwitchPublicationStatusResponse,
} from '../../types';

import {_createEntry} from './create-entry';
import {getEntries} from './get-entries';
import {getEntriesRelations} from './get-entries-relations';
import {getEntry} from './get-entry';
import {listDirectory} from './list-directory';
import {_updateEntry} from './update-entry';

const PATH_PREFIX = '/v1';
const PATH_PREFIX_V2 = '/v2';
const PRIVATE_PATH_PREFIX = '/private';

export const entriesActions = {
    _createEntry,
    _updateEntry,
    getEntriesRelations,
    listDirectory,
    getEntries,
    getEntry,
    _getEntryWithAudit: getEntry,
    getEntryByKey: createAction<GetEntryByKeyResponse, GetEntryByKeyArgs>({
        method: 'GET',
        path: () => `${PATH_PREFIX}/entriesByKey`,
        params: (query, headers) => ({query, headers}),
    }),
    getEntryMeta: createAction<GetEntryMetaResponse, GetEntryMetaArgs>({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/meta`,
        params: ({bindedWorkbookId, bindedDatasetId}, headers) => ({
            headers: {
                ...headers,
                ...(bindedWorkbookId ? {[WORKBOOK_ID_HEADER]: bindedWorkbookId} : {}),
                ...(bindedDatasetId ? {[DATASET_ID_HEADER]: bindedDatasetId} : {}),
            },
        }),
    }),
    getRevisions: createAction<GetRevisionsOutput, GetRevisionsArgs, GetRevisionsResponse>({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/revisions`,
        params: (args, headers, {ctx}) => {
            let updatedAfter;
            const isEnabledServerFeature = ctx.get('isEnabledServerFeature');
            if (!isEnabledServerFeature(Feature.RevisionsListNoLimit) && !args.revIds) {
                const date = new Date();
                date.setMonth(date.getMonth() - 3);
                updatedAfter = date.toISOString();
            }
            return {
                query: {
                    ...omit(args, 'entryId'),
                    updatedAfter,
                },
                headers,
            };
        },
        transformResponseData: (data) => ({
            hasNextPage: Boolean(data.nextPageToken),
            entries: data.entries,
        }),
    }),
    getRelations: createAction<GetRelationsOutput, GetRelationsArgs, GetRelationsResponse>({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/relations`,
        params: (args, headers) => ({
            query: omit(args, 'entryId', 'excludeUnregistredDlsEntries'),
            headers,
        }),
        transformResponseData: (data, {args, ctx}) => {
            let uniqRelations = uniqBy(
                data.map((relationEntry) => ({
                    ...relationEntry,
                    name: getEntryNameByKey({key: relationEntry.key}),
                })),
                (relationEntry) => relationEntry.entryId,
            );
            if (args.excludeUnregistredDlsEntries) {
                const isEnabledServerFeature = ctx.get('isEnabledServerFeature');
                if (isEnabledServerFeature(Feature.UseYqlFolderKey)) {
                    const yqlFolderKey = 'yql/charts/';
                    uniqRelations = uniqRelations.filter(
                        ({key}) => !key.toLowerCase().startsWith(yqlFolderKey),
                    );
                }
            }
            return uniqRelations;
        },
    }),
    moveEntry: createAction<MoveEntryResponse, MoveEntryArgs>({
        method: 'POST',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/move`,
        params: (args, headers) => {
            const body: Omit<MoveEntryArgs, 'entryId'> = {
                destination: normalizeDestination(args.destination),
            };
            if (args.name) {
                body.name = args.name;
            }
            return {body, headers};
        },
        timeout: TIMEOUT_60_SEC,
    }),
    copyEntry: createAction<CopyEntryResponse, CopyEntryArgs>({
        method: 'POST',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/copy`,
        params: (args, headers) => {
            const body: Omit<CopyEntryArgs, 'entryId'> = {
                destination: normalizeDestination(args.destination),
            };
            if (args.name) {
                body.name = args.name;
            }
            return {body, headers};
        },
    }),
    copyWorkbookEntry: createAction<CopyWorkbookEntryResponse, CopyWorkbookEntryArgs>({
        method: 'POST',
        path: ({entryId}) => `${PATH_PREFIX_V2}/entries/${filterUrlFragment(entryId)}/copy`,
        params: (args, headers) => {
            const body: Omit<CopyWorkbookEntryArgs, 'entryId'> = {};

            if (args.name) {
                body.name = args.name;
            }

            return {body, headers};
        },
    }),
    copyEntriesToWorkbook: createAction<CopyEntriesToWorkbookResponse, CopyEntriesToWorkbookArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX_V2}/copy-entries`,
        params: (args, headers) => ({
            body: {workbookId: args.workbookId, entryIds: args.entryIds},
            headers,
        }),
    }),
    renameEntry: createAction<RenameEntryResponse, RenameEntryArgs>({
        method: 'POST',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/rename`,
        params: ({name}, headers) => ({body: {name}, headers}),
        timeout: TIMEOUT_60_SEC,
    }),
    createFolder: createAction<CreateFolderResponse, CreateFolderArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX}/entries`,
        params: ({key}, headers) => {
            return {
                body: {
                    scope: 'folder',
                    type: '',
                    key,
                    meta: {},
                    data: {},
                },
                headers,
            };
        },
    }),
    _deleteUSEntry: createAction<DeleteUSEntryResponse, DeleteUSEntryArgs>({
        method: 'DELETE',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}`,
        params: ({lockToken, scope, types}, headers) => ({
            query: {lockToken, scope, types},
            headers,
        }),
    }),
    _switchPublicationStatus: createAction<
        SwitchPublicationStatusResponse,
        SwitchPublicationStatusArgs
    >({
        method: 'POST',
        path: () => `${PRIVATE_PATH_PREFIX}/entries/switchPublicationStatus`,
        params: ({entries, mainEntry}, headers) => ({
            body: {entries, mainEntry},
            headers,
        }),
    }),
    _getEntriesByKeyPattern: createAction<
        GetEntriesByKeyPatternResponse,
        GetEntriesByKeyPatternArgs
    >({
        method: 'GET',
        path: () => `${PRIVATE_PATH_PREFIX}/getEntriesByKeyPattern`,
        params: ({keyPattern}, headers) => ({query: {keyPattern}, headers}),
    }),
    getRelationsGraph: createAction<GetRelationsGraphResponse, GetRelationsGraphArgs>({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/entries/${filterUrlFragment(entryId)}/relations-graph`,
        timeout: TIMEOUT_90_SEC,
    }),
    getEntriesAnnotation: createAction<GetEntriesAnnotationResponse, GetEntriesAnnotationArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX}/get-entries-annotation`,
        params: (params, headers) => ({
            body: params,
            headers,
        }),
    }),
    getSharedEntryDelegation: createAction<
        EntityBindingsResponse,
        Omit<EntityBindingsArgs, 'delegation'>
    >({
        method: 'GET',
        path: () => `${PATH_PREFIX}/entity-bindings`,
        params: ({sourceId, targetId, bindedWorkbookId}, headers) => ({
            query: {
                sourceId,
                targetId,
            },
            headers: {
                ...headers,
                ...(bindedWorkbookId ? {[WORKBOOK_ID_HEADER]: bindedWorkbookId} : {}),
            },
        }),
    }),
    createSharedEntryBinding: createAction<EntityBindingsResponse, EntityBindingsArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX}/entity-bindings/create`,
        params: (params, headers) => ({
            body: params,
            headers,
        }),
    }),
    updateSharedEntryBinding: createAction<EntityBindingsResponse, EntityBindingsArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX}/entity-bindings/update`,
        params: (params, headers) => ({
            body: params,
            headers,
        }),
    }),
    deleteSharedEntryBinding: createAction<
        EntityBindingsResponse,
        Omit<EntityBindingsArgs, 'delegation'>
    >({
        method: 'DELETE',
        path: () => `${PATH_PREFIX}/entity-bindings/delete`,
        params: (params, headers) => ({
            query: params,
            headers,
        }),
    }),
    getSharedEntryBindings: createAction<
        GetSharedEntryBindingsResponse,
        GetSharedEntryBindingsArgs
    >({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/shared-entries/${entryId}/entity-bindings`,
        params: (
            {page, pageSize, entryAs, mode, filterString, includePermissionsInfo},
            headers,
        ) => ({
            query: {
                pageSize,
                page,
                entryAs,
                mode,
                filterString,
                includePermissionsInfo,
            },
            headers,
        }),
    }),
    getSharedEntryWorkbookRelations: createAction<
        GetSharedEntryWorkbookRelationsResponse,
        GetSharedEntryWorkbookRelationsArgs
    >({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/shared-entries/${entryId}/workbook-relations`,
        params: ({scope, workbookId}, headers) => ({
            query: {
                workbookId,
                scope,
            },
            headers,
        }),
    }),
    moveSharedEntry: createAction<MoveEntryResponse, MoveSharedEntryArgs>({
        method: 'POST',
        path: ({entryId}) => `${PATH_PREFIX}/shared-entries/${filterUrlFragment(entryId)}/move`,
        params: ({collectionId, name}, headers) => ({headers, body: {collectionId, name}}),
        timeout: TIMEOUT_60_SEC,
    }),
    moveSharedEntries: createAction<MoveSharedEntriesResponse, MoveSharedEntriesArgs>({
        method: 'POST',
        path: () => `${PATH_PREFIX}/shared-entries/move-entries`,
        params: ({collectionId, entryIds}, headers) => ({headers, body: {collectionId, entryIds}}),
        timeout: TIMEOUT_60_SEC,
    }),
    deleteSharedEntries: createAction<DeleteSharedEntriesResponse, DeleteSharedEntriesArgs>({
        method: 'DELETE',
        path: () => `${PATH_PREFIX}/shared-entries/delete-entries`,
        params: ({entryIds}, headers) => ({headers, body: {entryIds}}),
        timeout: TIMEOUT_60_SEC,
    }),
    getSharedEntryInfo: createAction<GetSharedEntryInfoResponse, GetSharedEntryInfoArgs>({
        method: 'GET',
        path: ({entryId}) => `${PATH_PREFIX}/shared-entries/${entryId}/info`,
        params: ({includePermissionsInfo}, headers) => ({headers, query: {includePermissionsInfo}}),
        timeout: TIMEOUT_60_SEC,
    }),
};
