"""Poller — connects to TaskingTech server, polls for actions, executes them."""

import time
import json
import requests
import threading

from .config import load_config, get_os_info
from .executor import execute_actions


class DesktopAgentPoller:
    """Main polling loop for the desktop agent."""

    def __init__(self, config=None):
        self.config = config or load_config()
        self.server_url = self.config["server_url"].rstrip("/")
        self.api_key = self.config["api_key"]
        self.agent_id = self.config["agent_id"]
        self.poll_interval = self.config.get("poll_interval", 0.5)
        self.heartbeat_interval = self.config.get("heartbeat_interval", 15)
        self.running = False
        self._last_heartbeat = 0
        self.on_status = None  # callback(status_str)
        self.on_action = None  # callback(action_type, result)

    def _headers(self):
        return {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }

    def _log(self, msg):
        print(f"[tt-desktop] {msg}")
        if self.on_status:
            self.on_status(msg)

    def send_heartbeat(self):
        """Register/heartbeat with server."""
        try:
            resp = requests.post(
                f"{self.server_url}/_api/desktop/session",
                headers=self._headers(),
                json={
                    "agent_id": self.agent_id,
                    "os_info": get_os_info(),
                },
                timeout=10,
            )
            if resp.status_code == 200:
                self._last_heartbeat = time.time()
                return True
            elif resp.status_code == 401:
                self._log("Authentication failed — check your API key")
                return False
            else:
                body = resp.text[:200] if resp.text else ''
                self._log(f"Heartbeat failed: {resp.status_code} {body}")
                return False
        except requests.RequestException as e:
            self._log(f"Connection error: {e}")
            return False

    def poll_queue(self):
        """Poll for pending actions."""
        try:
            resp = requests.get(
                f"{self.server_url}/_api/desktop/queue",
                headers=self._headers(),
                timeout=10,
            )
            if resp.status_code == 204:
                return None  # No actions pending
            elif resp.status_code == 200:
                return resp.json()
            elif resp.status_code == 401:
                self._log("Authentication failed")
                return None
            else:
                return None
        except requests.RequestException:
            return None

    def submit_result(self, queue_id, results, status="completed"):
        """Submit action results back to server."""
        try:
            resp = requests.post(
                f"{self.server_url}/_api/desktop/result",
                headers=self._headers(),
                json={
                    "id": queue_id,
                    "result": json.dumps(results),
                    "status": status,
                },
                timeout=15,
            )
            return resp.status_code == 200
        except requests.RequestException as e:
            self._log(f"Failed to submit result: {e}")
            return False

    def disconnect(self):
        """Disconnect from server."""
        try:
            requests.delete(
                f"{self.server_url}/_api/desktop/session",
                headers=self._headers(),
                json={"agent_id": self.agent_id},
                timeout=5,
            )
        except Exception:
            pass

    def run(self):
        """Main polling loop. Blocks until self.running is set to False."""
        self.running = True
        self._log("Starting desktop agent...")

        # Initial heartbeat
        if not self.send_heartbeat():
            self._log("Failed to connect. Check your API key and server URL.")
            self.running = False
            return

        self._log(f"Connected to {self.server_url}")
        self._log("Waiting for actions...")

        try:
            while self.running:
                # Heartbeat every N seconds
                if time.time() - self._last_heartbeat > self.heartbeat_interval:
                    self.send_heartbeat()

                # Poll for actions
                action_data = self.poll_queue()

                if action_data:
                    queue_id = action_data.get("id")
                    actions = action_data.get("actions", [])
                    task_id = action_data.get("task_id")

                    self._log(f"Received {len(actions)} action(s)" + (f" [task: {task_id}]" if task_id else ""))

                    # Execute actions
                    try:
                        results = execute_actions(actions)
                        all_ok = all(r.get("success", False) for r in results)
                        self.submit_result(queue_id, results, "completed" if all_ok else "failed")

                        for r in results:
                            action_type = r.get("action_type", "?")
                            ok = r.get("success", False)
                            self._log(f"  {'✓' if ok else '✗'} {action_type}")
                            if self.on_action:
                                self.on_action(action_type, r)
                    except Exception as e:
                        self._log(f"Execution error: {e}")
                        self.submit_result(queue_id, [{"success": False, "error": str(e)}], "failed")

                    # After executing, poll again immediately — there may be more
                    continue

                time.sleep(self.poll_interval)

        except KeyboardInterrupt:
            self._log("Interrupted by user")
        finally:
            self.running = False
            self.disconnect()
            self._log("Disconnected")

    def run_in_background(self):
        """Start polling in a background thread."""
        thread = threading.Thread(target=self.run, daemon=True)
        thread.start()
        return thread
