import pandas as pd
from typing import Dict
from app.indicators.ema import ema
from app.indicators.rsi import rsi
from app.indicators.atr import atr

def apply_indicators(df: pd.DataFrame, specs) -> Dict[str, pd.Series]:
    out = {}
    for c in specs:
        key = f"{c.indicator}_{c.period}"
        if key in out:
            continue
        if c.indicator == "ema":
            out[key] = ema(df["close"], c.period)
        elif c.indicator == "rsi":
            out[key] = rsi(df["close"], c.period)
        elif c.indicator == "atr":
            out[key] = atr(df, c.period)
    return out

def eval_condition(row_idx, cond, inds):
    a = inds[f"{cond.indicator}_{cond.period}"]
    if cond.op in ("cross_above", "cross_below"):
        if not cond.compare:
            return False
        b = inds[f"{cond.compare.indicator}_{cond.compare.period}"]
        if row_idx == 0:
            return False
        if cond.op == "cross_above":
            return a.iloc[row_idx-1] <= b.iloc[row_idx-1] and a.iloc[row_idx] > b.iloc[row_idx]
        return a.iloc[row_idx-1] >= b.iloc[row_idx-1] and a.iloc[row_idx] < b.iloc[row_idx]
    else:
        val = cond.value
        if cond.op == ">":  return a.iloc[row_idx] > val
        if cond.op == "<":  return a.iloc[row_idx] < val
        if cond.op == ">=": return a.iloc[row_idx] >= val
        if cond.op == "<=": return a.iloc[row_idx] <= val
    return False

def backtest(df: pd.DataFrame, strategy) -> pd.DataFrame:
    conds = strategy.entry.long + strategy.entry.short
    inds = apply_indicators(df, conds)

    trades = []
    position = None

    for i in range(len(df)):
        price = df.loc[i, "close"]
        time = df.loc[i, "time"]

        if position is None:
            long_ok = all(eval_condition(i, c, inds) for c in strategy.entry.long)
            short_ok = all(eval_condition(i, c, inds) for c in strategy.entry.short)

            if long_ok:
                position = {
                    "side": "buy",
                    "entry_price": price,
                    "entry_time": time
                }
            elif short_ok:
                position = {
                    "side": "sell",
                    "entry_price": price,
                    "entry_time": time
                }
        else:
            sl = strategy.exit.sl_points
            tp = strategy.exit.tp_points

            if position["side"] == "buy":
                if price <= position["entry_price"] - sl:
                    trades.append({**position, "exit_price": price, "exit_time": time, "result": "SL"})
                    position = None
                elif price >= position["entry_price"] + tp:
                    trades.append({**position, "exit_price": price, "exit_time": time, "result": "TP"})
                    position = None
            else:
                if price >= position["entry_price"] + sl:
                    trades.append({**position, "exit_price": price, "exit_time": time, "result": "SL"})
                    position = None
                elif price <= position["entry_price"] - tp:
                    trades.append({**position, "exit_price": price, "exit_time": time, "result": "TP"})
                    position = None

    return pd.DataFrame(trades)
