import i18next, { Resource } from 'i18next';
import { Languages, LanguagesValues } from '../../business/constants/languages';
import { Modules } from '../../business/constants/modules';
import bgTranslations from './bg.json';
import daTranslations from './da.json';
import deTranslations from './de.json';
import enGbTranslations from './en-gb.json';
import enTranslations from './en.json';
import esTranslations from './es.json';
import fiTranslations from './fi.json';
import frTranslations from './fr.json';
import idTranslations from './id.json';
import isTranslations from './is.json';
import itTranslations from './it.json';
import nlTranslations from './nl.json';
import noTranslations from './no.json';
import plTranslations from './pl.json';
import ptBrTranslations from './pt-br.json';
import ptTranslations from './pt.json';
import roTranslations from './ro.json';
import ruTranslations from './ru.json';
import svTranslations from './sv.json';
import thTranslations from './th.json';
import ukTranslations from './uk.json';

type TranslationRequest = { [key: string]: I18NParams | undefined };
type TranslationResponse<T> = { [K in keyof T]: string };

export const CommonI18nBundle: Resource = {
    [Languages.BG]: { translation: bgTranslations },
    [Languages.DA]: { translation: daTranslations },
    [Languages.EN]: { translation: enTranslations },
    [Languages['EN-GB']]: { translation: enGbTranslations },
    [Languages.ES]: { translation: esTranslations },
    [Languages.ID]: { translation: idTranslations },
    [Languages.PT]: { translation: ptTranslations },
    [Languages['PT-BR']]: { translation: ptBrTranslations },
    [Languages.DE]: { translation: deTranslations },
    [Languages.FI]: { translation: fiTranslations },
    [Languages.FR]: { translation: frTranslations },
    [Languages.IT]: { translation: itTranslations },
    [Languages.NL]: { translation: nlTranslations },
    [Languages.NO]: { translation: noTranslations },
    [Languages.PL]: { translation: plTranslations },
    [Languages.IS]: { translation: isTranslations },
    [Languages.RO]: { translation: roTranslations },
    [Languages.RU]: { translation: ruTranslations },
    [Languages.SV]: { translation: svTranslations },
    [Languages.TH]: { translation: thTranslations },
    [Languages.UK]: { translation: ukTranslations },
};

export const consolidateResources = (bundles: Resource[]): Resource =>
    bundles.reduce((acc, i18nBundle) => {
        Object.keys(i18nBundle).forEach((language) => {
            if (!acc[language]) {
                acc[language] = {};
            }
            if (Object.keys(i18nBundle).some((module) => !!acc[language][module])) {
                throw new Error(`Duplicate translations for language ${language} ${Object.keys(i18nBundle).join(', ')}.`);
            }

            acc[language] = { ...acc[language], ...i18nBundle[language] };
        });
        return acc;
    }, {} as Resource);

export class I18n {
    static fallbackMap: Record<LanguagesValues, LanguagesValues> = {
        [Languages.BG]: Languages.BG,
        [Languages.DA]: Languages.DA,
        [Languages['EN-GB']]: Languages.EN,
        [Languages.ES]: Languages.ES,
        [Languages.ID]: Languages.ID,
        [Languages.PT]: Languages.PT,
        [Languages['PT-BR']]: Languages.PT,
        [Languages.DE]: Languages.DE,
        [Languages.EN]: Languages.EN,
        [Languages.FI]: Languages.FI,
        [Languages.IT]: Languages.IT,
        [Languages.NO]: Languages.NO,
        [Languages.FR]: Languages.FR,
        [Languages.NL]: Languages.NL,
        [Languages.PL]: Languages.PL,
        [Languages.IS]: Languages.IS,
        [Languages.RO]: Languages.RO,
        [Languages.RU]: Languages.RU,
        [Languages.SV]: Languages.SV,
        [Languages.TH]: Languages.TH,
        [Languages.UK]: Languages.UK,
    };

    static translate = (language: LanguagesValues, params?: I18NParams[]): Record<string, string> => {
        const fallbackLng = I18n.getFallbackLanguage(language);

        return (params || []).reduce(
            (previous, current) => {
                previous[current.key] = i18next.t(current.key, {
                    fallbackLng,
                    ...(current.params || {}),
                    lng: language,
                });
                return previous;
            },
            {} as Record<string, string>,
        );
    };

    static translateModule = (language: LanguagesValues, module: Modules, params?: I18NParams[]): Record<string, string> => {
        const fallbackLng = I18n.getFallbackLanguage(language);
        return (params || []).reduce(
            (previous, current) => {
                const translationParams = current.params || {};
                previous[current.key] = i18next.t(current.key, {
                    fallbackLng,
                    ...translationParams,
                    lng: language,
                    ns: module,
                });
                return previous;
            },
            {} as Record<string, string>,
        );
    };

    static getFallbackLanguage(language: LanguagesValues): LanguagesValues {
        return I18n.fallbackMap[language] || Languages.ES;
    }

    static getDateFormat(language?: LanguagesValues): string {
        const dateFormats: { [key in LanguagesValues]: string } = {
            bg: 'DD.MM.YYYY',
            da: 'DD.MM.YYYY',
            de: 'DD/MM/YYYY',
            en: 'MM/DD/YYYY',
            'en-gb': 'DD/MM/YYYY',
            es: 'DD/MM/YYYY',
            fi: 'DD.MM.YYYY',
            fr: 'DD/MM/YYYY',
            id: 'DD/MM/YYYY',
            is: 'DD/MM/YYYY',
            it: 'DD/MM/YYYY',
            nl: 'DD/MM/YYYY',
            no: 'DD.MM.YYYY',
            pl: 'DD/MM/YYYY',
            pt: 'DD/MM/YYYY',
            'pt-br': 'DD/MM/YYYY',
            ro: 'DD.MM.YYYY',
            ru: 'DD.MM.YYYY',
            sv: 'DD/MM/YYYY',
            th: 'DD/MM/YYYY',
            uk: 'DD/MM/YYYY',
        };

        return language ? dateFormats[language] || 'YYYY-MM-DD' : 'YYYY-MM-DD';
    }

    static getDateTimeFormat(language?: LanguagesValues): string {
        const dateFormats: { [key in LanguagesValues]: string } = {
            bg: 'DD.MM.YYYY HH:mm:ss ZZ',
            da: 'DD.MM.YYYY HH:mm:ss ZZ',
            de: 'DD/MM/YYYY HH:mm:ss ZZ',
            en: 'MM/DD/YYYY HH:mm:ss ZZ',
            'en-gb': 'DD/MM/YYYY HH:mm:ss ZZ',
            es: 'DD/MM/YYYY HH:mm:ss ZZ',
            fi: 'DD.MM.YYYY HH:mm:ss ZZ',
            fr: 'DD/MM/YYYY HH:mm:ss ZZ',
            id: 'DD/MM/YYYY HH:mm:ss ZZ',
            is: 'DD/MM/YYYY HH:mm:ss ZZ',
            it: 'DD/MM/YYYY HH:mm:ss ZZ',
            nl: 'DD/MM/YYYY HH:mm:ss ZZ',
            no: 'DD.MM.YYYY HH:mm:ss ZZ',
            pl: 'DD/MM/YYYY HH:mm:ss ZZ',
            pt: 'DD/MM/YYYY HH:mm:ss ZZ',
            'pt-br': 'DD/MM/YYYY HH:mm:ss ZZ',
            ro: 'DD.MM.YYYY HH:mm:ss ZZ',
            ru: 'DD.MM.YYYY HH:mm:ss ZZ',
            sv: 'DD/MM/YYYY HH:mm:ss ZZ',
            th: 'DD/MM/YYYY HH:mm:ss ZZ',
            uk: 'DD/MM/YYYY HH:mm:ss ZZ',
        };

        return language ? dateFormats[language] || 'YYYY-MM-DD' : 'YYYY-MM-DD';
    }

    static translateToAllLanguages<T extends TranslationRequest>(translationRequest: T): Record<LanguagesValues, TranslationResponse<T>> {
        const languages = Object.values(Languages);
        const translations: Record<LanguagesValues, TranslationResponse<T>> = {
            ...languages.reduce(
                (acc, language) => {
                    acc[language] = {} as TranslationResponse<T>;
                    return acc;
                },
                {} as Record<LanguagesValues, TranslationResponse<T>>,
            ),
        };
        languages.forEach((language) => {
            const params = Object.values(translationRequest)
                .filter((value) => value !== undefined)
                .map((value) => ({
                    key: value!.key,
                    params: value!.params,
                }));
            const translation = I18n.translate(language, params);
            Object.keys(translationRequest).forEach((key: keyof T) => {
                translations[language][key] = translationRequest[key] ? translation[translationRequest[key]!.key] : '';
            });
        });
        return translations;
    }
}

export type I18NParams = { key: string; params?: { [key: string]: string | undefined | { escapeValue: boolean } } | null };
