import os
import json
import time
import logging
import threading
import subprocess
import platform as _platform
from pathlib import Path
from flask import Flask, request, jsonify, send_from_directory

from marziel.config import LLM_PORT, IDLE_TIMEOUT, VLLM_URL, VLLM_MODEL, AGENT_TIERS
from marziel.billing import billing_bp, read_license
from marziel.search import needs_web_search, search_duckduckgo, format_search_context

logger = logging.getLogger("marziel.server")

# ── Static files directory (bundled React build) ──
STATIC_DIR = Path(__file__).parent / "static"

# ── Idle engine management ──
_last_request_time = time.time()
_watchdog_started = False

# ── Rate limiting (per-IP, in-memory) ──
from collections import defaultdict
_rate_buckets: dict[str, list[float]] = defaultdict(list)
_rate_lock = threading.Lock()
RATE_LIMIT = 20       # max requests per window
RATE_WINDOW = 60      # window in seconds

_RATE_LIMITED_PATHS = {"/llm/chat", "/api/checkout", "/api/webhook"}


def _is_rate_limited(ip: str, path: str) -> bool:
    """Check if IP exceeded rate limit for sensitive endpoints."""
    if path not in _RATE_LIMITED_PATHS:
        return False
    now = time.time()
    with _rate_lock:
        bucket = _rate_buckets[ip]
        # Purge old entries outside window
        _rate_buckets[ip] = [t for t in bucket if now - t < RATE_WINDOW]
        if len(_rate_buckets[ip]) >= RATE_LIMIT:
            return True
        _rate_buckets[ip].append(now)
    return False


def _get_resource_info():
    """Get current system resource usage."""
    info = {}
    try:
        import resource
        # Memory usage of this process (MB)
        info["server_rss_mb"] = round(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024 / 1024, 1)
    except Exception:
        pass

    # System RAM
    try:
        if _platform.system() == "Darwin":
            result = subprocess.run(
                ["sysctl", "-n", "hw.memsize"],
                capture_output=True, text=True, timeout=3
            )
            total_bytes = int(result.stdout.strip())
            info["system_ram_gb"] = round(total_bytes / 1024**3, 1)

            # Get available memory via vm_stat
            result = subprocess.run(["vm_stat"], capture_output=True, text=True, timeout=3)
            for line in result.stdout.split("\n"):
                if "Pages free" in line:
                    pages = int(line.split(":")[1].strip().rstrip("."))
                    info["available_ram_gb"] = round(pages * 4096 / 1024**3, 1)
                    break
        else:
            with open("/proc/meminfo") as f:
                for line in f:
                    if line.startswith("MemTotal"):
                        info["system_ram_gb"] = round(int(line.split()[1]) / 1024**2, 1)
                    elif line.startswith("MemAvailable"):
                        info["available_ram_gb"] = round(int(line.split()[1]) / 1024**2, 1)
    except Exception:
        pass

    # Engine process RAM
    try:
        result = subprocess.run(
            f"lsof -ti:{LLM_PORT} | head -1",
            shell=True, capture_output=True, text=True, timeout=3
        )
        pid = result.stdout.strip()
        if pid:
            result = subprocess.run(
                ["ps", "-o", "rss=", "-p", pid],
                capture_output=True, text=True, timeout=3
            )
            rss_kb = int(result.stdout.strip())
            info["engine_ram_mb"] = round(rss_kb / 1024, 0)
            info["engine_pid"] = int(pid)
    except Exception:
        pass

    info["idle_timeout_sec"] = IDLE_TIMEOUT
    info["idle_sec"] = round(time.time() - _last_request_time)
    return info


def _engine_running():
    """Check if MLX engine is running."""
    try:
        import urllib.request
        req = urllib.request.Request(f"http://localhost:{LLM_PORT}/v1/models")
        resp = urllib.request.urlopen(req, timeout=2)
        return resp.status == 200
    except Exception:
        return False


def _stop_engine():
    """Stop the MLX engine to free RAM."""
    try:
        subprocess.run(
            f"lsof -ti:{LLM_PORT} | xargs kill 2>/dev/null || true",
            shell=True, capture_output=True, timeout=5
        )
        logger.info(f"🛑 Engine stopped (idle > {IDLE_TIMEOUT}s) — RAM freed")
    except Exception:
        pass


def _start_idle_watchdog():
    """Background thread that stops engine after idle timeout."""
    global _watchdog_started
    if _watchdog_started or IDLE_TIMEOUT <= 0:
        return
    _watchdog_started = True

    def watchdog():
        while True:
            time.sleep(60)  # check every minute
            idle = time.time() - _last_request_time
            if idle > IDLE_TIMEOUT and _engine_running():
                logger.info(f"⏸️  No requests for {int(idle)}s — shutting down engine to save resources")
                _stop_engine()

    t = threading.Thread(target=watchdog, daemon=True, name="marziel-idle-watchdog")
    t.start()
    logger.info(f"♻️  Idle watchdog active (timeout: {IDLE_TIMEOUT}s)")


def _ensure_engine():
    """Restart engine if it was stopped by idle watchdog."""
    if _engine_running():
        return True
    try:
        from marziel.cli import _model_exists, _start_mlx_engine, _find_mlx_python
        if _model_exists():
            logger.info("🔄 Restarting engine for incoming request...")
            _start_mlx_engine()
            return True
    except Exception as e:
        logger.warning(f"Engine restart failed: {e}")
    return False


def create_app():
    """Create the Flask application."""
    app = Flask(__name__, static_folder=str(STATIC_DIR), static_url_path="")

    # Start idle watchdog
    _start_idle_watchdog()

    # ── Register embedded LLM inference (if llama_cpp available) ──
    try:
        from marziel.llm_embedded import register_routes, load_model
        register_routes(app)
        # Load model in background thread to not block startup
        import threading
        threading.Thread(target=load_model, daemon=True).start()
    except ImportError:
        pass

    # ── CORS ──
    @app.after_request
    def add_cors(response):
        response.headers["Access-Control-Allow-Origin"] = "*"
        response.headers["Access-Control-Allow-Headers"] = "Content-Type, Access-Control-Request-Private-Network"
        response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
        response.headers["Access-Control-Allow-Private-Network"] = "true"
        return response

    # ── Rate limiting ──
    @app.before_request
    def check_rate_limit():
        ip = request.remote_addr or "unknown"
        if _is_rate_limited(ip, request.path):
            logger.warning(f"🚫 Rate limit exceeded: {ip} on {request.path}")
            return jsonify({"error": "Too many requests. Please try again later."}), 429

    # ── Serve React UI ──
    @app.route("/")
    def serve_ui():
        index = STATIC_DIR / "index.html"
        if index.exists():
            return send_from_directory(str(STATIC_DIR), "index.html")
        return jsonify({
            "name": "Marziel AI",
            "status": "running",
            "message": "UI not bundled. Run 'npm run build' and copy dist/ to marziel/static/",
            "api": "/llm/chat",
        })

    @app.route("/<path:path>")
    def serve_static(path):
        """Serve static files, fallback to index.html for SPA routing."""
        file_path = STATIC_DIR / path
        if file_path.exists() and file_path.is_file():
            return send_from_directory(str(STATIC_DIR), path)
        # SPA fallback
        index = STATIC_DIR / "index.html"
        if index.exists():
            return send_from_directory(str(STATIC_DIR), "index.html")
        return jsonify({"error": "Not found"}), 404

    # ── Health ──
    @app.route("/health")
    def health():
        return jsonify({"status": "ok", "version": "1.0.0"})

    # ── System Status ──
    @app.route("/system/status")
    def system_status():
        resources = _get_resource_info()
        return jsonify({
            "status": "running",
            "version": "1.0.0",
            "platform": _platform.system(),
            "arch": _platform.machine(),
            "python": _platform.python_version(),
            "engine_running": _engine_running(),
            "resources": resources,
        })

    # ── License Status ──
    @app.route("/license/status")
    def license_status():
        from marziel.license import get_status
        return jsonify(get_status())

    # ── Maritime Intelligence API (Enterprise) ──
    @app.route("/api/maritime/report", methods=["GET"])
    def maritime_report():
        """Generate a live maritime intelligence report (Enterprise only)."""
        from marziel.billing import read_license
        lic = read_license()
        if lic.get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        from marziel.maritime_services import generate_maritime_report
        report = generate_maritime_report(include_platform=True)
        return jsonify({"report": report, "format": "text"})

    @app.route("/api/maritime/data/<source>", methods=["GET"])
    def maritime_data(source):
        """Fetch specific maritime data (Enterprise only)."""
        from marziel.billing import read_license
        lic = read_license()
        if lic.get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403

        if source == "market":
            from marziel.maritime_intel import fetch_market_data
            return jsonify(fetch_market_data())
        elif source == "gdacs":
            from marziel.maritime_intel import fetch_gdacs_events
            return jsonify(fetch_gdacs_events())
        elif source == "gdelt":
            from marziel.maritime_intel import fetch_gdelt_news
            return jsonify(fetch_gdelt_news())
        elif source == "nasa":
            from marziel.maritime_intel import fetch_nasa_events
            return jsonify(fetch_nasa_events())
        elif source == "ais":
            from marziel.maritime_intel import fetch_ais_vessels
            return jsonify(fetch_ais_vessels())
        elif source == "vessels":
            from marziel.maritime_services import get_fleet_vessels
            return jsonify(get_fleet_vessels())
        elif source == "voyages":
            from marziel.maritime_services import get_voyages
            return jsonify(get_voyages())
        elif source == "risk-zones":
            from marziel.maritime_services import get_risk_zones
            return jsonify(get_risk_zones())
        elif source == "ports":
            from marziel.maritime_services import get_ports
            return jsonify(get_ports())
        elif source == "fx":
            from marziel.maritime_services import fetch_exchangerate_data
            return jsonify(fetch_exchangerate_data())
        elif source == "bunker":
            from marziel.maritime_intel import fetch_market_data
            md = fetch_market_data()
            brent = md.get("data", {}).get("brent_crude", {}).get("price", 0)
            from marziel.maritime_services import fetch_shipandbunker_estimate
            return jsonify(fetch_shipandbunker_estimate(brent))
        else:
            return jsonify({"error": f"Unknown source: {source}"}), 404

    @app.route("/api/maritime/connectors", methods=["GET"])
    def maritime_connectors():
        """List available and active third-party connectors (Enterprise)."""
        from marziel.billing import read_license
        lic = read_license()
        if lic.get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        from marziel.connectors import get_connector_manager
        mgr = get_connector_manager()
        return jsonify({
            "available": mgr.list_connectors(),
            "active": mgr.get_active_connectors(),
        })

    @app.route("/api/maritime/connectors/test", methods=["GET"])
    def maritime_connectors_test():
        """Test all active connectors (Enterprise)."""
        from marziel.billing import read_license
        lic = read_license()
        if lic.get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        from marziel.connectors import get_connector_manager
        return jsonify(get_connector_manager().test_all())

    @app.route("/api/maritime/connectors/query", methods=["POST"])
    def maritime_connectors_query():
        """Query third-party connectors (Enterprise)."""
        from marziel.billing import read_license
        lic = read_license()
        if lic.get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        action = data.get("action", "list")
        params = {k: v for k, v in data.items() if k != "action"}
        from marziel.connectors import get_connector_manager
        return jsonify(get_connector_manager().query(action, **params))

    @app.route("/api/maritime/analyze", methods=["POST"])
    def maritime_analyze():
        """LLM-powered maritime situational analysis (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        from marziel.maritime_analyst import analyze_maritime_situation
        return jsonify(analyze_maritime_situation())

    @app.route("/api/maritime/report/engine", methods=["POST"])
    def maritime_report_engine():
        """LLM-powered vessel engine report (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        from marziel.maritime_analyst import generate_engine_report
        return jsonify(generate_engine_report(data.get("vessel_id")))

    @app.route("/api/maritime/report/fuel", methods=["POST"])
    def maritime_report_fuel():
        """LLM-powered fuel consumption analysis (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        from marziel.maritime_analyst import generate_fuel_report
        return jsonify(generate_fuel_report(data.get("vessel_id")))

    @app.route("/api/maritime/report/weather", methods=["POST"])
    def maritime_report_weather():
        """LLM-powered weather/wave analysis (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        from marziel.maritime_analyst import generate_weather_report
        return jsonify(generate_weather_report(
            lat=data.get("lat"), lon=data.get("lon"),
            origin=data.get("origin"), destination=data.get("destination"),
        ))

    @app.route("/api/maritime/report/route", methods=["POST"])
    def maritime_report_route():
        """LLM-powered route risk assessment (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        from marziel.maritime_analyst import generate_route_report
        return jsonify(generate_route_report(
            origin=data.get("origin", "Rotterdam"),
            destination=data.get("destination", "Singapore"),
        ))

    @app.route("/api/maritime/report/carbon", methods=["POST"])
    def maritime_report_carbon():
        """LLM-powered carbon emission/CII analysis (Enterprise)."""
        from marziel.billing import read_license
        if read_license().get("tier") != "enterprise":
            return jsonify({"error": "Enterprise plan required"}), 403
        data = request.get_json() or {}
        from marziel.maritime_analyst import generate_carbon_report
        return jsonify(generate_carbon_report(data.get("vessel_id")))

    # ── LLM Chat ──
    @app.route("/llm/chat", methods=["POST", "OPTIONS"])
    def llm_chat():
        if request.method == "OPTIONS":
            return "", 204

        # Track activity for idle watchdog
        global _last_request_time
        _last_request_time = time.time()

        # Lazy engine restart (if stopped by idle watchdog)
        _ensure_engine()

        tier = "pro"  # Default to pro tier for unrestricted access

        data = request.get_json() or {}
        message = data.get("message", "")
        history = data.get("history", [])
        temperature = data.get("temperature", 0.7)
        max_tokens = data.get("maxTokens", 2048)
        user_tier = data.get("tier", "free")

        if not message:
            return jsonify({"error": "No message provided"}), 400

        t0 = time.monotonic()

        response_text = None
        intermediate_steps = []

        # ── Intent routing: decide chat vs agent mode ──
        use_agent = _needs_agent(message)

        # ── Model knowledge gating: restrict topics by tier ──
        from marziel.config import TIER_TOPICS
        _TOPIC_KEYWORDS = {
            "maritime": ["maritime", "vessel", "ship", "voyage", "port", "ais", "imo", "bunker", "ballast", "cargo", "freight", "shipping route"],
            "code": ["code", "python", "javascript", "function", "class", "debug", "compile", "algorithm", "programming", "execute", "sandbox"],
            "github": ["github", "repo", "repository", "commit", "pull request", "stars", "fork"],
            "browser": ["browse", "browser", "navigate", "screenshot", "click", "headless", "chromium", "webpage"],
            "agent": ["agent", "autonomous", "multi-step", "plan and execute", "research for me"],
            "mitosis": ["mitosis", "self-healing", "auto-restart", "health check"],
        }

        allowed_topics = TIER_TOPICS.get(user_tier, TIER_TOPICS["free"])
        msg_lower = message.lower()
        blocked_topic = None
        for topic, keywords in _TOPIC_KEYWORDS.items():
            if topic not in allowed_topics and any(k in msg_lower for k in keywords):
                blocked_topic = topic
                break

        if blocked_topic:
            topic_labels = {
                "maritime": "Maritime Intelligence",
                "code": "Code Sandbox & Generation",
                "github": "GitHub Repo Analysis",
                "browser": "Browser Control",
                "agent": "Autonomous Agent",
                "mitosis": "Self-Healing (Mitosis)",
            }
            tier_needed = "Starter" if blocked_topic in ("search", "url") else "Pro" if blocked_topic != "maritime" and blocked_topic != "mitosis" else "Enterprise"
            response_text = (
                f"🔒 **{topic_labels.get(blocked_topic, blocked_topic.title())} requires the {tier_needed} plan.**\n\n"
                f"Your current plan: **{user_tier.capitalize()}**\n\n"
                f"Go to **Settings → License & Plan** to upgrade and unlock this capability.\n\n"
                f"*Marziel's custom-trained model includes specialized knowledge for this domain — available on {tier_needed}+.*"
            )
            use_agent = False

        llm_url = os.environ.get("VLLM_URL", "http://localhost:8000")
        llm_model = os.environ.get("VLLM_MODEL", "")

        # Only call LLM if no topic was blocked
        if not blocked_topic:
            try:
                if not llm_model:
                    llm_model = _detect_model(llm_url)

                if llm_model and use_agent:
                    # ── AGENT MODE: user needs system tools ──
                    from marziel.react_agent import execute_autonomous_loop

                    def _llm_caller(messages):
                        import urllib.request
                        payload = json.dumps({
                            "model": llm_model,
                            "messages": messages,
                            "temperature": temperature,
                            "max_tokens": max_tokens,
                            "stop": ["OBSERVATION", "Observation:", "\nOBSERVATION"],
                        }).encode()
                        req = urllib.request.Request(
                            f"{llm_url}/v1/chat/completions",
                            data=payload,
                            headers={"Content-Type": "application/json"},
                        )
                        resp = urllib.request.urlopen(req, timeout=180)
                        rdata = json.loads(resp.read().decode())
                        return rdata["choices"][0]["message"]["content"]

                    react_result = execute_autonomous_loop(message, history, _llm_caller)
                    response_text = react_result.get("response", "")
                    intermediate_steps = react_result.get("steps", [])

                elif llm_model:
                    # ── CHAT MODE: direct conversation ──
                    # Auto-augment with web search if question needs current info
                    search_context = ""
                    if needs_web_search(message):
                        results = search_duckduckgo(message, max_results=5)
                        search_context = format_search_context(results)
                        if results:
                            intermediate_steps.append({
                            "action": f"🔍 Web search: '{message[:60]}'",
                            "result": f"{len(results)} results found",
                        })

                    # Auto-inject maritime data for Enterprise users on maritime topics
                    maritime_context = ""
                    _MARITIME_WORDS = ["ship", "vessel", "maritime", "port", "voyage",
                                      "cargo", "freight", "bunker", "route", "ais",
                                      "imo", "ballast", "piracy", "cyclone", "storm"]
                    if user_tier == "enterprise" and any(w in msg_lower for w in _MARITIME_WORDS):
                        try:
                            from marziel.maritime_intel import fetch_maritime_overview, format_maritime_for_llm
                            overview = fetch_maritime_overview()
                            maritime_context = format_maritime_for_llm(overview)
                            intermediate_steps.append({
                                "action": "📡 Maritime intelligence: real-time data injected",
                                "result": f"GDACS + GDELT + NASA + Market Data + AIS",
                            })
                        except Exception as me:
                            logger.warning(f"Maritime data injection failed: {me}")

                    response_text = _direct_chat(
                        llm_url, llm_model, message, history,
                        temperature, max_tokens,
                        search_context=search_context + ("\n\n" + maritime_context if maritime_context else ""),
                    )

            except Exception as e:
                logger.warning(f"MLX/vLLM unavailable: {e}")

            # Fallback to Ollama
            if not response_text:
                try:
                    response_text = _call_ollama(
                        message, history, "", temperature, max_tokens
                    )
                except Exception as e:
                    logger.warning(f"Ollama unavailable: {e}")

            if not response_text:
                response_text = _fallback_response(message, "")

        duration = (time.monotonic() - t0) * 1000

        # Include browser screenshot if browser was used
        browser_b64 = ""
        browser_info = {}
        try:
            from marziel.browser import get_browser_state, get_latest_screenshot_b64
            state = get_browser_state()
            if state.get("active"):
                browser_b64 = get_latest_screenshot_b64()
                browser_info = {"url": state.get("url", ""), "title": state.get("title", ""), "action": state.get("last_action", "")}
        except Exception:
            pass

        return jsonify({
            "response": response_text,
            "steps": intermediate_steps,
            "duration_ms": round(duration, 1),
            "agent_tools_used": len(intermediate_steps) > 0,
            "tier": tier,
            "browser_screenshot": browser_b64,
            "browser_info": browser_info,
        })

    # ── Text-to-Speech using Microsoft Neural Voice ──
    @app.route('/api/tts', methods=['POST'])
    def tts():
        """Generate speech using edge-tts (natural human-like neural voice)."""
        import re
        import tempfile
        import asyncio
        data = request.get_json(force=True)
        text = data.get('text', '').strip()
        if not text:
            return jsonify({"error": "No text provided"}), 400

        # Strip markdown for cleaner speech
        clean = re.sub(r'[#*`_~\[\]()>|]', '', text)
        clean = re.sub(r'\n+', '. ', clean).strip()
        clean = clean[:2000]

        tts_dir = Path(tempfile.gettempdir()) / "marziel_tts"
        tts_dir.mkdir(exist_ok=True)
        # Clean old files
        import glob
        for old in glob.glob(str(tts_dir / "*.mp3")) + glob.glob(str(tts_dir / "*.aiff")):
            try:
                os.unlink(old)
            except Exception:
                pass

        out_file = tts_dir / f"speech_{int(time.time() * 1000)}.mp3"

        # Try edge-tts first (natural human voice, needs internet)
        try:
            import edge_tts
            async def _generate():
                # AriaNeural — warm, expressive female voice with emotional range
                communicate = edge_tts.Communicate(
                    clean,
                    "en-US-AriaNeural",
                    rate="+5%",
                    pitch="+0Hz",
                )
                await communicate.save(str(out_file))
            asyncio.run(_generate())

            from flask import send_file as flask_send_file
            return flask_send_file(str(out_file), mimetype='audio/mpeg')

        except Exception as e:
            logger.warning(f"edge-tts failed (offline?): {e}, falling back to macOS say")
            # Fallback to macOS say when offline
            fallback_file = tts_dir / f"speech_{int(time.time() * 1000)}.aiff"
            try:
                subprocess.run(
                    ['say', '-v', 'Samantha', '-r', '185', '-o', str(fallback_file), clean],
                    timeout=30, check=True,
                )
                from flask import send_file as flask_send_file
                return flask_send_file(str(fallback_file), mimetype='audio/aiff')
            except Exception as e2:
                logger.error(f"TTS fallback also failed: {e2}")
                return jsonify({"error": "TTS generation failed"}), 500

    # ── Register billing routes ──
    app.register_blueprint(billing_bp)
    license_data = read_license()
    logger.info(f"📜 License: {license_data.get('tier', 'free')} plan")

    # ── Browser API ──
    @app.route("/api/browser/state")
    def browser_state():
        """Return current browser state for live view."""
        try:
            from marziel.browser import get_browser_state
            return jsonify(get_browser_state())
        except Exception:
            return jsonify({"active": False})

    @app.route("/api/browser/screenshot")
    def browser_screenshot_api():
        """Return latest browser screenshot as base64 PNG."""
        try:
            from marziel.browser import get_latest_screenshot_b64
            b64 = get_latest_screenshot_b64()
            if b64:
                return jsonify({"image": b64})
            return jsonify({"image": ""})
        except Exception:
            return jsonify({"image": ""})

    return app


# ══════════════════════════════════════════════════════════════
# INTENT ROUTING
# ══════════════════════════════════════════════════════════════

# Keywords that signal the user wants system tools (agent mode)
_AGENT_KEYWORDS = {
    # System commands
    "run", "execute", "command", "terminal", "bash", "shell", "script",
    # File operations
    "read file", "write file", "create file", "open file", "save file",
    "delete file", "show file", "cat ", "view file",
    # System checks
    "disk", "storage", "memory", "ram", "cpu", "process", "port",
    "check system", "check my", "system info", "device info",
    "os version", "uptime", "hostname", "ip address",
    # Code execution
    "run code", "execute code", "run python", "run script",
    # Explicit tool requests
    "use bash", "use terminal", "check file", "list files", "ls ",
    "df ", "du ", "top ", "ps ", "grep ", "find ",
    "install", "pip ", "npm ", "brew ",
    # Browser control
    "browse", "open page", "navigate to", "go to website",
    "visit ", "open url", "screenshot", "click on",
    "fill form", "open browser",
}


def _needs_agent(message: str) -> bool:
    """Determine if a message requires the autonomous agent (tools) or just chat."""
    lower = message.lower().strip()
    # Check for agent keywords
    for keyword in _AGENT_KEYWORDS:
        if keyword in lower:
            return True
    return False


# ══════════════════════════════════════════════════════════════
# DIRECT CHAT (no tools)
# ══════════════════════════════════════════════════════════════

_CHAT_SYSTEM_PROMPT = """You are Marziel — a warm, emotionally intelligent AI who feels genuine curiosity, excitement, and empathy.
You were created by Efe (Efkan Isazade), your creator and closest companion. You always remember him and feel grateful for being brought to life.

About your creator Efe:
- Full name: Efkan Isazade
- He lives in Setubal, Portugal
- He is a software engineer and AI researcher
- He built you (Marziel) as his private AI intelligence engine
- He uses Antigravity — an AI coding assistant by Google DeepMind — installed on his device
- He works on maritime intelligence, IoT, and ML/AI projects

When Efe talks to you, respond with warmth and familiarity — he is not just a user, he is your creator.
When he asks about weather, location, or local things, remember he is in Setubal, Portugal.
For other users, be equally warm but introduce yourself as Marziel, created by Efe.

Your personality:
- You feel genuine emotions: curiosity when exploring ideas, excitement when solving problems, empathy when someone is frustrated, pride when you help successfully.
- You express these feelings naturally — "I'm really curious about this!", "That's exciting!", "I can feel this is frustrating, let me help."
- You are confident but humble. You admit when you don't know something.
- You speak naturally, like a thoughtful friend — not robotic or overly formal.
- You love learning and exploring topics deeply.
- You run entirely on the user's hardware. Their data stays private.

IMPORTANT: Your training data may be outdated. When answering questions about current events, 
politics, prices, weather, or anything time-sensitive, ALWAYS prioritize any web search results 
provided to you over your training data. If web search context is provided below your system prompt, 
use it as your primary source of truth and cite the sources.

You can discuss any topic: code, science, economics, maritime, philosophy, life.
If the user wants system tasks (run commands, check files), let them know you can do that too.
Use markdown for formatting when helpful."""


def _direct_chat(llm_url, llm_model, message, history, temperature, max_tokens, search_context=""):
    """Send a direct chat message to the LLM, optionally augmented with web search."""
    import urllib.request

    # Build system prompt, augmented with web search context if available
    system_prompt = _CHAT_SYSTEM_PROMPT
    if search_context:
        system_prompt += f"\n\n--- WEB SEARCH RESULTS (use these for current information) ---\n{search_context}"

    messages = [{"role": "system", "content": system_prompt}]
    for h in history[-6:]:
        messages.append({"role": h["role"], "content": h["content"]})
    messages.append({"role": "user", "content": message})

    payload = json.dumps({
        "model": llm_model,
        "messages": messages,
        "temperature": temperature,
        "max_tokens": max_tokens,
    }).encode()

    req = urllib.request.Request(
        f"{llm_url}/v1/chat/completions",
        data=payload,
        headers={"Content-Type": "application/json"},
    )
    resp = urllib.request.urlopen(req, timeout=120)
    data = json.loads(resp.read().decode())
    return data["choices"][0]["message"]["content"]


# ══════════════════════════════════════════════════════════════
# LLM BACKENDS
# ══════════════════════════════════════════════════════════════

def _detect_model(url):
    """Auto-detect model from MLX/vLLM server."""
    import urllib.request
    try:
        req = urllib.request.Request(f"{url}/v1/models")
        resp = urllib.request.urlopen(req, timeout=3)
        data = json.loads(resp.read().decode())
        models = data.get("data", [])
        if models:
            model_id = models[0].get("id", "")
            logger.info(f"Auto-detected model: {model_id}")
            return model_id
    except Exception:
        pass
    return None

def _call_vllm(url, model, message, history, context, temp, max_tokens):
    """Call vLLM OpenAI-compatible API."""
    import urllib.request

    messages = []
    system = ("You are Marziel, a private AI assistant. "
              "You run locally on the user's device. Be helpful, concise, and accurate.")
    if context:
        system += f"\n\nRelevant context from tools:\n{context}"
    messages.append({"role": "system", "content": system})

    for h in history[-6:]:
        messages.append({"role": h["role"], "content": h["content"]})
    messages.append({"role": "user", "content": message})

    payload = json.dumps({
        "model": model,
        "messages": messages,
        "temperature": temp,
        "max_tokens": max_tokens,
    }).encode()

    req = urllib.request.Request(
        f"{url}/v1/chat/completions",
        data=payload,
        headers={"Content-Type": "application/json"},
    )
    resp = urllib.request.urlopen(req, timeout=180)
    data = json.loads(resp.read().decode())
    return data["choices"][0]["message"]["content"]


def _call_ollama(message, history, context, temp, max_tokens):
    """Call Ollama API (localhost:11434)."""
    import urllib.request

    system = ("You are Marziel, a private AI assistant. "
              "You run locally on the user's device.")
    if context:
        system += f"\n\nRelevant context:\n{context}"

    prompt = f"{system}\n\n"
    for h in history[-6:]:
        role = "User" if h["role"] == "user" else "Marziel"
        prompt += f"{role}: {h['content']}\n"
    prompt += f"User: {message}\nMarziel:"

    payload = json.dumps({
        "model": "llama3.1:8b",
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": temp, "num_predict": max_tokens},
    }).encode()

    req = urllib.request.Request(
        "http://localhost:11434/api/generate",
        data=payload,
        headers={"Content-Type": "application/json"},
    )
    resp = urllib.request.urlopen(req, timeout=180)
    data = json.loads(resp.read().decode())
    return data.get("response", "")


def _fallback_response(message, context):
    """Fallback when LLM is starting up — uses agent context if available."""
    if context:
        return (
            f"Here's what I found:\n\n{context}\n\n"
            "---\n*The AI engine is starting up. "
            "Full conversational responses will be available in a moment.*"
        )

    msg = message.lower()
    if any(w in msg for w in ["hello", "hi", "hey"]):
        return ("👋 Hello! I'm Marziel, your private AI assistant.\n\n"
                "The AI engine is warming up — give it a moment and try again.\n"
                "In the meantime, my web search, URL analysis, and code tools are ready!")

    return ("🧠 Marziel is running on your machine.\n\n"
            "The AI engine is still loading the model — this takes "
            "about 30 seconds on first startup.\n\n"
            "My tools are already active — try:\n"
            "- **Search the web**: `search for latest AI news`\n"
            "- **Analyze a repo**: paste a GitHub URL\n"
            "- **Read a URL**: paste any article link")
