"""
signal_parser.py
================
Regex parser untuk format sinyal trading dari Telegram.

Format yang didukung:
🚨 SIGNAL ALERT 🚨
🌐 #XAUUSD
📊 Trade Details:📈#SELL
⚪️ Entry Point: 4515
🔴 Stop Loss (SL): 4523
🟢 Take Profit 1 (TP1): 4512
🟢 Take Profit 2 (TP2): 4507
🟢 Take Profit 3 (TP3): 4499
"""

import re
from dataclasses import dataclass
from typing import Optional


@dataclass
class Signal:
    pair: str        # contoh: "XAUUSD"
    direction: str   # "BUY" atau "SELL"
    entry: float     # contoh: 4515.0
    sl: float        # contoh: 4523.0
    tp1: float       # contoh: 4512.0
    raw_text: str    # teks asli untuk logging
    entry_high: Optional[float] = None # Untuk range, contoh 4578 - 4582
    tp2: Optional[float] = None  # Take Profit 2 untuk partial close

def __validate_signal(sig: Optional[Signal]) -> Optional[Signal]:
    if not sig:
        return None
        
    # Auto-Correct: Jika TP > Entry dan SL < Entry, ini PASTI transaksi BUY
    if sig.tp1 > sig.entry and sig.sl < sig.entry:
        if sig.direction != "BUY":
            sig.direction = "BUY"
            
    # Auto-Correct: Jika TP < Entry dan SL > Entry, ini PASTI transaksi SELL
    elif sig.tp1 < sig.entry and sig.sl > sig.entry:
        if sig.direction != "SELL":
            sig.direction = "SELL"
            
    # Jika logika benar-benar hancur (misal TP dan SL sama-sama di atas Entry), batalkan
    else:
        return None

    return sig

def parse_signal(text: str) -> Optional[Signal]:
    """
    Mem-parsing teks sinyal dari Telegram.
    Return Signal object jika parsing berhasil dan logis, None jika gagal/typo.
    """
    sig = _parse_raw_signal(text)
    return __validate_signal(sig)

def _parse_raw_signal(text: str) -> Optional[Signal]:
    pair = None
    direction = None
    entry = None
    entry_high = None
    sl = None
    tp1 = None
    tp2 = None

    # Tipe 1: TheForexSupport (TFS)
    if "#" in text and "Entry Point" in text:
        try:
            pair_match = re.search(r"#([A-Z]{3,10})", text)
            dir_match = re.search(r"#(BUY|SELL)", text, re.IGNORECASE)
            entry_match = re.search(r"Entry\s*(?:Point)?\s*:\s*([\d.]+)", text, re.IGNORECASE)
            sl_match = re.search(r"Stop\s*Loss\s*\(?SL\)?\s*:\s*([\d.]+)", text, re.IGNORECASE)
            tp1_match = re.search(r"Take\s*Profit\s*1\s*\(?TP1\)?\s*:\s*([\d.]+)", text, re.IGNORECASE)
            tp2_match = re.search(r"Take\s*Profit\s*2\s*\(?TP2\)?\s*:\s*([\d.]+)", text, re.IGNORECASE)

            if pair_match and dir_match and entry_match and sl_match and tp1_match:
                return Signal(
                    pair=pair_match.group(1).upper(),
                    direction=dir_match.group(1).upper(),
                    entry=float(entry_match.group(1)),
                    sl=float(sl_match.group(1)),
                    tp1=float(tp1_match.group(1)),
                    raw_text=text,
                    tp2=float(tp2_match.group(1)) if tp2_match else None
                )
        except Exception:
            pass

    # Tipe 2: Format Baru (e.g. GOLD Sell. 4578.4582 atau 4578)
    # SKIP jika ada "Takeprofit" (itu Tipe 5)
    if ("GOLD" in text.upper() or "𝐆𝐎𝐋𝐃" in text) and "Takeprofit" not in text:
        try:
            pair = "XAUUSD"
            range_match = re.search(r"(BUY|SELL)[^\d]+(\d{4}(?:\.\d{1,2})?)[.\-](\d{4}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            single_match = re.search(r"(BUY|SELL)[^\d]+(\d{4}(?:\.\d{1,2})?)", text, re.IGNORECASE)

            if range_match:
                direction = range_match.group(1).upper()
                entry = float(range_match.group(2))
                entry_high = float(range_match.group(3))
            elif single_match:
                direction = single_match.group(1).upper()
                entry = float(single_match.group(2))

            sl_match = re.search(r"(?:SL|𝐒𝐋)[^\d]*([\d.]+)", text, re.IGNORECASE)
            if sl_match:
                sl = float(sl_match.group(1))
                
            tp_matches = re.findall(r"(?:TP|𝐓𝐏)[^\d]*([\d.]+)", text, re.IGNORECASE)
            if tp_matches:
                tp1 = float(tp_matches[0])
                if len(tp_matches) > 1:
                    tp2 = float(tp_matches[1])

            if pair and direction and entry and sl and tp1:
                # Pastikan entry selalu nilai terendah, dan entry_high nilai tertinggi
                if entry_high and entry_high < entry:
                    entry, entry_high = entry_high, entry
                return Signal(pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, raw_text=text, entry_high=entry_high, tp2=tp2)
        except Exception:
            pass

    # Tipe 3: Format "XAUUSD BUY 4485/82" (shorthand range, angka belakang)
    # Entry: 4485/82 → range 4482 - 4485 (/82 = 2 digit terakhir diganti)
    # TP berurutan (ambil TP pertama), SL terpisah
    pair_dir_match = re.search(r"([A-Z]{3,10})\s+(BUY|SELL)\s+(\d{4,5})(?:/(\d{2,3}))?", text, re.IGNORECASE)
    if pair_dir_match:
        try:
            pair = pair_dir_match.group(1).upper()
            direction = pair_dir_match.group(2).upper()
            entry_main = float(pair_dir_match.group(3))
            
            if pair_dir_match.group(4):
                # Shorthand range: 4485/82 → 4482
                short = pair_dir_match.group(4)
                main_str = pair_dir_match.group(3)
                # Ganti digit terakhir sesuai panjang shorthand
                entry_low = float(main_str[:len(main_str)-len(short)] + short)
                entry = min(entry_main, entry_low)
                entry_high = max(entry_main, entry_low)
            else:
                entry = entry_main
                entry_high = None

            sl_match = re.search(r"SL\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if sl_match:
                sl = float(sl_match.group(1))

            # Ambil TP pertama dan kedua
            tp_matches = re.findall(r"TP\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if tp_matches:
                tp1 = float(tp_matches[0])
                if len(tp_matches) > 1:
                    tp2 = float(tp_matches[1])

            if pair and direction and entry and sl and tp1:
                return Signal(pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, raw_text=text, entry_high=entry_high, tp2=tp2)
        except Exception:
            pass

    # Tipe 4: Format "#XAUUSD SELL @4567/4575" (Sure_Trade style)
    # Pair dengan #, entry dengan @, range penuh dengan /
    pair_dir_match = re.search(r"#([A-Z]{3,10})\s+(BUY|SELL)\s+@?(\d{4,5})\s*/\s*(\d{4,5})", text, re.IGNORECASE)
    if pair_dir_match:
        try:
            pair = pair_dir_match.group(1).upper()
            direction = pair_dir_match.group(2).upper()
            entry_a = float(pair_dir_match.group(3))
            entry_b = float(pair_dir_match.group(4))
            entry = min(entry_a, entry_b)
            entry_high = max(entry_a, entry_b)

            sl_match = re.search(r"SL\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if sl_match:
                sl = float(sl_match.group(1))

            # Ambil TP pertama dan kedua (skip "TP OPEN")
            tp_matches = re.findall(r"TP\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if tp_matches:
                tp1 = float(tp_matches[0])
                if len(tp_matches) > 1:
                    tp2 = float(tp_matches[1])

            if pair and direction and entry and sl and tp1:
                return Signal(pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, raw_text=text, entry_high=entry_high, tp2=tp2)
        except Exception:
            pass

    # Tipe 5: Format "SELL GOLD 4567 - 4572" dengan "Takeprofit1 = 4557" dan "Stoploss = 4587"
    if "GOLD" in text.upper() and "Takeprofit" in text:
        try:
            pair = "XAUUSD"
            dir_match = re.search(r"(BUY|SELL)", text, re.IGNORECASE)
            if dir_match:
                direction = dir_match.group(1).upper()

            # Range entry: 4567   - 4572  atau  4724.5  - 4729.5
            range_match = re.search(r"(\d{4,5}(?:\.\d{1,2})?)\s*-\s*(\d{4,5}(?:\.\d{1,2})?)", text)
            if range_match:
                entry_a = float(range_match.group(1))
                entry_b = float(range_match.group(2))
                entry = min(entry_a, entry_b)
                entry_high = max(entry_a, entry_b)
            else:
                single_match = re.search(r"GOLD\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
                if single_match:
                    entry = float(single_match.group(1))

            sl_match = re.search(r"Stoploss\s*=\s*(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if sl_match:
                sl = float(sl_match.group(1))

            tp_match = re.search(r"Takeprofit1\s*=\s*(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if tp_match:
                tp1 = float(tp_match.group(1))

            tp2_match = re.search(r"Takeprofit2\s*=\s*(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if tp2_match:
                tp2 = float(tp2_match.group(1))

            if pair and direction and entry and sl and tp1:
                if entry_high and entry_high < entry:
                    entry, entry_high = entry_high, entry
                return Signal(pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, raw_text=text, entry_high=entry_high, tp2=tp2)
        except Exception:
            pass

    # TIPE 6: The Forex Support Quick Signal
    # Contoh:
    # XAUUSD SELL NOW 4673
    # Set TP1 +30 Pips
    if "NOW" in text.upper() and ("TP1" in text.upper() and "+" in text):
        try:
            pair_match = re.search(r"([A-Z]{6})", text)
            pair = pair_match.group(1).upper() if pair_match else "XAUUSD" # Default XAUUSD
            
            dir_match = re.search(r"(BUY|SELL)\s+NOW\s+(\d{4,5}(?:\.\d{1,2})?)", text, re.IGNORECASE)
            if dir_match:
                direction = dir_match.group(1).upper()
                entry = float(dir_match.group(2))
                
                tp_match = re.search(r"TP1\s*\+\s*(\d+)", text, re.IGNORECASE)
                if tp_match:
                    pips = float(tp_match.group(1))
                    points = pips / 10.0 # asumsikan 10 pip = 1.0 point XAUUSD
                    
                    if direction == "BUY":
                        tp1 = entry + points
                        sl = entry - 8.0 # Default SL 80 pips
                    else:
                        tp1 = entry - points
                        sl = entry + 8.0 # Default SL 80 pips
                        
                    return Signal(pair=pair, direction=direction, entry=entry, sl=sl, tp1=tp1, raw_text=text)
        except Exception:
            pass

    return None


# === UNIT TEST ===
if __name__ == "__main__":
    test_msg = """🚨 SIGNAL ALERT 🚨

🌐 #XAUUSD

📊 Trade Details:📈#SELL

⚪️ Entry Point: 4515
🔴 Stop Loss (SL): 4523

🟢 Take Profit 1 (TP1): 4512
🟢 Take Profit 2 (TP2): 4507
🟢 Take Profit 3 (TP3): 4499

⚠️ Keep in mind to not risk more then 1-2% of your balance on this trade"""

    result = parse_signal(test_msg)
    if result:
        print("✅ Parsing berhasil!")
        print(f"   Pair     : {result.pair}")
        print(f"   Direction: {result.direction}")
        print(f"   Entry    : {result.entry}")
        print(f"   SL       : {result.sl}")
        print(f"   TP1      : {result.tp1}")
    else:
        print("❌ Parsing gagal!")
