from abc import ABC, abstractmethod
from typing import List
from .core import TaskRequest, TaskResult, send_msg, recv_msg
import socket
import requests
import pickle
from .worker import BaseWorker


class Device(ABC):
    @abstractmethod
    def run_task(self, task: TaskRequest) -> TaskResult:
        pass


class LocalDevice(Device):
    def __init__(self, command: str = 'python'):
        self.command = command

    def run_task(self, task: TaskRequest) -> TaskResult:
        worker = BaseWorker()
        worker.base_command = self.command
        return worker.process_task(task)


class RemoteDevice(Device):
    def __init__(self, ip, port, password):
        self.ip = ip
        self.port = port
        self.password = password

    def run_task(self, task: TaskRequest) -> TaskResult:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((self.ip, self.port))

        send_msg(s, self.password)
        resp = recv_msg(s)
        if resp != "OK":
            raise Exception("Auth failed")

        send_msg(s, task)

        result = recv_msg(s)
        s.close()
        return result


class CenterDevices(Device):
    def __init__(self, ip, port, password, required_device_count):
        self.url = f"http://{ip}:{port}/run_proxy"
        self.password = password
        self.count = required_device_count
        self._expanded = False

    def expand(self) -> List['CenterDevices']:
        return [self] * self.count

    def run_task(self, task: TaskRequest) -> TaskResult:
        data = pickle.dumps(task)
        resp = requests.post(self.url, data=data)
        if resp.status_code == 200:
            return pickle.loads(resp.content)
        else:
            return TaskResult(task.task_id, False, "", f"HTTP Error {resp.status_code}")