import React, { useCallback, useEffect, useRef, useState } from 'react';
import { LoadingSpinner, Pagination } from '@theorchard/suite-components';
import { Page } from '@theorchard/suite-frontend';
import StickyHeader from 'src/components/stickyHeader';
import { ROUTE_CATALOG } from 'src/constants';
import { useGetContributionsQuery } from 'src/data/queries';
import { PAGE_SIZE } from 'src/pages/catalogPage/constants';
import { useContributionsParamsComposer } from 'src/pages/catalogPage/utils/contributionsParamsComposer';
import { useRouteParams } from 'src/utils/route';
import {
    recordCatalogSortingByKey,
    recordVisitMyCatalog,
    recordClearFilters,
    recordRowsPerPageClick,
    recordGoToPreviousPage,
    recordGoToNextPage,
    recordGoToLastPage,
    recordGoToFirstPage,
} from 'src/utils/segment/segment';
import CatalogTable from './catalogTable';
import Filters from './components/filters';
import type { SortBy, SortDirection } from './types';
import type { QueryParamsConfig } from '@theorchard/suite-frontend';
import type { ParamValue } from 'src/utils/route';

export const CLASSNAME = 'CatalogPage';
export const DEFAULT_SORT_DIR: SortDirection = 'desc';

export interface CatalogRouteParams {
    selectedAccount: number;
    selectedIsrc: string;
    selectedParticipant: string;
    selectedPriority: string;
    selectedRecordingTitle: string;
    selectedContributor: string;
    selectedRecordingId: string;
    orderBy: string;
    orderDir: string;
}

export const PAGE_QUERY_PARAMS: QueryParamsConfig = {
    selectedAccount: { name: 'account', type: 'number' },
    selectedIsrc: { name: 'isrc', type: 'string' },
    selectedParticipant: { name: 'participant', type: 'string' },
    selectedPriority: { name: 'priority', type: 'string' },
    selectedRecordingTitle: { name: 'recordingTitle', type: 'string' },
    selectedContributor: { name: 'contributor', type: 'string' },
    selectedRecordingId: { name: 'soundRecordingId', type: 'string' },
    orderBy: { name: 'orderBy', default: 'lastModifiedAt', type: 'string' },
    orderDir: {
        name: 'orderDir',
        default: `${DEFAULT_SORT_DIR}`,
        type: 'string',
    },
};

const CatalogPage = () => {
    useEffect(() => {
        recordVisitMyCatalog();
    }, []);

    const [page, setPage] = useState(0);
    const [pageSize, setPageSize] = useState(PAGE_SIZE);
    const headerVisibilityThreshold = useRef<HTMLDivElement>(null);
    const [params, setParams] = useRouteParams<CatalogRouteParams>(
        ROUTE_CATALOG,
        {
            queryParams: PAGE_QUERY_PARAMS,
        }
    );

    const {
        selectedAccount,
        selectedIsrc,
        selectedParticipant,
        selectedPriority,
        selectedRecordingTitle,
        selectedRecordingId,
        selectedContributor,
        orderBy,
        orderDir,
    } = params;

    const {
        data: contributions,
        loading,
        error,
        fetchMore,
        fetchingMore,
    } = useGetContributionsQuery(
        useContributionsParamsComposer(page, pageSize, params)
    );

    const filtersApplied = !!(
        selectedIsrc ||
        selectedRecordingTitle ||
        selectedAccount ||
        selectedPriority ||
        selectedContributor ||
        selectedParticipant ||
        selectedRecordingId
    );
    const sortingApplied =
        orderDir !== DEFAULT_SORT_DIR || orderBy !== DEFAULT_SORT_DIR;

    const handleFiltersChange = useCallback(
        (filters: string | Record<string, ParamValue | undefined>) => {
            // reset pagination when filters change
            setPage(0);
            setParams(filters);
        },
        [setParams]
    );

    useEffect(() => {
        const handleFetchMore = (limit: number, offset: number) => {
            void fetchMore({ variables: { limit, offset } });
        };

        if (!loading) handleFetchMore(pageSize, page * pageSize);
    }, [fetchMore, loading, page, pageSize]);

    const handleSort = (sortBy: SortBy[]) => {
        setParams({ orderBy: sortBy[0].key, orderDir: sortBy[0].direction });
        recordCatalogSortingByKey(sortBy[0].key);
    };

    const handleClearFilters = useCallback(() => {
        setParams({
            account: undefined,
            isrc: undefined,
            participant: undefined,
            priority: undefined,
            recordingTitle: undefined,
            contributor: undefined,
            soundRecordingId: undefined,
        });
        recordClearFilters();
    }, [setParams]);

    const handlePageChange = useCallback(
        (currentPage: number) => {
            const shift = currentPage - page;
            const lastPage =
                Math.ceil((contributions?.totalCount ?? 0) / pageSize) - 1;

            if (shift === 1) {
                recordGoToNextPage();
            } else if (shift === -1) {
                recordGoToPreviousPage();
            } else if (currentPage === lastPage) {
                recordGoToLastPage();
            } else if (currentPage === 0) {
                recordGoToFirstPage();
            }

            setPage(currentPage);
        },
        [page, contributions?.totalCount, pageSize]
    );

    const handlePageSizeChange = useCallback((size: number) => {
        setPageSize(size);
        recordRowsPerPageClick(size);
    }, []);

    return (
        <div className={CLASSNAME}>
            <StickyHeader target={headerVisibilityThreshold}>
                <Filters setParams={handleFiltersChange} params={params} />
                <div
                    className={`${CLASSNAME}-sticky-row`}
                    style={{ paddingBottom: loading ? 10 : 0 }}
                >
                    <Pagination
                        totalCount={contributions?.totalCount ?? 0}
                        currentPage={page}
                        pageSize={pageSize}
                        onChange={handlePageChange}
                        onSetPageSize={handlePageSizeChange}
                        loading={loading}
                    />
                    {loading && (
                        <div className={`${CLASSNAME}-sticky-row-status`}>
                            <LoadingSpinner
                                show
                                className={`${CLASSNAME}-sticky-row-status-icon`}
                            />
                            {$t('neighbouringRights.catalogPage.updatingTable')}
                        </div>
                    )}
                </div>
            </StickyHeader>
            <Filters setParams={handleFiltersChange} params={params} />
            <Page.View
                error={error}
                loading={loading}
                hasData={!!contributions && !error}
            >
                <div ref={headerVisibilityThreshold} />
                <CatalogTable
                    contributions={contributions?.items ?? []}
                    totalContributions={contributions?.totalCount}
                    loading={
                        (loading || fetchingMore) &&
                        (sortingApplied || filtersApplied)
                    }
                    onSort={handleSort}
                    sortBy={{
                        key: orderBy,
                        direction: orderDir as SortDirection,
                    }}
                    onClearFilters={handleClearFilters}
                    filtersApplied={filtersApplied}
                    page={page}
                    pageSize={pageSize}
                    handlePageChange={handlePageChange}
                    handlePageSizeChange={handlePageSizeChange}
                />
            </Page.View>
        </div>
    );
};

export default CatalogPage;
