import React, {
    useEffect,
    useState,
    type Dispatch,
    type SetStateAction,
} from 'react';
import { Alert, GridTable, useToast } from '@theorchard/suite-components';
import { Illustration } from '@theorchard/suite-icons';
import { camelCase, isEmpty } from 'lodash-es';
import { useHistory, useLocation } from 'react-router-dom';
import { SortOrder } from 'src/apollo/definitions/globalTypes';
import { useStatementPeriodAdjustmentFilesList } from 'src/apollo/queries/adjustment';
import { inProgressAdjustmentsFiles } from 'src/apollo/reactive-vars/adjustment-files-status';
import {
    ADJUSTMENTS_LIST_ERROR_MESSAGE,
    sortByColumn,
    sortDirection,
    SORT_DIRECTION_MAP,
} from 'src/apollo/type-constants/adjustment';
import { useSoftDeleteStatementPeriodAdjustmentFile } from 'src/apollo/mutations/statement-period-adjustment-file';
import { useCreateAbacusEvent } from 'src/apollo/mutations/abacus-event';
import { useResetAbacusStates } from 'src/apollo/mutations/abacus-state';
import { AdjustmentsFilters } from 'src/components/adjustments/adjustments-filters';
import AdjustmentsListTableColumns from 'src/components/adjustments/adjustments-list-table-columns';
import {
    ADJUST_FILTERS_MSG,
    DEFAULT_ITEMS_PER_PAGE,
    DEFAULT_NO_RESULTS_MSG,
    NO_ADJUSTMENT_RESULTS,
    USER_FEATURES,
} from 'src/constants';
import { isFeatureFlagEnabled } from 'src/utils/feature-flag';
import { getQueryParams, updateQueryParam } from 'src/utils/route';
import type { GridTableSortBy } from '@theorchard/suite-components';
import type { GetStatementPeriodAdjustmentFilesListQuery } from 'src/apollo/queries/adjustment/__generated__/get-statement-period-adjustments-files-list';
import type { AdjustmentFilter } from 'src/types/adjustment-file';

type _AbacusStatementPeriodAdjustmentFilesList = NonNullable<
    GetStatementPeriodAdjustmentFilesListQuery['abacusStatementPeriodAdjustmentFilesList']
>;
type AbacusStatementPeriodAdjustmentFilesList =
    _AbacusStatementPeriodAdjustmentFilesList['items'][0];

interface AdjustmentsListPropTypes {
    setRetryBatch: Dispatch<SetStateAction<number>>;
}

export const AdjustmentsList: React.FC<AdjustmentsListPropTypes> = ({
    setRetryBatch,
}) => {
    const history = useHistory();
    const location = useLocation();

    const isAutoGenerateEnabled = isFeatureFlagEnabled(
        USER_FEATURES.ABACUS_AUTO_GENERATE_ADJUSTMENTS_FLOWTHROUGH
    );

    const [pageSize, setPageSize] = useState<number>(DEFAULT_ITEMS_PER_PAGE);
    const [page, setPage] = useState<number>(0);
    const [sortBy, setSortBy] = useState<GridTableSortBy[]>();
    const [disableRetryBatch, setDisableRetryBatch] = useState<number>(0);
    const [deletingBatch, setDeletingBatch] = useState<number>(0);

    const [totalCount, setTotalCount] = useState<number>(0);
    const [adjustments, setAdjustments] = useState<
        AbacusStatementPeriodAdjustmentFilesList[]
    >([]);
    const [clearBatchIdSearchField, setClearBatchIdSearchField] =
        useState<boolean>(false);

    const [requestParams, setRequestParams] = useState<AdjustmentFilter>({
        limit: pageSize,
        offset: page * pageSize,
        sortOrder: SortOrder.DESC,
    });

    const toast = useToast();
    const softDeleteStatementPeriodAdjustmentFile =
        useSoftDeleteStatementPeriodAdjustmentFile();
    const resetAbacusStates = useResetAbacusStates();
    const createAbacusEvent = useCreateAbacusEvent();

    const { data, error, loading, refetch, networkStatus } =
        useStatementPeriodAdjustmentFilesList({
            limit: requestParams.limit,
            offset: requestParams.offset,
            sortOrder: requestParams.sortOrder,
            statementPeriodAdjustmentFileId:
                requestParams?.statementPeriodAdjustmentFileId,
            statementPeriodAdjustmentFileStatus:
                requestParams?.statementPeriodAdjustmentFileStatus,
            filters: {
                createdBy: requestParams?.createdBy,
                fileName: requestParams?.fileName,
                statementPeriodId: requestParams?.statementPeriodId,
            },
        });

    const deleteBatch = async (batchId: string) => {
        try {
            setDeletingBatch(parseInt(batchId, 10));
            await softDeleteStatementPeriodAdjustmentFile({
                variables: {
                    statementPeriodAdjustmentFileId: batchId,
                },
            });
            await refetch();
            toast(`Batch ${batchId} has been successfully deleted.`);
            setDeletingBatch(0);
        } catch (e) {
            setDeletingBatch(0);
            toast(`Error while deleting batch ${batchId}. Please try again.`);
        } finally {
            setDeletingBatch(0);
        }
    };

    const retryBatch = async (batchId: string, fileName: string) => {
        try {
            const id = parseInt(batchId, 10);
            setDisableRetryBatch(id);
            toast(
                `Regeneration for batch ${batchId} has been started and it will take time.`
            );
            const currentFiles = inProgressAdjustmentsFiles();

            inProgressAdjustmentsFiles({
                ...currentFiles,
                [batchId]: {
                    id: batchId,
                    status: 'failed_to_generate',
                    fileName: fileName,
                },
            });
            await resetAbacusStates({
                variables: {
                    parentTableId: batchId,
                    parentTableName: 'statement_period_adjustment_file',
                },
            });
            await createAbacusEvent({
                variables: {
                    eventName: 'generate_flowthrough_adjustments',
                    targetId: id,
                    targetType: 'statement_period_adjustment_file',
                },
            });

            setRetryBatch(id);
            await refetch();
            setDisableRetryBatch(0);
        } catch (e) {
            setDisableRetryBatch(0);
            toast(
                `Error while retrying batch ${batchId}. Please contact support.`
            );
        }
    };

    const hasFilterApplied = () =>
        Boolean(
            requestParams?.statementPeriodAdjustmentFileId ||
            !isEmpty(requestParams?.statementPeriodAdjustmentFileStatus) ||
            requestParams?.fileName ||
            requestParams?.statementPeriodId ||
            requestParams?.createdBy
        );

    useEffect(() => {
        setRequestParams({
            ...requestParams,
            limit: pageSize,
            offset: page * pageSize,
        });
    }, [page, pageSize]);

    useEffect(() => {
        /* eslint-disable @typescript-eslint/ban-ts-comment */
        if (sortBy)
            setRequestParams({
                ...requestParams,
                // @ts-ignore
                sortBy: sortByColumn[sortBy[0].key],
                // @ts-ignore
                sortOrder: sortDirection[sortBy[0].direction],
            });
        /* eslint-enable @typescript-eslint/ban-ts-comment */
    }, [sortBy]);

    useEffect(() => {
        if (data && data?.abacusStatementPeriodAdjustmentFilesList) {
            setTotalCount(
                data.abacusStatementPeriodAdjustmentFilesList.totalCount || 0
            );
            setAdjustments(
                data.abacusStatementPeriodAdjustmentFilesList.items || []
            );
        }
    }, [data, loading, requestParams]);

    const queryParams: any = {
        batchId: {
            name: 'batchId',
            type: 'string',
        },
        status: {
            name: 'status',
            type: 'string[]',
        },
        fileName: {
            name: 'fileName',
            type: 'string',
        },
        statementPeriodId: {
            name: 'statementPeriodId',
            type: 'string',
        },
        addedBy: {
            name: 'addedBy',
            type: 'string',
        },
    };

    const mapQueryParams: any = {
        statementPeriodAdjustmentFileId: 'batchId',
        statementPeriodAdjustmentFileStatus: 'status',
        fileName: 'fileName',
        statementPeriodId: 'statementPeriodId',
        createdBy: 'addedBy',
    };

    const changeHandler = (fieldName: string, value: string) => {
        const key = fieldName as keyof AdjustmentFilter;
        if (!value || value.length === 0) {
            delete requestParams[key];
            setRequestParams({ ...requestParams, offset: 0 });
            setPage(0);
            if (key == 'statementPeriodAdjustmentFileStatus') refetch();
        } else
            setRequestParams({
                ...requestParams,
                offset: 0,
                [key]:
                    key === 'statementPeriodAdjustmentFileId'
                        ? value.trim()
                        : value,
            });
        setPage(0);
        updateQueryParam(
            location,
            history,
            queryParams,
            mapQueryParams[key],
            value
        );
    };

    const onClearFilters = () => {
        setRequestParams({
            limit: pageSize,
            offset: page * pageSize,
            sortOrder: SortOrder.DESC,
        });
        setPage(0);
        setClearBatchIdSearchField(!clearBatchIdSearchField);
        updateQueryParam(location, history, queryParams, null, null);
    };

    useEffect(() => {
        const parsedParams = getQueryParams(location, queryParams);
        const updatedParams: any = {};

        Object.keys(mapQueryParams).forEach((key: string) => {
            if (Object.keys(parsedParams).includes(mapQueryParams[key])) {
                if (!isEmpty(parsedParams[mapQueryParams[key]]))
                    updatedParams[key] = parsedParams[mapQueryParams[key]];
            }
        });

        if (!isEmpty(updatedParams)) {
            setRequestParams({ ...requestParams, ...updatedParams });
        }
    }, []);

    return (
        <div className="Adjustments-list-table">
            {error && (
                <Alert
                    variant="error"
                    text={ADJUSTMENTS_LIST_ERROR_MESSAGE}
                    className="error-alert"
                />
            )}
            <AdjustmentsFilters
                changeHandler={changeHandler}
                clearBatchIdSearchField={clearBatchIdSearchField}
                requestParams={requestParams}
                setRequestParams={setRequestParams}
            />
            <GridTable
                stickyHeader
                bordered
                totalCount={totalCount}
                page={page}
                pageSize={pageSize}
                paginated
                onPageChange={setPage}
                onPageSizeChange={setPageSize}
                showUpdateIndicator
                defaultColumnDefs={{
                    headerTextWrap: 'nowrap',
                }}
                columnDefs={AdjustmentsListTableColumns(
                    isAutoGenerateEnabled,
                    disableRetryBatch,
                    deleteBatch,
                    retryBatch
                )}
                data={adjustments}
                loading={
                    loading ||
                    networkStatus === 4 ||
                    disableRetryBatch !== 0 ||
                    deletingBatch !== 0
                }
                loadingRows={5}
                className="AdjustmentsListTable"
                rowKey="StatementPeriodAdjustmentFile"
                sortBy={[
                    {
                        key: camelCase(requestParams?.sortBy),
                        direction: SORT_DIRECTION_MAP[requestParams.sortOrder],
                    },
                ]}
                onSort={setSortBy}
                emptyStateTitle={
                    hasFilterApplied()
                        ? DEFAULT_NO_RESULTS_MSG
                        : NO_ADJUSTMENT_RESULTS
                }
                emptyStateIcon={<Illustration name="noResults" />}
                emptyStateBody={hasFilterApplied() ? ADJUST_FILTERS_MSG : ''}
                onClearFilters={onClearFilters}
                showClearFilters={hasFilterApplied()}
            />
        </div>
    );
};

export default AdjustmentsList;
