# coding=utf-8
"""
run the test from the src/invesytoolbox directory:
pytest ../tests/test_locales.py -v
"""

import datetime
import json
import sys
from pathlib import Path

import DateTime
from invesytoolbox.itb_locales import (
    fetch_all_countries,
    fetch_holidays,
    format_price,
    get_country,
    get_language_name,
    get_locale,
    is_holiday,
)
from testdata.locales import days_and_holidays, feiertage_2022_2023

TESTDATA = Path(__file__).parent / 'testdata'
with open(TESTDATA / 'locales_plain.json') as f:
    _data = json.load(f)

test_locales = [tuple(loc) for loc in _data['test_locales']]
locale_dicts = _data['locale_dicts']
# Convert locale lists back to tuples
for ld in locale_dicts:
    ld['locale'] = tuple(ld['locale'])

check_country = _data['check_country']
language_names = _data['language_names']
price_examples = {float(k) if '.' in k else int(k): v for k, v in _data['price_examples'].items()}


def test_fetch_all_countries():
    # list returning with default keys
    result = fetch_all_countries(
        keys=('alpha_2', 'name'),
        returning='list',
        language='en'
    )
    assert isinstance(result, list)
    assert len(result) > 200  # there are ~249 countries

    # tuple returning
    result = fetch_all_countries(
        keys=('alpha_2', 'name'),
        returning='tuple',
        language='en'
    )
    assert isinstance(result, tuple)
    assert len(result) > 200

    # dict returning
    result = fetch_all_countries(
        keys=('alpha_2', 'name'),
        returning='dict',
        language='en'
    )
    assert isinstance(result, dict)
    assert 'AT' in result

    # dictdict returning
    result = fetch_all_countries(
        keys=('alpha_2', 'name'),
        returning='dictdict',
        language='en'
    )
    assert isinstance(result, dict)
    assert 'AT' in result
    assert isinstance(result['AT'], dict)
    assert 'name' in result['AT']

    # German translation
    result = fetch_all_countries(
        keys=('alpha_2', 'name'),
        returning='dictdict',
        language='de'
    )
    assert result['AT']['name'] == 'Österreich'
    assert result['DE']['name'] == 'Deutschland'

    # single-key list: must be sorted alphabetically (via map_special_chars),
    # not by last character (which was the bug before the fix)
    result = fetch_all_countries(
        keys=('name',),
        returning='list',
        language='en'
    )
    from invesytoolbox.itb_text_name import map_special_chars
    assert result == sorted(result, key=lambda s: map_special_chars(s, sort=True))


def test_get_locale():
    for test_locale, locale_dict in zip(test_locales, locale_dicts):
        loc = get_locale(test_locale)
        assert isinstance(loc, dict), f'get_locale must return dict, got {type(loc).__name__}'
        if 'locale' not in loc:
            expected = {**locale_dict}
            expected.pop('locale', None)
            assert expected == loc
        else:
            assert locale_dict == loc


def test_get_country():
    country_info = get_country(
        country='at',
        language='de'
    )
    assert isinstance(country_info, dict), f'get_country must return dict, got {type(country_info).__name__}'
    assert check_country == country_info


def test_get_language_name():
    for lang_code, names in language_names.items():
        for code, name in names.items():
            lang_name = get_language_name(
                code=lang_code,
                language=code
            )
            assert name == lang_name


def test_fetch_holidays():
    holidays = fetch_holidays(
        country='AT',
        state=9,
        years=[2022, 2023, 2024]
    )
    # tests on gitlab will provide English names, so omit the test for now
    # assert holidays == feiertage_2022_2023

    holidays = fetch_holidays(
        country='AT',
        state=9,
        daterange=['1.1.2022', '31.12.2024']
    )
    # tests on gitlab will provide English names, so omit the test for now
    # assert holidays == feiertage_2022_2023


def test_fetch_holidays_length_returns_earliest():
    """Length limit should return the earliest holidays, not arbitrary ones."""
    all_holidays = fetch_holidays(
        country='AT',
        state=9,
        years=[2023],
        returning='days'
    )
    limited = fetch_holidays(
        country='AT',
        state=9,
        years=[2023],
        length=3,
        returning='days'
    )
    assert len(limited) == 3
    assert limited == sorted(all_holidays)[:3]


def test_is_holiday():
    for date, check in days_and_holidays.items():
        checked = is_holiday(
            datum=date,
            country='AT',
            state=9)

        assert checked == check


def test_format_price():
    loc = locale_dicts[0]
    for price, price_formatted in price_examples.items():
        for round_mode in ('round', 'ceil', 'floor'):
            price_str = format_price(
                price=price,
                loc=loc,
                round_mode=round_mode
            )

            assert price_str == price_formatted[round_mode]


def test_format_price_decimals():
    """Test that decimals parameter controls the number of output decimal places."""
    loc_at = get_locale(('de_AT', None))

    # decimals=2 (default) — whole number gets dash
    assert format_price(5.0, loc=loc_at) == '€ 5,-'
    assert format_price(5.99, loc=loc_at) == '€ 5,99'
    assert format_price(1.50, loc=loc_at) == '€ 1,50'

    # decimals=3 — three decimal places shown
    assert format_price(1.459, loc=loc_at, decimals=3) == '€ 1,459'
    assert format_price(1.450, loc=loc_at, decimals=3) == '€ 1,450'
    assert format_price(1.0, loc=loc_at, decimals=3) == '€ 1,-'

    # decimals=0 — no decimal places, no dash
    assert format_price(5.0, loc=loc_at, decimals=0) == '€ 5'
    assert format_price(5.49, loc=loc_at, decimals=0) == '€ 5'
    assert format_price(5.99, loc=loc_at, decimals=0) == '€ 6'

    # Large numbers: thousands separator must not be affected by dash replacement
    assert format_price(10000.0, loc=loc_at) == '€ 10.000,-'
    assert format_price(10000.0, loc=loc_at, decimals=3) == '€ 10.000,-'
    assert format_price(100000.99, loc=loc_at) == '€ 100.000,99'


def test_format_price_locale_currency_position():
    """Test that dash replacement works regardless of currency symbol position."""
    # en_US: currency symbol before number, no space
    loc_us = get_locale(('en_US', None))
    assert format_price(5.0, loc=loc_us) == '$5.-'
    assert format_price(5.99, loc=loc_us) == '$5.99'
    assert format_price(10000.0, loc=loc_us) == '$10,000.-'
    assert format_price(1.459, loc=loc_us, decimals=3) == '$1.459'
    assert format_price(1.0, loc=loc_us, decimals=3) == '$1.-'

    # fr_FR: currency symbol after number
    loc_fr = get_locale(('fr_FR', None))
    assert format_price(5.0, loc=loc_fr) == '5,- €'
    assert format_price(5.99, loc=loc_fr) == '5,99 €'
    assert format_price(1.0, loc=loc_fr, decimals=3) == '1,- €'
    assert format_price(1.459, loc=loc_fr, decimals=3) == '1,459 €'
