import socket
import time
import select
from PyQt5.QtCore import QThread, pyqtSignal
from client_state import ClientState

class PhoneClient(QThread):
    data_received = pyqtSignal(bytes, int)
    state_changed = pyqtSignal(str, str, int)

    def __init__(self, client_id):
        super().__init__()
        self.host = "localhost"
        self.port = 12345
        self.client_id = client_id
        self.sock = None
        self.running = True
        self.state = ClientState(client_id)

    def connect_socket(self):
        retries = 3
        for attempt in range(retries):
            try:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                self.sock.settimeout(120)
                self.sock.connect((self.host, self.port))
                print(f"Client {self.client_id} connected to {self.host}:{self.port}")
                return True
            except Exception as e:
                print(f"Client {self.client_id} connection attempt {attempt + 1} failed: {e}")
                if attempt < retries - 1:
                    time.sleep(1)
                self.sock = None
        return False

    def run(self):
        while self.running:
            if not self.sock:
                if not self.connect_socket():
                    print(f"Client {self.client_id} failed to connect after retries")
                    self.state_changed.emit("CALL_END", "", self.client_id)
                    break
            try:
                while self.running:
                    self.state.process_command(self)
                    self.state.check_handshake_timeout(self)
                    if not self.state.handshake_in_progress:
                        if self.sock is None:
                            print(f"Client {self.client_id} socket is None, exiting inner loop")
                            break
                        readable, _, _ = select.select([self.sock], [], [], 0.01)
                        if readable:
                            try:
                                if self.sock is None:
                                    print(f"Client {self.client_id} socket is None before recv, exiting")
                                    break
                                data = self.sock.recv(1024)
                                if not data:
                                    print(f"Client {self.client_id} disconnected")
                                    self.state_changed.emit("CALL_END", "", self.client_id)
                                    break
                                self.state.handle_data(self, data)
                            except socket.error as e:
                                print(f"Client {self.client_id} socket error: {e}")
                                self.state_changed.emit("CALL_END", "", self.client_id)
                                break
                    else:
                        self.msleep(20)
                        print(f"Client {self.client_id} yielding during handshake")
                    self.msleep(1)
            except Exception as e:
                print(f"Client {self.client_id} unexpected error in run loop: {e}")
                self.state_changed.emit("CALL_END", "", self.client_id)
                break
            finally:
                if self.sock:
                    try:
                        self.sock.close()
                    except Exception as e:
                        print(f"Client {self.client_id} error closing socket: {e}")
                    self.sock = None

    def send(self, message):
        if self.sock and self.running:
            try:
                if isinstance(message, str):
                    data = message.encode('utf-8')
                    self.sock.send(data)
                    print(f"Client {self.client_id} sent: {message}, length={len(data)}")
                else:
                    self.sock.send(message)
                    print(f"Client {self.client_id} sent binary data, length={len(message)}")
            except socket.error as e:
                print(f"Client {self.client_id} send error: {e}")
                self.state_changed.emit("CALL_END", "", self.client_id)

    def stop(self):
        self.running = False
        if self.sock:
            try:
                self.sock.close()
            except Exception as e:
                print(f"Client {self.client_id} error closing socket in stop: {e}")
            self.sock = None
        self.quit()
        self.wait(1000)

    def start_handshake(self, initiator, keypair, peer_pubkey):
        self.state.start_handshake(initiator, keypair, peer_pubkey)