"""Клиент API — программа на ПК общается с ботом на сервере."""

from __future__ import annotations

import json
import secrets
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import requests

from paths import get_config_path
from panel_defaults import DEFAULT_PANEL_HOST, DEFAULT_PANEL_PORT


CONFIG_PATH = get_config_path()


@dataclass
class ConnectionConfig:
    host: str = DEFAULT_PANEL_HOST
    port: int = DEFAULT_PANEL_PORT
    token: str = ""
    radar_session: str = ""
    guest_device_id: str = ""
    telegram_bot_username: str = ""
    notify_sound_enabled: bool = True
    admin_capable: bool = False

    @property
    def base_url(self) -> str:
        return f"http://{self.host}:{self.port}"

    def save(self):
        CONFIG_PATH.write_text(
            json.dumps(
                {
                    "host": self.host,
                    "port": self.port,
                    "token": self.token,
                    "radar_session": self.radar_session,
                    "guest_device_id": self.guest_device_id,
                    "telegram_bot_username": self.telegram_bot_username,
                    "notify_sound_enabled": self.notify_sound_enabled,
                    "admin_capable": self.admin_capable,
                },
                ensure_ascii=False,
                indent=2,
            ),
            encoding="utf-8",
        )

    @classmethod
    def load(cls) -> "ConnectionConfig":
        if not CONFIG_PATH.exists():
            cfg = cls()
            cfg.guest_device_id = secrets.token_hex(8)
            cfg.save()
            return cfg
        try:
            data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
            cfg = cls(
                host=str(data.get("host") or DEFAULT_PANEL_HOST),
                port=int(data.get("port") or DEFAULT_PANEL_PORT),
                token=str(data.get("token", "")),
                radar_session=str(data.get("radar_session", "")),
                guest_device_id=str(data.get("guest_device_id", "")).strip(),
                telegram_bot_username=str(data.get("telegram_bot_username", "")),
                notify_sound_enabled=bool(data.get("notify_sound_enabled", True)),
                admin_capable=bool(data.get("admin_capable", False)),
            )
            if not cfg.guest_device_id:
                cfg.guest_device_id = secrets.token_hex(8)
                cfg.save()
            return cfg
        except Exception:
            cfg = cls()
            cfg.guest_device_id = secrets.token_hex(8)
            cfg.save()
            return cfg


class BotApiClient:
    def __init__(self, config: ConnectionConfig):
        self.config = config
        self.session = requests.Session()
        self.session.headers.update({"X-Admin-Token": config.token})
        self.timeout = 20

    def set_radar_session(self, token: str):
        self.config.radar_session = str(token or "")

    def _url(self, path: str) -> str:
        return f"{self.config.base_url}{path}"

    def _request(self, method: str, path: str, timeout: int | None = None, **kwargs) -> Any:
        t = timeout if timeout is not None else self.timeout
        resp = self.session.request(method, self._url(path), timeout=t, **kwargs)
        resp.raise_for_status()
        if resp.content:
            return resp.json()
        return {}

    @staticmethod
    def _radar_error_message(resp: requests.Response) -> str:
        err_msg = ""
        try:
            payload = resp.json()
            err_msg = str(payload.get("error") or payload.get("message") or "").strip()
        except Exception:
            pass

        code = resp.status_code
        if code == 429 or err_msg in {
            "Daily analyze limit reached",
            "Лимит Анализера на сегодня исчерпан",
        }:
            return (
                "Ваши попытки Анализера на сегодня закончились. "
                "Завтра лимит обновится, или войди с community-подпиской."
            )
        if code == 401:
            if err_msg in ("Unauthorized", ""):
                return "Сессия недействительна. Зайди в профиль и войди снова."
            return err_msg or "Не авторизован"
        if code == 403:
            return err_msg or "Нет доступа к этой функции"
        if code == 400:
            if err_msg:
                return err_msg
            return "Неверный запрос к серверу"
        if code == 502:
            return err_msg or "Сервер не смог получить данные с биржи. Попробуй позже."
        if err_msg:
            return err_msg
        return f"Ошибка сервера ({code})"

    def _radar_request(self, method: str, path: str, timeout: int | None = None, **kwargs) -> Any:
        headers = dict(kwargs.pop("headers", {}) or {})
        headers["X-Radar-Session"] = self.config.radar_session or ""
        t = timeout if timeout is not None else self.timeout
        resp = self.session.request(method, self._url(path), timeout=t, headers=headers, **kwargs)
        if resp.status_code >= 400:
            raise RuntimeError(self._radar_error_message(resp))
        if resp.content:
            return resp.json()
        return {}

    def health(self, timeout: int | None = None) -> dict:
        return self._request("GET", "/api/health", timeout=timeout)

    def status(self) -> dict:
        return self._request("GET", "/api/status")

    def get_settings(self) -> dict:
        return self._request("GET", "/api/settings")

    def update_settings(self, payload: dict) -> dict:
        return self._request("PUT", "/api/settings", json=payload)

    def ban_list(self) -> list:
        return self._request("GET", "/api/ban-list")

    def add_ban(self, symbol: str, reason: str = "") -> dict:
        return self._request("POST", "/api/ban-list", json={"symbol": symbol, "reason": reason})

    def remove_ban(self, symbol: str) -> dict:
        return self._request("DELETE", f"/api/ban-list/{symbol.upper()}")

    def signals(self, hours: int = 168, limit: int = 200) -> list:
        data = self._request("GET", f"/api/signals?hours={hours}&limit={limit}")
        return data.get("items", [])

    def restart(self) -> dict:
        return self._request("POST", "/api/restart")

    def deploy_manifest(self) -> dict:
        return self._request("GET", "/api/deploy/manifest")

    def deploy_files(self, files: list) -> dict:
        return self._request("POST", "/api/deploy/files", json={"files": files}, timeout=120)

    def logs(self, lines: int = 200) -> str:
        data = self._request("GET", f"/api/logs?lines={lines}")
        return data.get("text", "")

    def tracking(self) -> list:
        data = self._request("GET", "/api/tracking")
        return data.get("items", [])

    def set_tracking(self, signal_id: str, enabled: bool) -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        return self._request("PUT", f"/api/tracking/{sid}", json={"enabled": enabled})

    def tracking_detail(self, signal_id: str) -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        return self._request("GET", f"/api/tracking/{sid}")

    def tracking_chart(self, signal_id: str, timeframe: str = "1m") -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        tf = quote(str(timeframe or "1m"), safe="")
        return self._request("GET", f"/api/tracking/{sid}/chart?tf={tf}")

    def tracking_live(self, signal_id: str) -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        return self._request("GET", f"/api/tracking/{sid}/live")

    def radar_auth_guest(self) -> dict:
        return self._radar_request(
            "POST",
            "/api/radar/auth/guest",
            json={"device_id": self.config.guest_device_id or ""},
        )

    def radar_auth_telegram(self, payload: dict) -> dict:
        return self._radar_request("POST", "/api/radar/auth/telegram", json=payload)

    def radar_auth_site_link(self, link: str) -> dict:
        return self._request("POST", "/api/radar/auth/site-link", json={"link": str(link or "").strip()})

    def radar_me(self) -> dict:
        return self._radar_request("GET", "/api/radar/me")

    def radar_refresh(self) -> dict:
        return self._radar_request("POST", "/api/radar/auth/refresh", json={})

    def radar_analyze(self, query: str) -> dict:
        q = str(query or "").strip().lower().lstrip("/")
        timeout = 360 if q in {"top", "topcoins"} else 120
        return self._radar_request("POST", "/api/radar/analyze", json={"query": query}, timeout=timeout)

    def radar_ban_list(self) -> dict:
        return self._radar_request("GET", "/api/radar/ban-list")

    def radar_tracking(self) -> list:
        data = self._radar_request("GET", "/api/radar/tracking")
        return data.get("items", [])

    def radar_signals(self, hours: int = 168, limit: int = 200) -> list:
        data = self._radar_request("GET", f"/api/radar/signals?hours={hours}&limit={limit}")
        return data.get("items", [])

    def radar_signals_outcomes(self, hours: int = 168, limit: int = 200) -> list:
        data = self._radar_request(
            "GET", f"/api/radar/signals/outcomes?hours={hours}&limit={limit}"
        )
        return data.get("items", [])

    def radar_tracking_detail(self, signal_id: str) -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        return self._radar_request("GET", f"/api/radar/tracking/{sid}")

    def radar_tracking_chart(self, signal_id: str, timeframe: str = "1m") -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        tf = quote(str(timeframe or "1m"), safe="")
        return self._radar_request("GET", f"/api/radar/tracking/{sid}/chart?tf={tf}")

    def radar_tracking_live(self, signal_id: str) -> dict:
        from urllib.parse import quote

        sid = quote(str(signal_id), safe="")
        return self._radar_request("GET", f"/api/radar/tracking/{sid}/live")
