import React, { useEffect, useState } from 'react';
import { Button, useToast, LoadingSpinner } from '@theorchard/suite-components';
import { GlyphIcon } from '@theorchard/suite-icons';
import classNames from 'classnames';
import { isEmpty } from 'lodash-es';
import { Link, useParams } from 'react-router-dom';
import { AbacusContractPartyTargetType } from 'src/apollo/definitions/globalTypes';
import { useCreateParty } from 'src/apollo/mutations/contract-party';
import { useNRContributorSearch } from 'src/apollo/queries/nr-contributors';
import {
    CONTRIBUTORS_HEADERS,
    SEARCH_TEXT,
} from 'src/apollo/type-constants/contract';
import {
    sortByColumn,
    sortDirection,
} from 'src/apollo/type-constants/schedule';
import InfiniteScroll from 'src/components/shared/infinite-scroll';
import { SearchField } from 'src/components/shared/search-field';
import TableBasic from 'src/components/shared/table-basic';
import { NO_SEARCH_RESULTS } from 'src/constants';
import { getContributorDetail } from 'src/urls/frontend-royalties';
import { reorderSchedulesList } from 'src/utils/schedules';
import type { GetContractPartyListQuery } from 'src/apollo/queries/contract-party/__generated__/get-contract-party-list';
import type { GetNRContributorsListQuery } from 'src/apollo/queries/nr-contributors/__generated__/get-nr-contributors';

type AbacusContractPartyItem =
    GetContractPartyListQuery['abacusContractParties']['items'][0];

type NrContributors = NonNullable<GetNRContributorsListQuery['nrContributors']>;
type NRContributor = NonNullable<
    NrContributors['contributors'] extends (infer T)[] | null ? T : null
>;

export interface ContributorsSearchPropType {
    addedContractContributors: AbacusContractPartyItem[];
    addingContributorId?: string;
    isClearFilter: boolean;
    setAddingContributorId?: any;
    setIsClearFilter: any;
}

export const ContributorsSearch: React.FC<ContributorsSearchPropType> = ({
    addedContractContributors,
    addingContributorId,
    isClearFilter,
    setAddingContributorId,
    setIsClearFilter,
}) => {
    const itemsPerPage = 100;
    const { contractId } = useParams<{ contractId: string }>();
    const [filterValues, setFilterValues] = useState<any>({
        limit: itemsPerPage,
        offset: 0,
        orderBy: sortByColumn['name'],
        orderDir: sortDirection['asc'],
    });

    const [contributorsSearchResults, setContributorsSearchResults] = useState<
        any[]
    >([]);
    const [contributorsSearchList, setContributorsSearchList] = useState<any[]>(
        []
    );
    const [contributorsSearchCount, setContributorsSearchCount] =
        useState<number>(0);
    const [clearContributorSearchField, setClearContributorSearchField] =
        useState<boolean>(false);
    const addToast = useToast();

    const {
        data: nrContributorSearchResults,
        loading: nrContributorSearchLoading,
        getNRContributorSearchResult,
    } = useNRContributorSearch();

    const addContractParty = useCreateParty(
        contractId,
        AbacusContractPartyTargetType.CONTRIBUTOR
    );

    useEffect(() => {
        if (!filterValues?.term) {
            setContributorsSearchResults([]);
            setContributorsSearchList([]);
            setContributorsSearchCount(0);
        }

        if (filterValues?.term) {
            const variables = { ...filterValues };
            variables.orderBy = null;
            variables.orderDir = null;
            getNRContributorSearchResult(variables);
        }
    }, [filterValues]);

    const changeHandler = (value: string) =>
        setFilterValues({
            ...filterValues,
            term: value,
            limit: itemsPerPage,
            offset: 0,
        });

    const handleFetchMore = (limit: number, offset: number) =>
        setFilterValues({ ...filterValues, limit, offset });

    const clearAllFilters = () => {
        setClearContributorSearchField(!clearContributorSearchField);
        setContributorsSearchResults([]);
        setContributorsSearchList([]);
        setContributorsSearchCount(0);
        setFilterValues({
            ...filterValues,
            limit: itemsPerPage,
            offset: 0,
            term: '',
        });
        setIsClearFilter(false);
    };

    useEffect(() => {
        if (isClearFilter) clearAllFilters();
    }, [isClearFilter]);

    const formatSchedules = (abacusSchedules: any) =>
        !isEmpty(abacusSchedules) ? (
            <div>
                {abacusSchedules.map((s: any) => (
                    <div key={s.scheduleId}>
                        {`${s.scheduleName} - (${s.scheduleAttachments.totalCount})`}
                        {s.conditions?.autoAdd && (
                            <span className="AutoAdd">
                                <GlyphIcon name="check" size={12} />
                                <span className="AutoAdd-text">
                                    Auto add new
                                </span>
                            </span>
                        )}
                    </div>
                ))}
            </div>
        ) : (
            <div className="no-schedules">
                <i>No Schedules</i>
            </div>
        );

    const handleAddToContract = (
        contributorId: string,
        contributorName: string,
        index: number
    ) => {
        setAddingContributorId(contributorId);
        addContractParty({
            variables: {
                contractId,
                targetId: contributorId,
                targetType: AbacusContractPartyTargetType.CONTRIBUTOR,
            },
        })
            .then(() => {
                const rows = [...contributorsSearchResults];
                rows.splice(index, 1);
                setContributorsSearchResults(rows);

                setAddingContributorId('');
                addToast(
                    `Contributor "${contributorName}" successfully added to this contract.`
                );
            })
            .catch(() => {
                setAddingContributorId('');
                addToast(
                    `Error while adding contributor "${contributorName}".`,
                    { variant: 'danger' }
                );
            });
    };

    const formatAddToContractLink = (
        contributorId: string,
        contributorName: string,
        index: number
    ) => (
        <span
            onClick={() =>
                addingContributorId
                    ? false
                    : handleAddToContract(contributorId, contributorName, index)
            }
            className={classNames('addToContract-link', {
                disabled: addingContributorId === contributorId,
            })}
        >
            Add to this Contract
        </span>
    );

    const filterNotAddedContributors = (
        items: NRContributor[]
    ): NRContributor[] => {
        const addedIds: string[] = addedContractContributors.map(
            (item: AbacusContractPartyItem) => item.targetId
        );
        return items.filter(
            (item: NRContributor) => !addedIds.includes(item.id)
        );
    };

    const formatContributorsSearchList = (
        items: NRContributor[] | [] | null
    ) => {
        const contributors = items
            ? filterNotAddedContributors(items).map(
                  (contributor: NRContributor, index) => {
                      const { id, name, abacusSchedules } = contributor;
                      const contributorName = (
                          <div>
                              <Link key={id} to={getContributorDetail(id)}>
                                  {name}
                              </Link>
                              <br />
                              <span className="ContractContributors-ContributorLabel">
                                  {id}
                              </span>
                          </div>
                      );
                      const schedules = formatSchedules(
                          reorderSchedulesList(abacusSchedules)
                      );
                      const addToContractLink = formatAddToContractLink(
                          id,
                          name,
                          index
                      );
                      return {
                          id,
                          cols: [contributorName, schedules, addToContractLink],
                      };
                  }
              )
            : [];

        setContributorsSearchList(contributors);
        setContributorsSearchCount(contributors.length);
    };

    useEffect(() => {
        if (nrContributorSearchResults && filterValues?.term) {
            const { contributors }: any =
                nrContributorSearchResults.nrContributorSearch;
            if (filterValues.offset)
                setContributorsSearchResults([
                    ...contributorsSearchResults,
                    ...contributors,
                ]);
            else setContributorsSearchResults(contributors);
            setAddingContributorId('');
        }
    }, [nrContributorSearchResults, filterValues]);

    useEffect(() => {
        formatContributorsSearchList(contributorsSearchResults);
    }, [contributorsSearchResults, addingContributorId]);

    return (
        <div className="ContractContributors-search">
            <div
                className="ContractContributors-search-text"
                data-testid="searchText"
            >
                <i>{SEARCH_TEXT}</i>
            </div>
            <SearchField
                clearSearchField={clearContributorSearchField}
                placeholder="Search by Contributor Name or ID"
                onHandleChange={changeHandler}
                searchInputName="contributorName"
                states={filterValues}
                setStates={setFilterValues}
                validatePattern={null}
            />
            <div className="ClearFilters">
                {filterValues?.term && (
                    <Button
                        className="ClearFilterButton"
                        onClick={() => clearAllFilters()}
                        variant="quartenary"
                    >
                        Clear Filters
                    </Button>
                )}
            </div>
            <div className="ContractContributors-search-count">
                {filterValues?.term && (
                    <div>
                        <span className="SearchableList-result-count-label">
                            Results:
                        </span>
                        <span className="SearchableList-result-count-value">
                            {` ${contributorsSearchCount}`}
                        </span>
                    </div>
                )}
            </div>
            <div className="ContractContributors-search-table">
                {addingContributorId && <LoadingSpinner show />}
                <InfiniteScroll
                    fetchMore={handleFetchMore}
                    loading={
                        nrContributorSearchLoading &&
                        Boolean(filterValues?.offset)
                    }
                    count={contributorsSearchList?.length}
                    loadLimit={itemsPerPage}
                    totalLimit={contributorsSearchCount}
                    totalResults={contributorsSearchCount}
                    scrollContainerClassName="ContractContributors-search-table"
                    loaderSize={true}
                >
                    <TableBasic
                        headers={CONTRIBUTORS_HEADERS}
                        hover={true}
                        isLoading={
                            nrContributorSearchLoading && !filterValues?.offset
                        }
                        rows={contributorsSearchList}
                        tableClassName="ContractContributors-search-list"
                    />
                    {isEmpty(contributorsSearchList) &&
                        filterValues?.term &&
                        !nrContributorSearchLoading && (
                            <div className="ContractContributors-no-contributors">
                                <i>{NO_SEARCH_RESULTS}</i>
                            </div>
                        )}
                </InfiniteScroll>
            </div>
        </div>
    );
};

export default ContributorsSearch;
