import json
from pathlib import Path
import threading
import time
import streamlit as st
from core.portfolio import place_market_order, place_limit_order, get_user_positions_by_token
from core.data_hub import DataHub
from datetime import datetime

RULES_DIR = Path("rules")
RULES_DIR.mkdir(exist_ok=True)
OWNERSHIP_FILE = Path("ownership/rule_ownership.json")
FLAG_FILE_BOT = Path(".rule_bot.flag")

class RuleEngine:
    def __init__(self, hub: DataHub):
        self.hub = hub
        self.thread = None
        self.running = FLAG_FILE_BOT.exists()
        if self.running:
            self.thread = threading.Thread(target=self._bot_loop, daemon=True)
            self.thread.start()
            print("🚀 RuleEngine bot started (runs in background)")


    def _load_ownership(self):
        if OWNERSHIP_FILE.exists():
            try:
                return json.loads(OWNERSHIP_FILE.read_text())
            except:
                pass
        return {"orders": {}, "positions": {}}

    def _save_ownership(self, data):
        OWNERSHIP_FILE.parent.mkdir(exist_ok=True)
        OWNERSHIP_FILE.write_text(json.dumps(data, indent=2))

    def owns(self, rule_name: str, slug: str, token: str, category="positions"):
        data = self._load_ownership()
        key = f"{rule_name}|{slug}|{token}"
        return key in data.get(category, {})

    def record_order(self, rule_name: str, slug: str, token: str):
        data = self._load_ownership()
        key = f"{rule_name}|{slug}|{token}"
        data.setdefault("positions", {})[key] = {"timestamp": time.time(), "rule": rule_name, "slug": slug, "token": token}
        self._save_ownership(data)

    def cleanup_closed_positions(self):
        data = self._load_ownership()
        wallet = st.secrets.get("POLYMARKET_WALLET_ADDRESS", "")
        real_pos = get_user_positions_by_token()
        current_tokens = set(real_pos.keys())
        positions = data.get("positions", {})
        to_remove = [k for k, v in positions.items() if v["token"] not in current_tokens]
        for k in to_remove:
            del positions[k]
        data["positions"] = positions
        self._save_ownership(data)

    def _clean_finished_events(self, rule: dict) -> dict:
        active = set(self.hub.get_active_slugs())
        cleaned = [e for e in rule.get("events", []) if e in active]
        if len(cleaned) < len(rule.get("events", [])):
            #print(f"🧹 Auto-cleaned finished events from rule '{rule.get('oname')}'")
            rule = rule.copy()
            rule["events"] = cleaned
        return rule

    def list_rules(self):
        rules = []
        for f in RULES_DIR.glob("*.json"):
            try:
                with open(f) as fh:
                    rule = json.load(fh)
                rule = self._clean_finished_events(rule)
                rules.append(rule)
            except Exception:
                continue
        return rules

    def save_rule(self, rule):
        rule = self._clean_finished_events(rule)
        name = rule.get("oname")
        if not name:
            return
        with open(RULES_DIR / f"{name}.json", "w") as f:
            json.dump(rule, f, indent=2)
        #print(f"💾 Rule '{name}' saved")

    def _safe_eval_condition(self, condition_boxes: list, context: dict) -> bool:
        if not condition_boxes:
            return True
        expr = " ".join(str(box) for box in condition_boxes)
        if "PriceAVG" in expr and "PriceAVG" not in context.keys():
            return False
        expr = expr.replace("AND", " and ").replace("OR", " or ")
        try:
            safe_globals = {"__builtins__": {}, "abs": abs, "max": max, "min": min, "round": round, "int": int, "float": float}
            return bool(eval(expr, safe_globals, context))
        except Exception as e:
            #print(f"❌ Eval error: {e}")
            return False

    def evaluate_rule(self, rule: dict, slug: str = None, token: str = None):
        """Always passes token so Top is correct"""
        if slug is None:
            slug = rule.get("events", [None])[0]
        if not slug:
            return False

        rule_side = rule.get("order", {}).get("side", "BUY")
        ctx = self.hub.get_rule_context(slug, token, rule_side)

        for v in rule.get("variables", []):
            try:
                ctx[v["name"]] = eval(v["expression"], {"__builtins__": {}}, ctx)
            except:
                pass

        if ctx.get("Count", 0) > ctx.get("Top", 99999):
            #print(f"[RULE_BOT] 🚫 HARD CONSTRAINT: Count ({ctx['Count']}) > Top ({ctx['Top']}) for {slug}/{token}")
            return False

        return self._safe_eval_condition(rule.get("condition_boxes", []), ctx)

    def run_bot(self, dry_run=True):
        """Runs completely independently of any tab"""
        if not self.running:
            return
        """Reads real flag file every cycle — completely independent of session_state"""
        # Override with real flag file if it exists
        FLAG_FILE = Path(".dry_run.flag")
        dry_run = FLAG_FILE.exists()
        self.cleanup_closed_positions()
        for rule in self.list_rules():
            if not rule.get("enabled", False):
                continue
            rule_name = rule.get("oname") or rule.get("oname", "unnamed")
            for slug in rule.get("events", []):
                bins = self.hub.get_bins_for_event(slug)
                for bin_info in bins:
                    token = bin_info["token"]
                    if self.owns(rule_name, slug, token) or self.owns(rule_name, slug, token, "orders"):
                        continue
                    if self.evaluate_rule(rule, slug, token):
                        order_details = rule.get("order", {})
                        side = order_details.get("side", "BUY")
                        if side == "BUY":
                            amount = float(order_details.get("amount", 10))
                        elif token in hub.global_data["portfolio"].keys():
                            p=hub.global_data["portfolio"][token]
                            amount = max((p.get("size")/100.0)*float(order_details.get("amount", 10)), 5.0)
                        if amount == 0:
                            continue
                        type = order_details.get("order_type")
                        if not dry_run:
                            if type == "Limit (fixed)":
                                lim = order_details.get("price_offset")
                                res = place_limit_order(token, side, amount, lim, dry_run=False)
                            elif type == "Market":
                                res = place_market_order(token, side, amount, dry_run=False)
                            else:
                                print("❌ order-type not implemented.")
                            if res:
                                print(f"🧪 Order placed: {side} {amount} on {slug}/{token}")
                                self.record_order(rule_name, slug, token)
                            else:
                                print("❌ FAILED TO PLACE ORDER!!!")
                        else:
                            print(f"🧪 DRY-RUN: {side} {amount} on {slug}/{token}")

    def _bot_loop(self):
        while self.running:
            self.running=FLAG_FILE_BOT.exists()
            self.run_bot(dry_run=st.session_state.get("dry_run", True))
            time.sleep(30)

    def start(self):
        if self.running:
            return
        self.running = True
        self.thread = threading.Thread(target=self._bot_loop, daemon=True)
        self.thread.start()
        print("🚀 RuleEngine bot started (runs in background)")

    def stop(self):
        self.running = False
        if self.thread:
            self.thread.join(timeout=2)
        print("⏹️  RuleEngine bot stopped")


    def delete_rule(self, name: str):
        """Delete rule by name (used by the Delete button in the list)"""
        path = RULES_DIR / f"{name}.json"
        if path.exists():
            path.unlink()
            print(f"🗑️ Rule '{name}' deleted")
        else:
            print(f"⚠️ Rule '{name}' not found")

        self.running = False
        if self.thread:
            self.thread.join(timeout=2)
        print("⏹️ RuleEngine bot stopped")


# ====================== AUTO-INSTANTIATION ======================
if "rule_engine" not in st.session_state:
    hub = DataHub.get_instance()
    st.session_state.rule_engine = RuleEngine(hub)
