import type { FC } from 'react';
import { useState } from 'react';
import React, { useMemo, useEffect } from 'react';
import {
    Alert,
    LoadingIndicator,
    GridTable,
    Section,
    MarketSelector,
    Select,
} from '@theorchard/suite-components';
import { Segment } from '@theorchard/suite-frontend';
import {
    BLOCK_ACCESS_POLICY,
    CARVEOUT_POLICY,
    MONETIZE_POLICY,
    OSR_CATEGORY,
} from 'src/constants';
import { useTerritoriesQuery } from 'src/data/queries';
import RightsSummary from 'src/pages/osrPage/views/rightsView/rightsSummary';
import { paginate } from 'src/utils/pagination';
import { CountryCell, RuleCell, RuleType, ServiceCell } from './tableCells';
import type { CountryOption } from '@theorchard/suite-components';
import type { Rule } from 'src/data/queries/orchardSoundRecording/orchardSoundRecordingData';
import type { FormattedTrack } from 'src/data/queries/orchardSoundRecording/selector';
import type { Territory } from 'src/data/queries/territories/types';
import type { SortBy } from 'src/utils/types';

export const PARENT_CLASS_NAME = 'OsrPage';
export const CLASS_NAME = `${PARENT_CLASS_NAME}-Views-RightsView`;

interface Props {
    tracks: FormattedTrack[];
}

const DEFAULT_PAGE_SIZE = 50;

export enum RuleLevel {
    ACCOUNT = 'Account',
    SUBACCOUNT = 'Subaccount',
    TRACK = 'Track',
}

export interface ComposedRule {
    territoryCodeA2: string;
    countryName: string;
    accountName: string;
    accountId: number;
    isrc: string;
    level: RuleLevel;
    startDate: string | null;
    endDate: string | null;
    policy: string;
    service: string;
}

interface RuleFilter {
    value: string;
    label: string;
}

enum SortDirection {
    ASC = 'asc',
    DESC = 'desc',
}

const RULE_OPTIONS: RuleFilter[] = [
    {
        value: MONETIZE_POLICY,
        label: RuleType[MONETIZE_POLICY],
    },
    {
        value: BLOCK_ACCESS_POLICY,
        label: RuleType[BLOCK_ACCESS_POLICY],
    },
    {
        value: CARVEOUT_POLICY,
        label: RuleType[CARVEOUT_POLICY],
    },
];

const mapRulesPerCountry = (
    tracks: FormattedTrack[],
    territories: Territory[] | undefined
) => {
    if (territories) {
        const filter = ({ service, endDate }: Rule) => {
            if (service === 'meta') return false;
            return !endDate || new Date(endDate) >= new Date();
        };

        const processor = (
            rulesMap: Map<string, ComposedRule>,
            isrc: string,
            accountName: string,
            accountId: number,
            rule: Rule,
            level: RuleLevel
        ) => {
            // if it's '*' we need to 'spread' all the territories
            if (rule.service === '*') {
                // as of now we're supporting only TikTok,
                // but I'm making it as an array for the future purpose, to add new services easier
                ['tiktok'].forEach(service =>
                    processor(
                        rulesMap,
                        isrc,
                        accountName,
                        accountId,
                        {
                            ...rule,
                            service,
                        },
                        level
                    )
                );
                return;
            }

            const storeRuleToMap = (
                countryName: string,
                territoryCodeA2: string
            ) => {
                if (
                    !rulesMap.has(countryName) ||
                    // carveouts takes precedent in the scenario that we have a ‘clash’ in the same territory.
                    rule.policy === CARVEOUT_POLICY
                )
                    rulesMap.set(countryName, {
                        territoryCodeA2,
                        countryName,
                        accountName,
                        accountId,
                        isrc,
                        level,
                        ...rule,
                    });
            };

            // if it's GLOBAL we need to 'spread' all the territories
            if (rule.territory === '*') {
                territories.forEach((t: Territory) =>
                    storeRuleToMap(t.territoryName, t.territoryCodeA2)
                );
                return;
            }

            const foundCountry = territories.find(
                (t: Territory) => t.territoryCodeA2 === rule.territory
            );
            const countryName = foundCountry?.territoryName ?? rule.territory;
            storeRuleToMap(countryName, rule.territory);
        };

        const rulesMap: Map<string, ComposedRule> = new Map<
            string,
            ComposedRule
        >();

        tracks.forEach(track => {
            const { isrc, accountName, accountId } = track;

            // the order of rules processing matters
            // Track -> Subaccount (if available) -> Vendor for the rules to pick.
            track.rules
                ?.filter(filter)
                .forEach(rule =>
                    processor(
                        rulesMap,
                        isrc,
                        accountName,
                        accountId,
                        rule,
                        RuleLevel.TRACK
                    )
                );

            track.product.label.vendor?.rules
                ?.filter(filter)
                .forEach(rule =>
                    processor(
                        rulesMap,
                        isrc,
                        accountName,
                        accountId,
                        rule,
                        RuleLevel.ACCOUNT
                    )
                );

            track.product.label.rules
                ?.filter(filter)
                .forEach(rule =>
                    processor(
                        rulesMap,
                        isrc,
                        accountName,
                        accountId,
                        rule,
                        !track.product.label.vendor
                            ? RuleLevel.ACCOUNT
                            : RuleLevel.SUBACCOUNT
                    )
                );
        });

        return [...rulesMap.values()].sort((a, b) =>
            a.countryName.localeCompare(b.countryName)
        );
    }

    return [];
};

const RightsView: FC<Props> = ({ tracks }) => {
    const { loading, error, data } = useTerritoriesQuery();
    const [policyFilter, setPolicyFilter] = useState<RuleFilter | undefined>();
    const [countryFilter, setCountryFilter] = useState<string[]>();
    const [page, setPage] = useState(0);
    const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
    const [sortDirection, setSortDirection] = useState<SortDirection>(
        SortDirection.DESC
    );

    const dropdownCountries =
        data?.territories.map(country => ({
            value: country.territoryCodeA2,
            continent: country.continent,
        })) ?? [];

    useEffect(() => {
        Segment.trackEvent('View', { category: OSR_CATEGORY }, 'Rights').catch(
            console.error
        );
    }, []);

    const rules = useMemo<ComposedRule[]>(
        () => mapRulesPerCountry(tracks, data?.territories),
        [tracks, data?.territories]
    );
    const filteredRules = useMemo<ComposedRule[]>(() => {
        if (countryFilter?.length || policyFilter) {
            return rules.filter(rule => {
                const countryMatch =
                    !countryFilter?.length ||
                    countryFilter.includes(rule.territoryCodeA2);
                const policyMatch =
                    !policyFilter?.value || rule.policy === policyFilter.value;
                return countryMatch && policyMatch;
            });
        }
        return rules;
    }, [rules, countryFilter, policyFilter]);

    if (error) return <Alert variant="error" text={error.message} />;

    if (loading && !data) return <LoadingIndicator />;

    const paginatedRules = paginate(
        filteredRules.sort((a, b) =>
            sortDirection === SortDirection.ASC
                ? a.countryName.localeCompare(b.countryName)
                : b.countryName.localeCompare(a.countryName)
        ),
        pageSize
    );

    const handleCountryChange = (events: CountryOption[]) => {
        if (events.length === 0) return setCountryFilter([]);

        setCountryFilter(events.map(e => e.value));
    };

    const handleSort = (sortBy: SortBy[]) => {
        setSortDirection(sortBy[0].direction as SortDirection);
    };

    return (
        <div className={CLASS_NAME}>
            <p className={`${CLASS_NAME}-description`}>
                {$t(`orchardSoundRecordings.rightsView.description`)}
            </p>
            <div className={`${CLASS_NAME}-filters`}>
                <div className={`${CLASS_NAME}-filters-left`}>
                    <RightsSummary rules={rules} />
                </div>
                <div className={`${CLASS_NAME}-filters-right`}>
                    <MarketSelector
                        className={`${CLASS_NAME}-filters-dropdown-country`}
                        selectedValue={countryFilter}
                        onChange={handleCountryChange}
                        countries={dropdownCountries}
                        testId="dpd-country"
                        variant="compact"
                        placeholder="Country"
                        sectionGroupSelect
                    />
                    <Select
                        className={`${CLASS_NAME}-filters-dropdown-policy`}
                        selectedValue={policyFilter}
                        options={RULE_OPTIONS}
                        onChange={e => setPolicyFilter(e)}
                        testId="dpd-policy"
                        placeholder="Rule"
                        compact
                        hideFilter
                    />
                </div>
            </div>
            <Section>
                <Section.Body>
                    <Section.Table>
                        <GridTable
                            bordered
                            className={`${CLASS_NAME}-table`}
                            data={paginatedRules[page]}
                            variant="zebra"
                            totalCount={filteredRules.length}
                            paginated
                            page={page}
                            pageSize={pageSize}
                            onPageChange={setPage}
                            onPageSizeChange={setPageSize}
                            onSort={handleSort}
                            sortBy={{
                                key: 'countryName',
                                direction: sortDirection,
                            }}
                            columnDefs={[
                                {
                                    name: 'countryName',
                                    title: $t(
                                        `orchardSoundRecordings.rightsView.table.headers.countryName`
                                    ),
                                    className: `${CLASS_NAME}-table-column-country-name`,
                                    sortable: true,
                                    Cell: CountryCell,
                                    minWidth: '30%',
                                },
                                {
                                    name: 'service',
                                    title: $t(
                                        `orchardSoundRecordings.rightsView.table.headers.service`
                                    ),
                                    className: `${CLASS_NAME}-table-column-service`,
                                    Cell: ServiceCell,
                                },
                                {
                                    name: 'rule',
                                    title: $t(
                                        `orchardSoundRecordings.rightsView.table.headers.rule`
                                    ),
                                    className: `${CLASS_NAME}-table-column-rule`,
                                    Cell: RuleCell,
                                },
                            ]}
                        ></GridTable>
                    </Section.Table>
                </Section.Body>
            </Section>
        </div>
    );
};

export default RightsView;
