import math
import pytz
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
import FinanceDataReader as fdr
from utils.css import load_css

def parse_input(text, cash_amount, cash_ratio):
    lines = text.strip().split(',')
    stock_inputs = []
    total_target_weight = 0
    
    for line in lines:
        parts = line.split()
        if len(parts) == 4:
            stock_code, currency_code, quantity_expr, target_weight_expr = parts
            quantity = math.floor(eval(quantity_expr.replace(' ', '')))
            target_weight = eval(target_weight_expr.replace(' ', ''))
            target_ratio = (1 - cash_ratio / 100) * target_weight
            stock_inputs.append((currency_code, stock_code, quantity, target_weight, target_ratio))
            total_target_weight += target_weight

    cash_amount = math.floor(cash_amount) if cash_amount else 0
    default_currency_cash = {'amount': cash_amount, 'target_weight': cash_ratio / 100.0}

    stock_total_weight = total_target_weight

    for i in range(len(stock_inputs)):
        stock_inputs[i] = (stock_inputs[i][0], stock_inputs[i][1], stock_inputs[i][2], stock_inputs[i][3], (1 - default_currency_cash['target_weight']) * stock_inputs[i][3] / stock_total_weight)

    return stock_inputs, default_currency_cash

def get_exchange_rate(currency_code, default_currency):
    if currency_code.lower() == default_currency.lower():
        return 1.0
    
    ticker = f"{currency_code.upper()}{default_currency.upper()}=X"
    data = fdr.DataReader(ticker)
    if not data.empty:
        return data['Close'].iloc[0]
    else:
        raise ValueError("Failed to retrieve exchange rate data.")

def get_exchange_reflected_stock_price(stock_code, currency_code, default_currency):
    new_price = get_current_stock_price(stock_code)
    exchange_rate = get_exchange_rate(currency_code, default_currency)
    return math.floor(new_price * exchange_rate)

def get_current_stock_price(stock_code):
    df = fdr.DataReader(stock_code)
    return df['Close'].iloc[-1]

def build_portfolio(stock_inputs, default_currency_cash, default_currency):
    portfolio = {}
    target_weights = {}
    
    with ThreadPoolExecutor() as executor:
        results = executor.map(lambda x: (x[1], get_exchange_reflected_stock_price(x[1], x[0], default_currency), x[2], x[3], x[4], x[0]), stock_inputs)
    
    for stock_code, new_price, quantity, target_weight, target_ratio, currency_code in results:
        portfolio[stock_code] = {'quantity': quantity, 'price': new_price, 'target_weight': target_weight, 'currency': currency_code}
        target_weights[stock_code] = target_ratio

    return portfolio, target_weights, default_currency_cash

def format_quantity(quantity):
    if quantity < 0:
        return f"({-quantity:,})"
    else:
        return f"{quantity:,}"

def get_portfolio_rebalancing_info(portfolio, target_weights, krw_cash, default_currency):
    css = load_css()

    kst = pytz.timezone('Asia/Seoul')
    current_time = datetime.now(kst).strftime("%I:%M %p %b-%d-%Y")

    total_value = sum(stock['price'] * stock['quantity'] for stock in portfolio.values()) + krw_cash['amount']
    total_new_stock_value = 0
    total_trade_value = 0
    adjustments = []

    currency_symbols = {
    "KRW": "₩",
    "USD": "$",
    "EUR": "€",
    "JPY": "¥",
    "GBP": "£",
    "AUD": "A$",
    "CAD": "C$",
    "CHF": "CHF",
    "CNY": "¥",
    "INR": "₹",
    "BRL": "R$",
    "ZAR": "R",
    "SGD": "S$",
    "HKD": "HK$"
}

    currency_symbol = currency_symbols.get(default_currency.upper(), "")

    # Calculate current weights and values
    current_weights = {stock_code: (stock['price'] * stock['quantity'] / total_value) * 100 for stock_code, stock in portfolio.items()}
    current_values = {stock_code: stock['price'] * stock['quantity'] for stock_code, stock in portfolio.items()}
    
    # Include cash in current weights and values
    current_weights['CASH'] = (krw_cash['amount'] / total_value) * 100
    current_values['CASH'] = krw_cash['amount']

    # Sort stocks by current weight in descending order
    sorted_stocks = sorted(current_weights.items(), key=lambda x: x[1], reverse=True)

    # Display current weights and values section
    current_info_html = "<h3>Your Portfolio Holdings</h3><div class='table-container'><table style='border-collapse: collapse;'>"
    current_info_html += "<thead><tr><th style='border: 1px hidden #ddd; text-align: center;'>Stock Code</th><th style='border: 1px hidden #ddd; text-align: center;'>Current Weight (%)</th><th style='border: 1px hidden #ddd; text-align: center;'>Current Value</th></tr></thead><tbody>"
    for stock_code, weight in sorted_stocks:
        current_info_html += (
            f"<tr>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{stock_code.upper()}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{weight:.1f}%</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{currency_symbol}{current_values[stock_code]:,.0f}</td>"
            f"</tr>"
        )
    current_info_html += "</tbody></table></div><br>"

    for stock_code, stock_data in portfolio.items():
        current_value = stock_data['price'] * stock_data['quantity']
        target_value = total_value * target_weights.get(stock_code, 0)
        difference = target_value - current_value
        trade_quantity = math.floor(difference / stock_data['price']) if difference > 0 else -math.ceil(-difference / stock_data['price'])
        new_quantity = trade_quantity + stock_data['quantity']
        new_value = new_quantity * stock_data['price']
        trade_value = trade_quantity * stock_data['price']
        total_trade_value += abs(trade_value)
        total_new_stock_value += new_value
        current_value_pct = (current_value / total_value) * 100
        new_value_pct = (new_value / total_value) * 100

        adjustments.append((difference, current_value, target_value, current_value_pct, trade_quantity, stock_code, stock_data['price'], new_value, trade_value, stock_data['quantity'], new_quantity, target_weights[stock_code], new_value_pct, stock_data['target_weight'], stock_data['currency']))

    krw_new_amount = total_value - total_new_stock_value
    krw_target_value = total_value * krw_cash['target_weight']
    krw_difference = krw_new_amount - krw_cash['amount']
    trade_quantity = krw_difference
    new_quantity = krw_cash['amount'] + trade_quantity
    new_value = new_quantity
    trade_value = trade_quantity
    current_value = krw_cash['amount']
    current_value_pct = (current_value / total_value) * 100
    new_value_pct = (new_value / total_value) * 100

    adjustments.append((krw_difference, current_value, krw_target_value, current_value_pct, trade_quantity, 'CASH', 1, new_value, trade_value, krw_cash['amount'], new_quantity, krw_cash['target_weight'], new_value_pct, '', 'KRW'))

    portfolio_info = css + f"""
        <div><br>
            <p><span class="wrap-text" style='font-size: 1.6rem; font-weight: bold; color: #1678fb;'>{currency_symbol}{total_value:,.0f}</span> as of <span style='color: #6e6e73;'>{current_time}</span></p>
        <br></div>
        """

    currency_totals = {stock_data['currency']: {'amount': 0, 'weight': 0} for stock_data in portfolio.values()}

    for stock_code, stock_data in portfolio.items():
        currency = stock_data['currency']
        current_value = stock_data['price'] * stock_data['quantity']
        currency_totals[currency]['amount'] += current_value
        currency_totals[currency]['weight'] += current_value / total_value

    currency_totals['CASH'] = {'amount': krw_cash['amount'], 'weight': krw_cash['amount'] / total_value}
    sorted_currencies = sorted(currency_totals.items(), key=lambda x: x[1]['weight'], reverse=True)

    currency_table = "<h3>Your Portfolio by Currency</h3><div class='table-container wrap-text'><table style='border-collapse: collapse;'>"
    currency_table += "<thead><tr><th style='border: 1px hidden #ddd; text-align: center;'>Currency</th><th style='border: 1px hidden #ddd; text-align: center;'>Total Weight (%)</th><th style='border: 1px hidden #ddd; text-align: center;'>Total Value</th></tr></thead><tbody>"

    for currency, data in sorted_currencies:
        currency_table += (
            f"<tr>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{currency.upper()}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{data['weight'] * 100:.1f}%</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{currency_symbol}{data['amount']:,}</td>"            
            f"</tr>"
        )

    currency_table += "</tbody></table></div><br>"

    result_message = portfolio_info + current_info_html + currency_table + "<h3>Re-Balancing Analysis</h3><div class='table-container wrap-text'><table style='border-collapse: collapse;'>"
    result_message += "<thead><tr><th style='border: 1px hidden #ddd; text-align: center;'>Stock Code</th><th style='border: 1px hidden #ddd; text-align: center;'>Current Weight (%)</th><th style='border: 1px hidden #ddd; text-align: center;'>Target Weight</th><th style='border: 1px hidden #ddd; text-align: center;'>Target Ratio (%)</th><th style='border: 1px hidden #ddd; text-align: center;'>Buy or Sell?</th><th style='border: 1px hidden #ddd; text-align: center;'>Trade Amount</th><th style='border: 1px hidden #ddd; text-align: center;'>Current Price per Share</th><th style='border: 1px hidden #ddd; text-align: center;'>Estimated # of<br> Shares to Buy or Sell</th><th style='border: 1px hidden #ddd; text-align: center;'>Quantity of Units</th><th style='border: 1px hidden #ddd; text-align: center;'>Market Value</th><th style='border: 1px hidden #ddd; text-align: center;'>% Asset Allocation</th></tr></thead><tbody>"

    for adj in adjustments:
        difference, current_value, target_value, current_value_pct, trade_quantity, stock_code, price, new_value, trade_value, old_quantity, new_quantity, target_ratio, new_value_pct, target_weight, currency = adj
        Buy_or_Sell = ""
        if trade_quantity > 0:
            Buy_or_Sell = f"<span class='buy-sell buy'>Buy</span>"
        elif trade_quantity < 0:
            Buy_or_Sell = f"<span class='buy-sell sell'>Sell</span>"
        else:
            Buy_or_Sell = f"<span></span>"

        current_value_pct_str = f"{current_value_pct:.1f}%"
        target_weight_str = f"<span class='highlight-edit'>{target_weight}</span>" if stock_code != 'CASH' else ''
        target_ratio_str = f"<span class='highlight-edit'>{target_ratio * 100:.1f}%</span>" if stock_code == 'CASH' else f"{target_ratio * 100:.1f}%"
        trade_value_str = f"<span class='highlight-sky'>{format_quantity(trade_value)}</span>" if trade_value != 0 else ''
        price_str = f"{currency_symbol}{price:,.0f}" if stock_code != 'CASH' else ''        
        trade_quantity_str = (
            f"<span class='highlight-sky'>{format_quantity(trade_quantity)}</span>"
            if stock_code != 'CASH' and trade_value != 0 else ''
        )
        old_quantity_str = f"{old_quantity:,.0f} → {new_quantity:,.0f}" if stock_code != 'CASH' else ''
        new_value_str = f"{currency_symbol}{new_value:,.0f}"
        new_value_pct_str = f"{new_value_pct:.1f}%"

        result_message += (
            f"<tr>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{stock_code.upper()}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{current_value_pct_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{target_weight_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{target_ratio_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{Buy_or_Sell}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{trade_value_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{price_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{trade_quantity_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{old_quantity_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{new_value_str}</td>"
            f"<td style='border: 1px hidden #ddd; text-align: center;'>{new_value_pct_str}</td>"
            f"</tr>"
        )

    result_message += "</tbody></table></div>"
    
    return result_message

def rebalancing_tool(user_input, cash_amount, cash_ratio, default_currency):
    try:
        stock_inputs, default_currency_cash = parse_input(user_input, cash_amount, cash_ratio)
        portfolio, target_weights, default_currency_cash = build_portfolio(stock_inputs, default_currency_cash, default_currency)
        result = get_portfolio_rebalancing_info(portfolio, target_weights, default_currency_cash, default_currency)
        return result
    except Exception as e:
        return str(e)