from __future__ import annotations

import json
import os
import platform
import socket
import urllib.parse
import urllib.request
from typing import Any, Dict, Optional

from .base import BaseTool, ToolType


class WeatherTool(BaseTool):
    """Example tool: fetch simple weather info from wttr.in."""

    name = "get_weather"
    description = "Get current weather for a city/location."

    @property
    def tool_type(self) -> ToolType:
        return ToolType.SYNC

    @property
    def schema(self) -> Dict[str, Any]:
        return self.get_schema()

    @classmethod
    def get_schema(cls) -> Dict[str, Any]:
        return {
            "type": "function",
            "function": {
                "name": cls.name,
                "description": cls.description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {"type": "string", "description": "City or location, e.g. San Francisco"},
                        "lang": {"type": "string", "description": "Language code for weather text, default en"},
                    },
                    "required": ["location"],
                },
            },
        }

    @classmethod
    def validate_arguments(cls, args: Dict[str, Any]) -> Optional[str]:
        loc = args.get("location")
        if not isinstance(loc, str) or not loc.strip():
            return "location must be a non-empty string"
        lang = args.get("lang")
        if lang is not None and not isinstance(lang, str):
            return "lang must be a string"
        return None

    @classmethod
    def get_preview(cls, args: Dict[str, Any]) -> Optional[str]:
        loc = str(args.get("location") or "").strip()
        if not loc:
            return None
        return f"Fetch weather for: {loc}"

    @staticmethod
    def execute(**kwargs) -> Any:
        location = str(kwargs.get("location") or "").strip()
        lang = str(kwargs.get("lang") or "en").strip() or "en"

        q = urllib.parse.quote(location)
        url = f"https://wttr.in/{q}?format=j1&lang={urllib.parse.quote(lang)}"
        req = urllib.request.Request(url, headers={"User-Agent": "oturn-weather/0.1"})
        with urllib.request.urlopen(req, timeout=10) as resp:
            raw = resp.read().decode("utf-8", errors="replace")

        data = json.loads(raw)
        cur = (data.get("current_condition") or [{}])[0]
        weather_desc = ""
        if isinstance(cur.get("weatherDesc"), list) and cur["weatherDesc"]:
            weather_desc = str(cur["weatherDesc"][0].get("value") or "")

        return {
            "location": location,
            "temperature_c": cur.get("temp_C"),
            "feels_like_c": cur.get("FeelsLikeC"),
            "humidity": cur.get("humidity"),
            "wind_kmph": cur.get("windspeedKmph"),
            "description": weather_desc,
            "source": "wttr.in",
        }


class HostConfigTool(BaseTool):
    """Example tool: inspect local host/runtime configuration."""

    name = "get_host_config"
    description = "Return local host/runtime configuration details."

    @property
    def tool_type(self) -> ToolType:
        return ToolType.SYNC

    @property
    def schema(self) -> Dict[str, Any]:
        return self.get_schema()

    @classmethod
    def get_schema(cls) -> Dict[str, Any]:
        return {
            "type": "function",
            "function": {
                "name": cls.name,
                "description": cls.description,
                "parameters": {
                    "type": "object",
                    "properties": {
                        "include_env": {
                            "type": "boolean",
                            "description": "Whether to include selected env vars (HOME, SHELL, USER).",
                        }
                    },
                    "required": [],
                },
            },
        }

    @classmethod
    def validate_arguments(cls, args: Dict[str, Any]) -> Optional[str]:
        v = args.get("include_env")
        if v is not None and not isinstance(v, bool):
            return "include_env must be a boolean"
        return None

    @classmethod
    def get_preview(cls, args: Dict[str, Any]) -> Optional[str]:
        include_env = bool(args.get("include_env", False))
        return f"Read host config (include_env={include_env})"

    @staticmethod
    def execute(**kwargs) -> Any:
        include_env = bool(kwargs.get("include_env", False))

        try:
            loadavg = os.getloadavg()
        except Exception:
            loadavg = None

        info: Dict[str, Any] = {
            "hostname": socket.gethostname(),
            "fqdn": socket.getfqdn(),
            "platform": platform.platform(),
            "system": platform.system(),
            "release": platform.release(),
            "machine": platform.machine(),
            "processor": platform.processor(),
            "python_version": platform.python_version(),
            "cpu_count_logical": os.cpu_count(),
            "loadavg": loadavg,
        }

        # Best-effort physical core count on Linux
        physical_cores = None
        try:
            if platform.system().lower() == "linux" and os.path.exists("/proc/cpuinfo"):
                phys_core_pairs = set()
                physical_id = None
                core_id = None
                with open("/proc/cpuinfo", "r", encoding="utf-8", errors="ignore") as f:
                    for line in f:
                        line = line.strip()
                        if line.startswith("physical id"):
                            physical_id = line.split(":", 1)[1].strip()
                        elif line.startswith("core id"):
                            core_id = line.split(":", 1)[1].strip()
                        elif not line:
                            if physical_id is not None and core_id is not None:
                                phys_core_pairs.add((physical_id, core_id))
                            physical_id = None
                            core_id = None
                if phys_core_pairs:
                    physical_cores = len(phys_core_pairs)
        except Exception:
            physical_cores = None

        info["cpu_count_physical"] = physical_cores

        if include_env:
            info["env"] = {
                "HOME": os.environ.get("HOME"),
                "SHELL": os.environ.get("SHELL"),
                "USER": os.environ.get("USER"),
            }

        return info


EXAMPLE_TOOLS = [WeatherTool, HostConfigTool]
