# -*- coding: utf-8 -*-
import datetime as dt
from functools import reduce
import os
import sys

import holidays
import numpy as np
import pandas as pd
from dateutil.relativedelta import relativedelta

years = range(1990,2100)

LOCALE_HOLIDAY = dict(sorted({
    'AE-DUBAI': holidays.AE(categories=['public'], years=years),
    'AR-BUENOS_AIRES': holidays.AR(categories=['public'], years=years),
    'AU-SYDNEY': holidays.AU(subdiv='NSW', categories=['public', 'bank'], years=years),
    'AU-MELBOURNE': holidays.AU(subdiv='VIC', categories=['public', 'bank'], years=years),
    'BR-SAO_PAULO': holidays.BR(subdiv='SP', categories=['public'], years=years),
    'CA-TORONTO': holidays.CA(subdiv='ON', categories=['public'], years=years),
    'CH-ZURICH': holidays.CH(subdiv='ZH', categories=['public'], years=years),
    'CL-SANTIAGO': holidays.CL(subdiv='RM', categories=['public', 'bank'], years=years),
    'CN-SHANGHAI': holidays.CN(categories=['public'], years=years),
    'CO-BOGOTA': holidays.CO(categories=['public'], years=years),
    'CZ-PRAGUE': holidays.CZ(categories=['public'], years=years),
    'DK-COPENHAGEN': holidays.DK(categories=['public'], years=years),
    'DE-FRANKFURT': holidays.DE(subdiv='HH', categories=['public'], years=years),
    'ES-MADRID': holidays.ES(subdiv='MD', categories=['public'], years=years),
    'FR-PARIS': holidays.FR(categories=['public'], years=years),
    'FI-HELSINKI': holidays.FI(categories=['public'], years=years),
    'HK-HONG_KONG': holidays.HK(categories=['public'], years=years),
    'HU-BUDAPEST': holidays.HU(categories=['public'], years=years),
    'ID-JAKARTA': holidays.ID(categories=['public'], years=years),
    'IN-MUMBAI': holidays.IN(subdiv='MH', categories=['public'], years=years),
    'IL-TEL_AVIV': holidays.IL(categories=['public'], years=years),
    'IS-REYKJAVIK': holidays.IS(categories=['public'], years=years),
    'MY-KUALA_LUMPUR': holidays.MY(subdiv='KUL', categories=['public'], years=years),
    'MX-MEXICO_CITY': holidays.MX(categories=['public'], years=years),
    'NO-OSLO': holidays.NO(categories=['public'], years=years),
    'NZ-AUCKLAND': holidays.NZ(subdiv='AUK', categories=['public'], years=years),
    'PL-WARSAW': holidays.PL(categories=['public'], years=years),
    'PH-MANILA': holidays.PH(categories=['public'], years=years),
    'RU-MOSCOW': holidays.RU(categories=['public'], years=years),
    'SA-RIYADH': holidays.SA(categories=['public'], years=years),
    'SE-STOCKHOLM': holidays.SE(categories=['public'], years=years),
    'SG-SINGAPORE': holidays.SG(categories=['public'], years=years),
    'TH-BANKCOK': holidays.TH(categories=['public', 'bank'], years=years),
    'TR-ISTANBUL': holidays.TR(categories=['public'], years=years),
    'TW-TAPEI': holidays.TW(categories=['public'], years=years),
    'UK-LONDON': holidays.UK(subdiv='ENG', categories=['public'], years=years),
    'US-NEW_YORK': holidays.US(subdiv='NY', categories=['public'], years=years),
    'VN-HO_CHI_MINH_CITY': holidays.VN(categories=['public'], years=years),
    'ZA-JOHANNESBURG': holidays.ZA(categories=['public'], years=years),
    'European_Central_Bank': holidays.ECB(categories=['public'], years=years),
    'ICE_Futures_Europe': holidays.IFEU(categories=['public'], years=years),
    'New_York_Stock_Exchange': holidays.XNYS(categories=['public'], years=years),
}.items()))


CCY_HOLIDAY = dict(sorted({
    'AED': holidays.AE(categories=['public'], years=years),
    'ARS': holidays.AR(categories=['public'], years=years),
    'AUD': holidays.AU(subdiv='NSW', categories=['public', 'bank'], years=years),
    'BRL': holidays.BR(subdiv='SP', categories=['public'], years=years),
    'CAD': holidays.CA(subdiv='ON', categories=['public'], years=years),
    'CHF': holidays.CH(subdiv='ZH', categories=['public'], years=years),
    'CLP': holidays.CL(subdiv='RM', categories=['public', 'bank'], years=years),
    'CNY': holidays.CN(categories=['public'], years=years),
    'COP': holidays.CO(categories=['public'], years=years),
    'CZK': holidays.CZ(categories=['public'], years=years),
    'DKK': holidays.DK(categories=['public'], years=years),
    'HKD': holidays.HK(categories=['public'], years=years),
    'HUF': holidays.HU(categories=['public'], years=years),
    'IDR': holidays.ID(categories=['public'], years=years),
    'INR': holidays.IN(subdiv='MH', categories=['public'], years=years),
    'ILS': holidays.IL(categories=['public'], years=years),
    'ISK': holidays.IS(categories=['public'], years=years),
    'MYR': holidays.MY(subdiv='KUL', categories=['public'], years=years),
    'MXN': holidays.MX(categories=['public'], years=years),
    'NOK': holidays.NO(categories=['public'], years=years),
    'NZD': holidays.NZ(subdiv='AUK', categories=['public'], years=years),
    'PLN': holidays.PL(categories=['public'], years=years),
    'PHP': holidays.PH(categories=['public'], years=years),
    'RUB': holidays.RU(categories=['public'], years=years),
    'SAR': holidays.SA(categories=['public'], years=years),
    'SEK': holidays.SE(categories=['public'], years=years),
    'SGD': holidays.SG(categories=['public'], years=years),
    'THB': holidays.TH(categories=['public', 'bank'], years=years),
    'TRY': holidays.TR(categories=['public'], years=years),
    'TWD': holidays.TW(categories=['public'], years=years),
    'GBP': holidays.UK(subdiv='ENG', categories=['public'], years=years),
    'USD': holidays.US(subdiv='NY', categories=['public'], years=years),
    'VND': holidays.VN(categories=['public'], years=years),
    'ZAR': holidays.ZA(categories=['public'], years=years),
    'EUR': holidays.ECB(categories=['public'], years=years),
}.items()))

#%%

def get_holidays_obj(ccy):
    ccy_uppercase = ccy.upper()
    if ccy_uppercase in CCY_HOLIDAY.keys():
        return CCY_HOLIDAY[ccy_uppercase]
    else:
        raise ValueError(f"Holidays not setup for {ccy_uppercase}")


def convert_weekend_to_weekmask(weekend_set):
    "For converting the holidays.weekend attribute to a valid weekmask for numpy.busdaycalendar"
    # Initialize a list of ones representing Monday to Sunday (working days)
    weekmask = [1, 1, 1, 1, 1, 1, 1]
    
    # Loop through the weekend days and set corresponding indexes in the weekmask to 0
    for weekend_day in weekend_set:
        weekmask[weekend_day] = 0
    
    # Return the weekmask as a string (optional, can also return as a list if needed)
    return ''.join(map(str, weekmask))


def get_busdaycalendar(ccys) -> np.busdaycalendar:
    """
    Create a calendar which has the holidays and business days of the currency inputs.
    """

    if ccys is None:
        return np.busdaycalendar()

    if type(ccys) is str:
        ccys = [ccys]
    elif type(ccys) is list:
        ccys = list(set(ccys))
        ccys = [ccy.upper() for ccy in ccys]

    holiday_objs = [get_holidays_obj(ccy) for ccy in ccys]
    combined_weekend_union = reduce(lambda x, y: x | y.weekend, holiday_objs, set())
    weekmasks = convert_weekend_to_weekmask(combined_weekend_union)

    # Flattan the list of lists
    holidays = [h for holiday_list in holiday_objs for h in holiday_list]

    return np.busdaycalendar(weekmask=weekmasks, holidays=holidays)  # .values.astype('datetime64[D]'))


# %% Create code for static holidays definition

if __name__ == "__main__":
    # Get the directory of the current file
    current_dir = os.path.dirname(os.path.abspath(__file__))

    # Add the directory to the system path
    sys.path.append(current_dir)
    df = pd.read_excel(current_dir + "\\calendar_map.xlsx", usecols=range(6))
    holiday_dict = {}
    holiday_dict_str = "LOCALE_HOLIDAY = dict(sorted({\n"
    ccy_holiday_dict = {}
    ccy_holiday_dict_str = "CCY_HOLIDAY = dict(sorted({\n"
    years=range(1990,2100)

    for i, row in df.iterrows():
        eval_str = None
        ccy = row["ISOCurrencyCode"]
        ui_string = row["UIString"]
        code = row["Code"]
        subdivision = row["Subdivision"]

        try:
            categories = [holidays.PUBLIC, holidays.BANK]
            if pd.isna(row["Subdivision"]):
                eval_str = f"holidays.{code}(categories={categories}, years=years)"

            else:
                eval_str = (
                    f"holidays.{code}(subdiv='{subdivision}', categories={categories}, years=years)"
                )
            holiday_obj = eval(eval_str)

        except ValueError:
            categories = [holidays.PUBLIC]
            if pd.isna(row["Subdivision"]):
                eval_str = f"holidays.{code}(categories={categories}, years=years)"
            else:
                eval_str = (
                    f"holidays.{code}(subdiv='{subdivision}', categories={categories}, years=years)"
                )
            holiday_obj = eval(eval_str)

        holiday_dict[ui_string] = holiday_obj
        holiday_dict_str += f"    '{ui_string}': {eval_str},\n"

        if row["CurrencyPrimaryCalendar"]:
            ccy_holiday_dict[ccy] = holiday_obj
            ccy_holiday_dict_str += f"    '{ccy}': {eval_str},\n"

    holiday_dict_str += "}.items()))"
    ccy_holiday_dict_str += "}.items()))"

    print(holiday_dict_str)
    print("\n")
    print(ccy_holiday_dict_str)
