import type { FC } from 'react';
import React, { useEffect, useCallback, useState } from 'react';
import {
    Button,
    Card,
    LoadingIndicator,
    HelpTooltip,
    useToast,
} from '@theorchard/suite-components';
import {
    formatMessage,
    Segment,
    useIdentity,
} from '@theorchard/suite-frontend';
import { GlyphIcon } from '@theorchard/suite-icons';
import { useHistory } from 'react-router-dom';
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 RegisterNewSongwriter from 'src/components/registerNewSongwriter';
import ViewSongWriter from 'src/components/viewSongWriter';
import { FEATURE_FLAGS } from 'src/constants';
import { useDeleteSongWriter } from 'src/data/mutations/deleteSongWriter/deleteSongWriter';
import { usePublishingSongWriters } from 'src/data/queries';
import {
    CompanyBrandName,
    OrderDir,
    PublishingSongWriterCompositionsOrderByField,
    PublishingSongWriterOrderByField,
} from 'src/data/schemaTypes';
import PageControls from 'src/pages/songWriterListPage/pageControls';
import SongWriterTable from 'src/pages/songWriterListPage/songWriterTable';
import { useApplicationContext } from 'src/utils/applicationContext';
import { useIsEmployeeWithFullAccess } from 'src/utils/helpers';
import { useLocationSearchQuery, useLocationWithParams } from 'src/utils/route';
import { songWritersUrl } from 'src/utils/urls';
import { isValidIpi } from 'src/utils/validators';
import type { BrandOption } from 'src/components/brandSelector';
import type { SongWriterListSort } from 'src/data/queries';
import type { PublishingSongWritersFilter } from 'src/data/schemaTypes';
import type { Label, SongWriter } from 'src/types';

export const CLASSNAME = 'SongWriterListPage';
export const SONGWRITERS_PER_PAGE = 25;

// Mirrors the server-side default for `publishingSongWriters`.
export const DEFAULT_SONGWRITER_SORT: SongWriterListSort = {
    orderBy: PublishingSongWriterOrderByField.LEGAL_NAME,
    orderDir: OrderDir.ASC,
};

interface PublishingSongWritersFilterLabel
    extends Omit<PublishingSongWritersFilter, 'label'> {
    label: Label | null;
    legalNameSearch: string | null;
    legalNameAndIpiSearch: boolean | null;
}

const enum URLParams {
    BRAND = 'brand',
    WRITER = 'songwriter',
    IPI = 'ipi',
}

const extractValuesFromSearchQuery = (
    searchQuery: URLSearchParams,
    isEmployeeWithFullAccess: boolean = false
) => {
    const brandValue =
        isEmployeeWithFullAccess && searchQuery.get(URLParams.BRAND)
            ? decodeURIComponent(searchQuery.get(URLParams.BRAND)!)
            : undefined;

    const songwriter = decodeURIComponent(
        searchQuery.get(URLParams.WRITER) || ''
    );

    return {
        songwriter,
        brand: Object.values(CompanyBrandName).includes(
            brandValue as CompanyBrandName
        )
            ? (brandValue as CompanyBrandName)
            : undefined,
    };
};

const SongWriterListPage: FC<object> = () => {
    const history = useHistory();
    const toast = useToast();
    const identity = useIdentity();
    const isSortableListEnabled = Boolean(
        identity.features[FEATURE_FLAGS.ADMIN_UX_IMPROVEMENTS]
    );

    useEffect(() => {
        const { notification } =
            (history.location.state as unknown as { notification: string }) ??
            {};
        if (notification) toast(notification);
    }, [toast, history]);

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

    const { params } = useLocationWithParams();
    const searchQuery = useLocationSearchQuery();
    const isEmployeeWithFullAccess = useIsEmployeeWithFullAccess();

    const category = 'SongWriters';
    const page = parseInt(params[0] || '1', 10);
    const {
        brand: brandFromSearchQuery,
        songwriter: songwriterFromSearchQuery,
    } = extractValuesFromSearchQuery(searchQuery, isEmployeeWithFullAccess);

    const brand =
        brandFromSearchQuery || (pageFilters?.brand as CompanyBrandName);
    const songwriterSearch =
        songwriterFromSearchQuery ||
        pageFilters?.songWritersPageSongwriterSearch ||
        null;

    const deleteSongWriter = useDeleteSongWriter();
    const [filter, setFilter] = useState<PublishingSongWritersFilterLabel>({
        label: null,
        brand: brand,
        legalNameSearch: songwriterSearch,
        legalNameAndIpiSearch:
            songwriterSearch && !Number.isNaN(parseInt(songwriterSearch, 10))
                ? true
                : null,
    });
    const [selectedSongWriter, setSelectedSongWriter] =
        useState<SongWriter | null>(null);
    const [detailMode, setDetailMode] = useState<string>('');

    const [pageSize, setPageSize] = useState(SONGWRITERS_PER_PAGE);
    const handleChangedPageSize = (newPageSize: number) => {
        setPageSize(newPageSize);
    };

    const [sort, setSort] = useState<SongWriterListSort>(
        DEFAULT_SONGWRITER_SORT
    );

    // With the flag off there is no songwriter-list sort at all: the column
    // header is not interactive and the sort variables are left out of the
    // query, so the server sorts the list however it did before the feature.
    const songWriterSort = isSortableListEnabled ? sort : undefined;

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

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

    const {
        data: songWritersQueryData,
        loading,
        error,
    } = usePublishingSongWriters(
        filter,
        page,
        pageSize,
        // Sorts each songwriter's nested compositions. Pre-existing behaviour,
        // deliberately not behind the flag.
        PublishingSongWriterCompositionsOrderByField.TITLE,
        OrderDir.ASC,
        songWriterSort
    );

    const totalPages = Math.ceil(
        (songWritersQueryData?.totalCount || 0) / pageSize
    );
    useEffect(() => {
        history.push(songWritersUrl(page, filter, isEmployeeWithFullAccess));
    }, [history, page, filter, isEmployeeWithFullAccess]);

    const onChangeSearch = useCallback(
        (e: React.ChangeEvent<HTMLInputElement>) => {
            const inputValue = e.target.value;
            updatePageFilters({
                songWritersPageSongwriterSearch: isValidIpi(inputValue)
                    ? null
                    : inputValue,
            });

            setFilter(f => {
                const { legalNameSearch, ...newFilter } = f;

                // This is to allow users to filter by partial IPIs & writers with numbers in their name
                const isNumeric = !Number.isNaN(parseInt(inputValue, 10));
                return {
                    ...newFilter,
                    legalNameSearch: inputValue,

                    professionallyKnownAs: inputValue,
                    ...(isNumeric && { legalNameAndIpiSearch: true }),
                };
            });
        },
        [updatePageFilters]
    );

    const onBrandChange = useCallback(
        (option?: BrandOption) => {
            updatePageFilters({ brand: option?.value || null });
            setFilter(f => ({
                ...f,
                brand: option?.value as CompanyBrandName,
            }));
        },
        [updatePageFilters]
    );

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

    const onSortChange = useCallback(
        (orderBy: PublishingSongWriterOrderByField) => {
            setSort(current => ({
                orderBy,
                orderDir:
                    current.orderBy === orderBy &&
                    current.orderDir === OrderDir.ASC
                        ? OrderDir.DESC
                        : OrderDir.ASC,
            }));

            // The server re-sorts the whole result set before slicing the page,
            // so the current offset points at arbitrary rows once the sort
            // changes. Go back to the first page.
            history.push(songWritersUrl(1, filter, isEmployeeWithFullAccess));
        },
        [history, filter, isEmployeeWithFullAccess]
    );

    // Check if the page number is valid and redirect if necessary to the first page when the sonwriters list changes
    // If the page number is greater than the total number of pages, redirect to the first page
    useEffect(() => {
        if (!loading && songWritersQueryData?.totalCount !== undefined) {
            const newPageNumber =
                totalPages === 0 || page > totalPages ? 1 : page;
            if (newPageNumber !== page) {
                history.push(
                    songWritersUrl(
                        newPageNumber,
                        filter,
                        isEmployeeWithFullAccess
                    )
                );
            }
        }
    }, [
        loading,
        songWritersQueryData,
        page,
        history,
        totalPages,
        filter,
        isEmployeeWithFullAccess,
    ]);

    const onDeleteSongWriter = useCallback(
        ({ id, legalName }: SongWriter) => {
            void Segment.trackEvent('Click - Delete Song Writer', {
                category: 'Delete Songwriter',
                songWriterId: id,
                songWriterName: legalName,
            });
            void deleteSongWriter({ variables: { id } });
        },
        [deleteSongWriter]
    );

    const toNewSongWriter = useCallback(() => {
        void Segment.trackEvent('Click - New Writer', { category });

        setSelectedSongWriter(null);
        setDetailMode('new');
    }, []);

    const onModeReset = useCallback(() => {
        setSelectedSongWriter(null);
        setDetailMode('');
    }, []);

    if (!songWritersQueryData) return <LoadingIndicator />;

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

    const isNewSongWriterDisabled = !filter.label;
    const newSongWriterButton = (
        <Button
            disabled={isNewSongWriterDisabled}
            className="icon-left"
            variant="primary"
            onClick={toNewSongWriter}
            size="lg"
        >
            <GlyphIcon name="plus" size={12} />
            {formatMessage('songWriters.newWriter')}
        </Button>
    );
    return (
        <div className={CLASSNAME}>
            {detailMode === 'new' && (
                <RegisterNewSongwriter
                    isOpen
                    onClose={onModeReset}
                    headerLabel={formatMessage(
                        'songWriterManager.registerNewSongWriter'
                    )}
                />
            )}
            <ViewSongWriter
                isOpen={Boolean(selectedSongWriter) && detailMode === 'view'}
                songWriter={selectedSongWriter}
                onClose={onModeReset}
            />

            <div className="AppHeader">
                <HeaderLabelPicker />
                {isNewSongWriterDisabled ? (
                    <HelpTooltip
                        message={formatMessage(
                            'songWriterManager.newSongWriterDisabled'
                        )}
                        tooltipPlacement="left"
                        id="new-song-writer-tooltip"
                    >
                        {newSongWriterButton}
                    </HelpTooltip>
                ) : (
                    newSongWriterButton
                )}
            </div>

            <PageControls
                searchValue={songwriterSearch}
                onSearchChange={onChangeSearch}
                isEmployeeWithFullAccess={isEmployeeWithFullAccess}
                initialBrandValue={brand}
                onBrandChange={onBrandChange}
            />

            <div className="PageContent">
                <Card suite>
                    {!songWritersQueryData?.songWriters?.length ? (
                        <EmptyState isLoading={loading} page="songWriters" />
                    ) : (
                        <SongWriterTable
                            page={page - 1}
                            totalPages={totalPages}
                            totalCount={songWritersQueryData.totalCount || 0}
                            pageSize={pageSize}
                            songWriters={songWritersQueryData?.songWriters}
                            sort={songWriterSort}
                            onSortChange={
                                isSortableListEnabled ? onSortChange : undefined
                            }
                            onPaginationChange={onPaginationChange}
                            onSongWriterDelete={onDeleteSongWriter}
                            onSetPageSize={handleChangedPageSize}
                        />
                    )}
                </Card>
            </div>
        </div>
    );
};

export default SongWriterListPage;
