import sys
import os
import time
from datetime import datetime, timedelta
import calendar

# Přidání adresáře src do sys.path, aby bylo možné importovat moduly
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))

import database
import holidays
import data_fetcher
import rate_finder

# Global debug flag
DEBUG = False


def debug_print(*args, **kwargs):
    """Print debug messages only if debug mode is enabled."""
    if DEBUG:
        print(*args, **kwargs)


def set_debug_mode(debug):
    """Set the debug mode for this module."""
    global DEBUG
    DEBUG = debug


def get_czech_day_name(date_str):
    """
    Vrátí český název dne v týdnu pro zadané datum.

    :param date_str: Datum ve formátu DD.MM.YYYY
    :return: Český název dne v týdnu
    """
    try:
        date_obj = datetime.strptime(date_str, "%d.%m.%Y")
        # Czech day names
        czech_days = [
            "pondělí",
            "úterý",
            "středa",
            "čtvrtek",
            "pátek",
            "sobota",
            "neděle",
        ]
        return czech_days[date_obj.weekday()]
    except ValueError:
        return "neznámý den"


def is_holiday(date_str):
    """
    Zkontroluje, zda je zadané datum státní svátek.

    :param date_str: Datum ve formátu DD.MM.YYYY
    :return: True pokud je svátek, jinak False
    """
    return holidays.is_holiday(date_str)


def get_rate_for_date_with_fallback(date_str, currency_code):
    """
    Vyhledá kurz pro zadané datum a měnu. Pokud kurz pro dané datum neexistuje,
    aplikuje pravidla pro víkendy a státní svátky podle ČNB.
    Tato funkce je upravená verze z rate_finder.py bez kontrol času.

    :param date_str: Datum ve formátu DD.MM.YYYY.
    :param currency_code: Kód měny (např. USD).
    :return: Kurz jako desetinné číslo, nebo None pokud není nalezen.
    """
    try:
        requested_date = datetime.strptime(date_str, "%d.%m.%Y")
    except ValueError:
        return None

    # 1. Zkusíme najít kurz pro přesné datum
    rate = database.get_rate(date_str, currency_code)
    if rate is not None:
        return rate

    # 2. Pokud kurz neexistuje, aplikujeme pravidla podle ČNB:
    # "Kurzy devizového trhu jsou vyhlašovány pro běžně obchodované měny,
    # a to každý pracovní den po 14.30 s platností pro aktuální pracovní den
    # a pro případnou následující sobotu, neděli či státní svátek"
    #
    # To znamená:
    # - Pro víkendy a svátky hledáme kurz zpět v čase
    # - Pro běžné dny, které nemají kurz, hledáme kurz z posledního pracovního dne před nimi

    # Zkontrolujeme, zda je datum víkend nebo svátek
    if holidays.is_weekend(date_str) or holidays.is_holiday(date_str):
        # Pro víkendy a svátky hledáme kurz zpět v čase
        # podle pravidel ČNB by měl platit kurz z posledního dne před nimi,
        # který měl být vyhlášen s platností pro tento víkend/svátek.
        current_date = requested_date
        # Odečítáme dny, dokud nenajdeme den s kurzem
        # Omezení na 10 dní zpět
        for _ in range(10):
            current_date -= timedelta(days=1)
            check_date_str = current_date.strftime("%d.%m.%Y")
            rate = database.get_rate(check_date_str, currency_code)
            if rate is not None:
                # Našli jsme kurz, použijeme ho
                # (předpokládáme, že podle pravidel ČNB platí i pro následující víkend/svátek)
                return rate
        return None
    else:
        # Pro běžné dny, které nemají kurz, hledáme kurz z posledního pracovního dne před nimi
        current_date = requested_date
        # Odečítáme dny, dokud nenajdeme pracovní den s kurzem
        # Omezení na 10 dní zpět
        for _ in range(10):
            current_date -= timedelta(days=1)
            check_date_str = current_date.strftime("%d.%m.%Y")

            # Zkontrolujeme, zda je to pracovní den
            if not holidays.is_weekend(check_date_str) and not holidays.is_holiday(
                check_date_str
            ):
                rate = database.get_rate(check_date_str, currency_code)
                if rate is not None:
                    return rate

        return None


def get_missing_months_for_tax_calculation(year, currency_code):
    """
    Vrátí seznam měsíců, pro které chybí kurzy k posledním dnům pro výpočet 'Jednotného kurzu'.
    Zahrnuje pouze měsíce, jejichž poslední den je v minulosti (lze stáhnout).

    :param year: Rok k ověření
    :param currency_code: Kód měny
    :return: Seznam měsíců (1-12) s chybějícími kurzy
    """
    import calendar

    missing_months = []

    for month in range(1, 13):
        # Získáme poslední den měsíce
        last_day = calendar.monthrange(year, month)[1]
        date_str = f"{last_day:02d}.{month:02d}.{year}"

        # Pokud je datum v budoucnosti, přeskočíme (nelze stáhnout)
        date_obj = datetime.strptime(date_str, "%d.%m.%Y")
        current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
        if date_obj > current_date:
            continue

        # Zkusíme najít kurz pro dané datum
        rate = database.get_rate(date_str, currency_code)
        if rate is None:
            # Zkusíme dopočítat
            calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code)
            if calculated_rate is None:
                missing_months.append(month)

    return missing_months


def _is_year_complete_for_tax_calculation(year):
    """
    Zkontroluje, zda je rok kompletní pro výpočet 'Jednotného kurzu' podle metodiky ČNB.
    Rok je kompletní, pokud máme kurzy pro poslední den každého měsíce.

    :param year: Rok k ověření
    :return: True pokud je rok kompletní, jinak False
    """
    import calendar

    current_year = datetime.now().year
    current_month = datetime.now().month
    current_day = datetime.now().day

    # Pokud je rok v budoucnosti, není kompletní
    if year > current_year:
        return False

    # Pro všechny roky (včetně minulých) zkontrolujeme, zda máme data pro všechny měsíce
    for month in range(1, 13):
        # Získáme poslední den měsíce
        last_day = calendar.monthrange(year, month)[1]
        date_str = f"{last_day:02d}.{month:02d}.{year}"

        # Pokud je datum v budoucnosti, nemůže být kompletní
        date_obj = datetime.strptime(date_str, "%d.%m.%Y")
        current_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
        if date_obj > current_date:
            return False

        # Zkusíme najít kurz pro dané datum
        rate = database.get_rate(date_str, "USD")  # Testujeme na USD jako ukázku
        if rate is None:
            # Zkusíme dopočítat
            calculated_rate = get_rate_for_date_with_fallback(date_str, "USD")
            if calculated_rate is None:
                return False

    return True

    # Pro aktuální rok zkontrolujeme, zda máme data pro všechny měsíce
    for month in range(1, 13):
        # Získáme poslední den měsíce
        last_day = calendar.monthrange(year, month)[1]
        date_str = f"{last_day:02d}.{month:02d}.{year}"

        # Pokud je měsíc v budoucnosti, nemůže být kompletní
        if year == current_year and month > current_month:
            return False

        # Zkusíme najít kurz pro dané datum
        rate = database.get_rate(date_str, "USD")  # Testujeme na USD jako ukázku
        if rate is None:
            # Zkusíme dopočítat
            calculated_rate = get_rate_for_date_with_fallback(date_str, "USD")
            if calculated_rate is None:
                return False

    return True


def _auto_download_missing_monthly_data(year, currency_code, output_dir="data"):
    """
    Automatically download missing monthly data for tax calculation (silent operation).

    :param year: Year to check
    :param currency_code: Currency code
    :param output_dir: Output directory
    """
    missing_months = get_missing_months_for_tax_calculation(year, currency_code)
    if missing_months:
        debug_print(
            f"Auto-downloading missing monthly data for {currency_code} {year}: months {', '.join(f'{m:02d}' for m in missing_months)}"
        )
        for month in missing_months:
            start_date = f"01.{month:02d}.{year}"
            last_day = calendar.monthrange(year, month)[1]
            end_date = f"{last_day:02d}.{month:02d}.{year}"
            try:
                data_fetcher.download_monthly_data(
                    currency_code, start_date, end_date, output_dir=output_dir
                )
                # Small delay to be respectful to the API
                time.sleep(0.5)
            except Exception as e:
                debug_print(
                    f"Failed to download data for {currency_code} {month:02d}/{year}: {e}"
                )


def calculate_tax_yearly_average(year, currency_code, output_dir="data"):
    """
    Vypočítá 'Jednotný kurz' pro daňové účely podle metodiky ČNB.
    Jedná se o aritmetický průměr kurzů k posledním dnům každého kalendářního měsíce v roce.

    :param year: Rok
    :param currency_code: Kód měny (např. USD)
    :param output_dir: Adresář s daty
    :return: 'Jednotný kurz' jako desetinné číslo, nebo None pokud není k dispozici
    """
    debug_print(
        f"Vypočítávám 'Jednotný kurz' pro daňové účely podle metodiky ČNB pro {currency_code} za rok {year}..."
    )

    # Auto-download missing monthly data if needed (silent operation)
    _auto_download_missing_monthly_data(year, currency_code, output_dir)

    # Zkontrolujeme, zda databáze obsahuje data pro daný rok
    if not rate_finder.check_year_data_in_db(year):
        debug_print(f"Databáze neobsahuje data pro rok {year}. Stahuji roční data...")
        # Stáhneme roční data s vynuceným stažením
        os.makedirs("data", exist_ok=True)
        data_fetcher.download_yearly_data(year, output_dir="data", force=True)

    # Získáme seznam posledních dní všech měsíců v roce
    monthly_rates = []
    monthly_dates = []

    debug_print(f"Získávám kurzy k posledním dnům všech měsíců v roce {year}...")

    for month in range(1, 13):
        # Získáme poslední den měsíce
        last_day = calendar.monthrange(year, month)[1]
        date_str = f"{last_day:02d}.{month:02d}.{year}"

        # Získáme kurz pro dané datum
        rate = database.get_rate(date_str, currency_code)
        if rate is not None:
            monthly_rates.append(rate)
            monthly_dates.append(date_str)
            debug_print(f"Měsíc {month:02d}: {date_str} = {rate}")
        else:
            # Kurz nebyl nalezen, zkusíme dopočítat
            calculated_rate = get_rate_for_date_with_fallback(date_str, currency_code)
            if calculated_rate is not None:
                monthly_rates.append(calculated_rate)
                monthly_dates.append(date_str)
                debug_print(
                    f"Měsíc {month:02d}: {date_str} = {calculated_rate} (dopočítaný kurz)"
                )
            else:
                debug_print(f"Měsíc {month:02d}: {date_str} = kurz nenalezen")

    debug_print(f"Počet měsíců s kurzy: {len(monthly_rates)}/12")

    # Musíme mít kurzy pro všech 12 měsíců
    if len(monthly_rates) != 12:
        debug_print(
            f"Varování: Nenalezeny kurzy pro všech 12 měsíců ({len(monthly_rates)}/12)"
        )
        return None

    # Výpočet aritmetického průměru
    average = sum(monthly_rates) / len(monthly_rates)
    debug_print(f"Součet kurzů: {sum(monthly_rates):.6f}")
    debug_print(f"Počet měsíců: {len(monthly_rates)}")
    debug_print(f"'Jednotný kurz' pro daňové účely: {average:.6f}")

    return average


def generate_yearly_report(year, currency_code, output_dir="data"):
    """
    Vygeneruje report kurzů pro zadaný rok a měnu.

    :param year: Rok (např. 2020).
    :param currency_code: Kód měny (např. USD).
    :param output_dir: Adresář, kam se má CSV soubor s reportem uložit.
    :return: Cesta k vytvořenému CSV souboru.
    """
    debug_print(f"Generuji report kurzů pro {currency_code} za rok {year}...")

    # Zkontrolujeme, zda databáze obsahuje data pro daný rok
    # Pro generování reportu vždy stáhneme aktuální roční data, aby byl report kompletní
    debug_print(f"Stahuji roční data pro rok {year}...")
    # Stáhneme roční data s vynuceným stažením
    os.makedirs("data", exist_ok=True)
    data_fetcher.download_yearly_data(year, output_dir="data", force=True)

    # Vytvoříme seznam všech dní v roce
    start_date = datetime(year, 1, 1)
    end_date = datetime(year, 12, 31)

    # Ale nebudeme generovat report pro budoucí datumy
    today = datetime.now()
    # Pokud je požadovaný rok v budoucnosti, nepokračujeme
    if year > today.year:
        debug_print(f"Chyba: Nelze generovat report pro budoucí rok {year}.")
        return None

    # Pokud je požadovaný rok aktuální rok, omezíme konec na dnešní datum
    if year == today.year:
        end_date = today

    # Otevřeme CSV soubor pro zápis
    filename = f"report_{currency_code}_{year}.csv"
    filepath = os.path.join(output_dir, filename)

    try:
        with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
            # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
            csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")

            current_date = start_date
            while current_date <= end_date:
                date_str = current_date.strftime("%d.%m.%Y")

                # Získáme den v týdnu
                day_name = get_czech_day_name(date_str)

                # Zkontrolujeme, zda je svátek
                is_holiday_flag = is_holiday(date_str)
                holiday_text = "ano" if is_holiday_flag else "ne"

                # Získáme kurz pro dané datum
                rate = database.get_rate(date_str, currency_code)
                note = ""

                if rate is not None:
                    # Kurz byl nalezen v databázi
                    pass
                else:
                    # Kurz nebyl nalezen, zkusíme dopočítat
                    calculated_rate = get_rate_for_date_with_fallback(
                        date_str, currency_code
                    )
                    if calculated_rate is not None:
                        rate = calculated_rate
                        note = "Dopočítaný kurz"
                        # Pokud je kurz dopočítaný a je to svátek, přidáme informaci
                        if is_holiday_flag:
                            note += " (svátek)"
                    else:
                        note = "Kurz není k dispozici"

                # Zapišeme řádek do CSV
                csvfile.write(
                    f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
                )

                # Přejdeme na další den
                current_date += timedelta(days=1)

        debug_print(f"Report byl úspěšně uložen do: {filepath}")
        return filepath
    except IOError as e:
        debug_print(f"Chyba při zápisu do souboru: {e}")
        return None


def generate_monthly_report(year, month, currency_code, output_dir="data"):
    """
    Vygeneruje report kurzů pro zadaný měsíc, rok a měnu.

    :param year: Rok (např. 2020).
    :param month: Měsíc (1-12).
    :param currency_code: Kód měny (např. USD).
    :param output_dir: Adresář, kam se má CSV soubor s reportem uložit.
    :return: Chta k vytvořenému CSV souboru.
    """
    debug_print(f"Generuji report kurzů pro {currency_code} za {month}/{year}...")

    # Zkontrolujeme, zda databáze obsahuje data pro daný rok
    # Pro generování reportu vždy stáhneme aktuální roční data, aby byl report kompletní
    debug_print(f"Stahuji roční data pro rok {year}...")
    # Stáhneme roční data s vynuceným stažením
    os.makedirs("data", exist_ok=True)
    data_fetcher.download_yearly_data(year, output_dir="data", force=True)

    # Vytvoříme seznam všech dní v měsíci
    start_date = datetime(year, month, 1)
    # Poslední den měsíce
    last_day = calendar.monthrange(year, month)[1]
    end_date = datetime(year, month, last_day)

    # Ale nebudeme generovat report pro budoucí datumy
    today = datetime.now()
    # Pokud je požadovaný měsíc v budoucnosti, nepokračujeme
    if year > today.year or (year == today.year and month > today.month):
        debug_print(f"Chyba: Nelze generovat report pro budoucí měsíc {month}/{year}.")
        return None

    # Pokud je požadovaný měsíc aktuální měsíc, omezíme konec na dnešní datum
    if year == today.year and month == today.month:
        end_date = today

    # Otevřeme CSV soubor pro zápis
    filename = f"report_{currency_code}_{year}_{month:02d}.csv"
    filepath = os.path.join(output_dir, filename)

    try:
        with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
            # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
            csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")

            current_date = start_date
            while current_date <= end_date:
                date_str = current_date.strftime("%d.%m.%Y")

                # Získáme den v týdnu
                day_name = get_czech_day_name(date_str)

                # Zkontrolujeme, zda je svátek
                is_holiday_flag = is_holiday(date_str)
                holiday_text = "ano" if is_holiday_flag else "ne"

                # Získáme kurz pro dané datum
                rate = database.get_rate(date_str, currency_code)
                note = ""

                if rate is not None:
                    # Kurz byl nalezen v databázi
                    pass
                else:
                    # Kurz nebyl nalezen, zkusíme dopočítat
                    calculated_rate = get_rate_for_date_with_fallback(
                        date_str, currency_code
                    )
                    if calculated_rate is not None:
                        rate = calculated_rate
                        note = "Dopočítaný kurz"
                        # Pokud je kurz dopočítaný a je to svátek, přidáme informaci
                        if is_holiday_flag:
                            note += " (svátek)"
                    else:
                        note = "Kurz není k dispozici"

                # Zapišeme řádek do CSV
                csvfile.write(
                    f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
                )

                # Přejdeme na další den
                current_date += timedelta(days=1)

        debug_print(f"Report byl úspěšně uložen do: {filepath}")
        return filepath
    except IOError as e:
        debug_print(f"Chyba při zápisu do souboru: {e}")
        return None


def generate_period_report(
    start_date_str, end_date_str, currency_code, output_dir="data"
):
    """
    Vygeneruje report kurzů pro zadané období a měnu.

    :param start_date_str: Počáteční datum ve formátu DD.MM.YYYY.
    :param end_date_str: Koncové datum ve formátu DD.MM.YYYY.
    :param currency_code: Kód měny (např. USD).
    :param output_dir: Adresář, kam se má CSV soubor s reportem uložit.
    :return: Chta k vytvořenému CSV souboru.
    """
    debug_print(
        f"Generuji report kurzů pro {currency_code} za období {start_date_str} - {end_date_str}..."
    )

    try:
        start_date = datetime.strptime(start_date_str, "%d.%m.%Y")
        end_date = datetime.strptime(end_date_str, "%d.%m.%Y")
    except ValueError as e:
        debug_print(f"Neplatný formát data: {e}")
        return None

    # Ale nebudeme generovat report pro budoucí datumy
    today = datetime.now()
    # Pokud je požadované období v budoucnosti, nepokračujeme
    if start_date.date() > today.date():
        debug_print(
            f"Chyba: Nelze generovat report pro období v budoucnosti (od {start_date_str})."
        )
        return None

    # Pokud je konec období v budoucnosti, omezíme ho na dnešní datum
    if end_date.date() > today.date():
        debug_print(
            f"Upozornění: Konec období byl omezen na dnešní datum ({today.strftime('%d.%m.%Y')}), protože zbytek je v budoucnosti."
        )
        end_date = today

    # Zkontrolujeme, zda databáze obsahuje data pro roky v rozmezí
    # Pro generování reportu vždy stáhneme aktuální roční data, aby byl report kompletní
    start_year = start_date.year
    end_year = end_date.year

    for year in range(start_year, end_year + 1):
        debug_print(f"Stahuji roční data pro rok {year}...")
        # Stáhneme roční data s vynuceným stažením
        os.makedirs("data", exist_ok=True)
        data_fetcher.download_yearly_data(year, output_dir="data")

    # Otevřeme CSV soubor pro zápis
    filename = f"report_{currency_code}_{start_date_str.replace('.', '_')}_to_{end_date_str.replace('.', '_')}.csv"
    filepath = os.path.join(output_dir, filename)

    try:
        with open(filepath, "w", newline="", encoding="utf-8") as csvfile:
            # Nové pořadí sloupců: Datum, Kurz, Den v týdnu, Svátek, Poznámka
            csvfile.write("Datum,Kurz,Den v týdnu,Svátek,Poznámka\n")

            current_date = start_date
            while current_date <= end_date:
                date_str = current_date.strftime("%d.%m.%Y")

                # Získáme den v týdnu
                day_name = get_czech_day_name(date_str)

                # Zkontrolujeme, zda je svátek
                is_holiday_flag = is_holiday(date_str)
                holiday_text = "ano" if is_holiday_flag else "ne"

                # Získáme kurz pro dané datum
                rate = database.get_rate(date_str, currency_code)
                note = ""

                if rate is not None:
                    # Kurz byl nalezen v databázi
                    pass
                else:
                    # Kurz nebyl nalezen, zkusíme dopočítat
                    calculated_rate = get_rate_for_date_with_fallback(
                        date_str, currency_code
                    )
                    if calculated_rate is not None:
                        rate = calculated_rate
                        note = "Dopočítaný kurz"
                        # Pokud je kurz dopočítaný a je to svátek, přidáme informaci
                        if is_holiday_flag:
                            note += " (svátek)"
                    else:
                        note = "Kurz není k dispozici"

                # Zapišeme řádek do CSV
                csvfile.write(
                    f"{date_str},{rate if rate is not None else ''},{day_name},{holiday_text},{note}\n"
                )

                # Přejdeme na další den
                current_date += timedelta(days=1)

        debug_print(f"Report byl úspěšně uložen do: {filepath}")
        return filepath
    except IOError as e:
        debug_print(f"Chyba při zápisu do souboru: {e}")
        return None


# Příklad použití
if __name__ == "__main__":
    # Inicializace databáze (pro případ spuštění samostatně)
    database.init_db()

    # Test
    # generate_yearly_report(2020, "USD")
    # generate_monthly_report(2020, 7, "USD")
    # generate_period_report("01.07.2020", "31.07.2020", "USD")
