"""
Manages the trading bot process from the web panel.
The bot runs as a subprocess; a PID file tracks whether it's alive.
"""
import json
import logging
import os
import signal
import subprocess
import sys
import time
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

PID_FILE = "data/bot.pid"
LOG_FILE = "data/bot.log"
STATE_FILE = "data/state.json"
TRADES_FILE = "data/trades.json"
CONFIG_FILE = "data/bot_config_override.json"


def _write_pid(pid: int):
    os.makedirs("data", exist_ok=True)
    Path(PID_FILE).write_text(str(pid))


def _read_pid() -> Optional[int]:
    try:
        return int(Path(PID_FILE).read_text().strip())
    except Exception:
        return None


def _process_alive(pid: int) -> bool:
    try:
        os.kill(pid, 0)
        return True
    except (ProcessLookupError, PermissionError, OSError):
        return False


def is_running() -> bool:
    pid = _read_pid()
    return pid is not None and _process_alive(pid)


def start_bot(demo: bool = True) -> dict:
    if is_running():
        return {"ok": False, "msg": "Bot is already running"}

    env = os.environ.copy()
    env["DEMO_MODE"] = "true" if demo else "false"

    try:
        proc = subprocess.Popen(
            [sys.executable, "main.py"],
            cwd=os.getcwd(),
            env=env,
            stdout=open(LOG_FILE, "a"),
            stderr=subprocess.STDOUT,
            start_new_session=True,
        )
        _write_pid(proc.pid)
        time.sleep(1)
        if _process_alive(proc.pid):
            return {"ok": True, "msg": f"Bot started (PID {proc.pid}, demo={demo})"}
        else:
            return {"ok": False, "msg": "Bot process exited immediately — check logs"}
    except Exception as e:
        return {"ok": False, "msg": str(e)}


def stop_bot() -> dict:
    pid = _read_pid()
    if pid is None or not _process_alive(pid):
        try:
            Path(PID_FILE).unlink(missing_ok=True)
        except Exception:
            pass
        return {"ok": False, "msg": "Bot is not running"}
    try:
        os.kill(pid, signal.SIGTERM)
        for _ in range(10):
            time.sleep(0.5)
            if not _process_alive(pid):
                break
        else:
            os.kill(pid, signal.SIGKILL)
        Path(PID_FILE).unlink(missing_ok=True)
        return {"ok": True, "msg": f"Bot stopped (PID {pid})"}
    except Exception as e:
        return {"ok": False, "msg": str(e)}


def get_state() -> dict:
    try:
        with open(STATE_FILE) as f:
            return json.load(f)
    except Exception:
        return {}


def get_trades() -> list:
    try:
        with open(TRADES_FILE) as f:
            return json.load(f)
    except Exception:
        return []


def get_log_tail(lines: int = 100) -> list:
    try:
        with open(LOG_FILE, "r", encoding="utf-8", errors="replace") as f:
            all_lines = f.readlines()
        return [l.rstrip() for l in all_lines[-lines:]]
    except Exception:
        return []


def get_config_override() -> dict:
    try:
        with open(CONFIG_FILE) as f:
            return json.load(f)
    except Exception:
        return {}


def save_config_override(data: dict):
    os.makedirs("data", exist_ok=True)
    with open(CONFIG_FILE, "w") as f:
        json.dump(data, f, indent=2)
