import type { FC } from 'react';
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { Card, LoadingIndicator } from '@theorchard/suite-components';
import { useHistory } from 'react-router-dom';
import EditPublisher from 'src/components/editPublisher';
import EmptyState from 'src/components/emptyState';
import ReadError from 'src/components/errors/read-error';
import HeaderLabelPicker from 'src/components/headerLabelPicker';
import { SHOW_ALL_LABEL_UUID } from 'src/components/headerLabelPicker/labelSearchDropdown';
import ViewPublisher from 'src/components/viewPublisher';
import { useDeletePublisherMutation } from 'src/data/mutations/deletePublisher/deletePublisher';
import { usePublishingPublishers } from 'src/data/queries';
import PageControls from 'src/pages/publishersListPage/pageControls';
import PublishersTable from 'src/pages/publishersListPage/publishersTable';
import { useApplicationContext } from 'src/utils/applicationContext';
import { useLocationSearchQuery, useLocationWithParams } from 'src/utils/route';
import { publishersListUrl } from 'src/utils/urls';
import { isValidIpi } from 'src/utils/validators';
import type { PublishingPublishersFilter } from 'src/data/schemaTypes';
import type { Label, Publisher } from 'src/types';

export const CLASSNAME = 'PublishersListPage';
const enum DetailMode {
    View = 'view',
    Edit = 'edit',
}
export const PUBLISHERS_PER_PAGE = 25;

const enum URLParams {
    PUBLISHER = 'publisher',
}

export interface PublishingPublishersFilterLabel
    extends PublishingPublishersFilter {
    label?: Label | null;
}

const extractValuesFromSearchQuery = (searchQuery: URLSearchParams) => {
    const publisher = decodeURIComponent(
        searchQuery.get(URLParams.PUBLISHER) || ''
    );

    return {
        publisher,
    };
};

const PublishersListPage: FC = () => {
    const history = useHistory();

    const {
        label: labelFromContext,
        pageFilters,
        updatePageFilters,
    } = useApplicationContext();

    const deletePublisher = useDeletePublisherMutation();

    const { params } = useLocationWithParams();
    const page = parseInt(params[0] || '1', 10);
    const { publisher: publisherFromSearchQuery } =
        extractValuesFromSearchQuery(useLocationSearchQuery());

    const publisherSearch =
        publisherFromSearchQuery ||
        pageFilters?.publishersPagePublisherSearch ||
        null;

    const [filter, setFilter] = useState<PublishingPublishersFilterLabel>({
        label: null,
        nameOrIpiSearch: publisherSearch,
    });
    const [selectedPublisher, setSelectedPublisher] =
        useState<Publisher | null>(null);
    const [detailMode, setDetailMode] = useState<string>('');
    const [pageSize, setPageSize] = useState(PUBLISHERS_PER_PAGE);

    const handlePageSizeChange = (newPageSize: number) => {
        setPageSize(newPageSize);
    };

    useEffect(() => {
        if (
            filter.label?.uuid === labelFromContext?.uuid ||
            (labelFromContext?.uuid === SHOW_ALL_LABEL_UUID && !filter.label)
        )
            return;

        setFilter(f => ({
            ...f,
            label:
                labelFromContext &&
                labelFromContext.uuid !== SHOW_ALL_LABEL_UUID
                    ? labelFromContext
                    : null,
        }));
    }, [labelFromContext, filter]);

    const {
        data: publishersQueryData,
        loading,
        error,
    } = usePublishingPublishers(filter, page, pageSize);

    const onSearchChange = useCallback(
        (e: React.ChangeEvent<HTMLInputElement>) => {
            const inputValue = e.target.value;

            const isNumeric = !Number.isNaN(parseInt(inputValue, 10));
            updatePageFilters({
                publishersPagePublisherSearch: isValidIpi(inputValue)
                    ? null
                    : inputValue,
            });

            // This is to allow users to filter by partial IPIs & publishers with numbers in their name
            setFilter(f => {
                const { nameOrIpiSearch, nameSearch, ...newFilter } = f;
                return isNumeric
                    ? {
                          ...newFilter,
                          nameOrIpiSearch: inputValue,
                      }
                    : { ...newFilter, nameSearch: inputValue };
            });
        },
        [updatePageFilters]
    );

    const onPaginationChange = useCallback(
        (newPage: number) => {
            history.push(publishersListUrl(newPage + 1, {}));
        },
        [history]
    );

    const totalPages = useMemo(
        () => Math.ceil((publishersQueryData?.totalCount || 0) / pageSize),
        [publishersQueryData?.totalCount, pageSize]
    );

    // Check if the page number is valid and redirect if necessary to the first page when the publishers list changes
    // If the page number is greater than the total number of pages, redirect to the first page
    useEffect(() => {
        let updatedPage = page;

        if (!loading) {
            if (publishersQueryData?.totalCount !== undefined) {
                const newPageNumber =
                    totalPages === 0 || page > totalPages ? 1 : page;
                if (newPageNumber !== page) updatedPage = newPageNumber;
            }
        }

        history.push(
            publishersListUrl(updatedPage, {
                nameSearch: filter.nameSearch || filter.nameOrIpiSearch,
                ipi: filter.ipi,
            })
        );
    }, [
        loading,
        page,
        publishersQueryData?.totalCount,
        history,
        totalPages,
        filter,
    ]);

    const onPublisherView = useCallback((publisher: Publisher) => {
        setSelectedPublisher(publisher);
        setDetailMode(DetailMode.View);
    }, []);

    const onPublisherDelete = useCallback(
        ({ id }: Publisher) => {
            void deletePublisher({ variables: { id } });
        },
        [deletePublisher]
    );

    const onPublisherEdit = useCallback((publisher: Publisher) => {
        setSelectedPublisher(publisher);
        setDetailMode(DetailMode.Edit);
    }, []);

    const onDetailModeReset = useCallback(() => {
        setSelectedPublisher(null);
        setDetailMode('');
    }, []);

    if (!publishersQueryData) return <LoadingIndicator />;

    if (error) return <ReadError error={error} />;

    return (
        <div className={CLASSNAME}>
            {!!selectedPublisher && detailMode === DetailMode.View && (
                <ViewPublisher
                    isOpen
                    onClose={onDetailModeReset}
                    publisher={selectedPublisher}
                />
            )}

            {!!selectedPublisher && detailMode === DetailMode.Edit && (
                <EditPublisher
                    isOpen
                    onClose={onDetailModeReset}
                    publisher={selectedPublisher}
                />
            )}

            <div className="AppHeader">
                <HeaderLabelPicker />
            </div>

            <PageControls
                onSearchChange={onSearchChange}
                searchValue={publisherSearch}
            />

            <div className="PageContent">
                <Card suite>
                    {!publishersQueryData.publishers?.length ? (
                        <EmptyState
                            isLoading={loading}
                            page="publishers"
                            showSubHeader={false}
                        />
                    ) : (
                        <PublishersTable
                            publishers={publishersQueryData.publishers}
                            page={page - 1}
                            totalPages={totalPages}
                            totalCount={publishersQueryData.totalCount || 0}
                            pageSize={pageSize}
                            onPaginationChange={onPaginationChange}
                            onPublisherView={onPublisherView}
                            onPublisherDelete={onPublisherDelete}
                            onPublisherEdit={onPublisherEdit}
                            onSetPageSize={handlePageSizeChange}
                        />
                    )}
                </Card>
            </div>
        </div>
    );
};

export default PublishersListPage;
