/*
 * Copyright (C) 2026 Fluxer Contributors
 *
 * This file is part of Fluxer.
 *
 * Fluxer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fluxer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
 */

import {AllLocales, type LocaleCode, Locales} from '@fluxer/constants/src/Locales';
import {
	getFlagCode,
	getLocaleCode,
	getLocaleFromCode,
	getLocaleMetadata,
	getLocaleName,
	parseAcceptLanguage,
} from '@fluxer/locale/src/LocaleService';
import {describe, expect, it} from 'vitest';

interface LocaleLookupCase {
	input: string;
	expected: LocaleCode | null;
}

interface AcceptLanguageCase {
	header: string | null | undefined;
	expected: LocaleCode;
}

const localeNameCases: Array<{locale: LocaleCode; expected: string}> = [
	{locale: Locales.EN_US, expected: 'English (US)'},
	{locale: Locales.EN_GB, expected: 'English'},
	{locale: Locales.DE, expected: 'Deutsch'},
	{locale: Locales.JA, expected: '日本語'},
	{locale: Locales.ZH_CN, expected: '简体中文'},
	{locale: Locales.ZH_TW, expected: '繁體中文'},
	{locale: Locales.ES_419, expected: 'Español (Latinoamérica)'},
	{locale: Locales.PT_BR, expected: 'Português (Brasil)'},
];

const flagCodeCases: Array<{locale: LocaleCode; expected: string}> = [
	{locale: Locales.EN_US, expected: '1f1fa-1f1f8'},
	{locale: Locales.EN_GB, expected: '1f1ec-1f1e7'},
	{locale: Locales.DE, expected: '1f1e9-1f1ea'},
	{locale: Locales.JA, expected: '1f1ef-1f1f5'},
	{locale: Locales.ZH_CN, expected: '1f1e8-1f1f3'},
	{locale: Locales.ES_419, expected: '1f30e'},
	{locale: Locales.PT_BR, expected: '1f1e7-1f1f7'},
];

const localeLookupCases: Array<LocaleLookupCase> = [
	{input: 'en', expected: Locales.EN_US},
	{input: 'en-US', expected: Locales.EN_US},
	{input: 'en-us', expected: Locales.EN_US},
	{input: 'en_GB', expected: Locales.EN_GB},
	{input: 'en-gb', expected: Locales.EN_GB},
	{input: 'de', expected: Locales.DE},
	{input: 'ja', expected: Locales.JA},
	{input: 'zh-CN', expected: Locales.ZH_CN},
	{input: 'zh-cn', expected: Locales.ZH_CN},
	{input: 'zh-TW', expected: Locales.ZH_TW},
	{input: 'zh-tw', expected: Locales.ZH_TW},
	{input: 'es-419', expected: Locales.ES_419},
	{input: 'pt-br', expected: Locales.PT_BR},
	{input: 'sv', expected: Locales.SV_SE},
	{input: 'sv-se', expected: Locales.SV_SE},
	{input: 'xx', expected: null},
	{input: '', expected: null},
	{input: 'en-AU', expected: null},
];

const localeCodeCases: Array<{locale: LocaleCode; expected: string}> = [
	{locale: Locales.EN_US, expected: 'en'},
	{locale: Locales.EN_GB, expected: 'en'},
	{locale: Locales.DE, expected: 'de'},
	{locale: Locales.ZH_CN, expected: 'zh'},
	{locale: Locales.ZH_TW, expected: 'zh'},
	{locale: Locales.ES_ES, expected: 'es'},
	{locale: Locales.ES_419, expected: 'es'},
	{locale: Locales.PT_BR, expected: 'pt'},
	{locale: Locales.SV_SE, expected: 'sv'},
	{locale: Locales.JA, expected: 'ja'},
];

const acceptLanguageCases: Array<AcceptLanguageCase> = [
	{header: null, expected: Locales.EN_US},
	{header: undefined, expected: Locales.EN_US},
	{header: '', expected: Locales.EN_US},
	{header: 'de', expected: Locales.DE},
	{header: 'en-GB', expected: Locales.EN_GB},
	{header: 'en-gb', expected: Locales.EN_GB},
	{header: 'de;q=0.9, fr;q=1.0', expected: Locales.FR},
	{header: 'en-US,en;q=0.9,de;q=0.8', expected: Locales.EN_US},
	{header: 'en-AU', expected: Locales.EN_US},
	{header: 'es', expected: Locales.ES_ES},
	{header: 'pt', expected: Locales.PT_BR},
	{header: 'zh', expected: Locales.ZH_CN},
	{header: 'sv', expected: Locales.SV_SE},
	{header: 'de;q=0.5, ja;q=0.9, fr;q=0.7', expected: Locales.JA},
	{header: 'de, fr;q=0.5', expected: Locales.DE},
	{header: 'xx-YY', expected: Locales.EN_US},
	{header: 'fr-CA', expected: Locales.EN_US},
	{header: '  de  ,  fr  ', expected: Locales.DE},
	{header: 'zh-TW', expected: Locales.ZH_TW},
	{header: 'zh-CN', expected: Locales.ZH_CN},
	{header: 'es-419', expected: Locales.ES_419},
	{header: 'pt-BR', expected: Locales.PT_BR},
	{header: 'sv-SE', expected: Locales.SV_SE},
	{header: 'xx-YY, zz-AA, qq-BB', expected: Locales.EN_US},
	{header: 'zh-TW, zh;q=0.9', expected: Locales.ZH_TW},
	{header: 'en-US,en;q=0.9,ja;q=0.8,de;q=0.7,fr;q=0.6', expected: Locales.EN_US},
	{header: 'de;q=0.999, fr;q=0.998', expected: Locales.DE},
	{header: 'de;q=0, fr;q=1', expected: Locales.FR},
];

describe('LocaleService', () => {
	describe('getLocaleMetadata', () => {
		it('returns consolidated metadata for a locale', () => {
			expect(getLocaleMetadata(Locales.EN_US)).toEqual({
				code: Locales.EN_US,
				languageCode: 'en',
				name: 'English (US)',
				flagCode: '1f1fa-1f1f8',
			});
		});

		it('returns metadata for every supported locale', () => {
			for (const locale of AllLocales) {
				const metadata = getLocaleMetadata(locale);
				expect(metadata.code).toBe(locale);
				expect(metadata.name.length).toBeGreaterThan(0);
				expect(metadata.flagCode.length).toBeGreaterThan(0);
			}
		});
	});

	describe('getLocaleName', () => {
		for (const {locale, expected} of localeNameCases) {
			it(`returns ${expected} for ${locale}`, () => {
				expect(getLocaleName(locale)).toBe(expected);
			});
		}
	});

	describe('getFlagCode', () => {
		for (const {locale, expected} of flagCodeCases) {
			it(`returns ${expected} for ${locale}`, () => {
				expect(getFlagCode(locale)).toBe(expected);
			});
		}
	});

	describe('getLocaleFromCode', () => {
		for (const {input, expected} of localeLookupCases) {
			it(`resolves ${input || 'empty'} to ${expected ?? 'null'}`, () => {
				expect(getLocaleFromCode(input)).toBe(expected);
			});
		}

		it('resolves canonical locale codes for all supported locales', () => {
			for (const locale of AllLocales) {
				expect(getLocaleFromCode(locale)).toBe(locale);
			}
		});
	});

	describe('getLocaleCode', () => {
		for (const {locale, expected} of localeCodeCases) {
			it(`returns ${expected} for ${locale}`, () => {
				expect(getLocaleCode(locale)).toBe(expected);
			});
		}
	});

	describe('parseAcceptLanguage', () => {
		for (const {header, expected} of acceptLanguageCases) {
			it(`selects ${expected} for ${header ?? 'nullish header'}`, () => {
				expect(parseAcceptLanguage(header)).toBe(expected);
			});
		}
	});
});
