import React from 'react';
import { CountryFlag } from '@theorchard/suite-icons';
import { getContinents, getCountries, getCountry } from '../../utils/countries';
import type { CountryGroup, CountryInfo, CountryOption, CountrySelection } from './types';
import type { Country } from '@theorchard/countries';

const createCountryOption = (country: Country): CountryOption => ({
    ...country,
    label: country.label,
    icon: <CountryFlag countryCode={country.value} />,
    keywords: [{ value: country.name, match: 'includes' }, country.alpha3],
});

const createCountryOptionFromInfo = (info: CountryInfo): CountryOption => {
    const country = getCountry(info.value);
    const base = createCountryOption({
        ...country,
        ...info,
        value: country.value ?? info.value,
    });

    return {
        ...base,
        ...(info.icon !== undefined && { icon: info.icon }),
        ...(info.keywords !== undefined && { keywords: info.keywords }),
    };
};

export const groupByContinent = (countries: CountryInfo[] | string[]): CountryGroup[] => {
    const grouped = countries.reduce(
        (result, countryOrCode) => {
            const country =
                typeof countryOrCode === 'string' ? { value: countryOrCode } : countryOrCode;

            const option = createCountryOptionFromInfo(country);
            const continent = option.continent;

            if (!continent) return result;

            const group = result[continent] ?? {
                key: continent,
                label: continent,
                options: [],
            };

            group.options.push(option);

            return {
                ...result,
                [continent]: group,
            };
        },
        {} as Record<string, CountryGroup>
    );

    return Object.values(grouped);
};

export const createGroupedCountryOptions = (): CountryGroup[] => {
    const continents = getContinents();

    return continents.map((continent) => ({
        key: continent.key,
        label: continent.label,
        options: continent.countries.map(createCountryOption),
    }));
};

export const createFlatCountryOptions = (includeGlobal?: boolean): CountryOption[] => {
    const countries = getCountries({ includeGlobal });

    return countries.map(createCountryOption);
};

/**
 * Builds grouped selection options from country codes, then merges in any custom
 * props (disabled, indicator, indicatorTooltip, etc.) from the provided flat
 * options list.
 */
export const resolveCountrySelections = (
    selections: CountrySelection[],
    flatOptions: CountryOption[]
): CountryGroup[] => {
    const groups = createCountrySelections(selections);
    // Index by value for O(1) lookup when merging props into each group's options
    const byValue = new Map(flatOptions.map((option) => [option.value, option]));

    return groups.map((group) => ({
        ...group,
        options: group.options.map((option) => byValue.get(option.value) ?? option),
    }));
};

export const createCountrySelections = (countries: CountrySelection[]): CountryGroup[] => {
    const selectGroups = countries.map((group) => {
        const options = group.countryCodes.map((code) => createCountryOption(getCountry(code)));

        const label =
            group.label ??
            `${options[0]?.label}${options.length > 1 ? ` +${options.length - 1}` : ''}`;

        return {
            key: group.label,
            ...group,
            label,
            options,
        };
    });

    return selectGroups;
};

export const createCountryOptionFromCode = (countryCode: string): CountryOption => {
    const country = getCountry(countryCode);
    return createCountryOption(country);
};

export const createCountryOptionsFromCode = (countryCodes: string[]): CountryOption[] => {
    return countryCodes.map(createCountryOptionFromCode);
};

export const createCountryOptions = (countries: CountryInfo[] | string[]): CountryOption[] => {
    return countries.map((country) =>
        createCountryOptionFromInfo(typeof country === 'string' ? { value: country } : country)
    );
};
