from __future__ import annotations

import socket
from typing import Final
from construct import ConstructError
from api import Request
from secret_stream_socket import ProtocolError, wrap_connection_socket

SERVER_HOST: Final = "127.0.0.1"
SERVER_PORT: Final = 9000
SHARED_SECRET: Final = "change-me"


def print_batch(peer: str, request_id: int, batch: object) -> None:
    print(
        f"[packet] {peer} sent request_id={request_id} with {len(batch.messages)} message(s)",
        flush=True,
    )
    for index, message in enumerate(batch.messages):
        print(
            f"  [{index}] role={message.role!r} content={message.content!r}",
            flush=True,
        )


def handle_client(
    client_sock: socket.socket,
    address: tuple[str, int],
    shared_secret: str,
) -> None:
    peer = f"{address[0]}:{address[1]}"

    def on_frame(frame: bytes) -> None:
        req = Request.parse(frame)
        if req.kind == "chat":
            print_batch(peer, req.request_id, req.payload)
        elif req.kind == "cancel":
            print(f"[packet] {peer} sent request_id={req.request_id} cancel", flush=True)
        else:
            raise ConstructError(f"Unknown request kind {req.kind!r}")

    with wrap_connection_socket(client_sock, shared_secret) as transport:
        print(f"[connected] {peer}", flush=True)
        transport.run_receiving_loop(on_frame)
        print(f"[disconnected] {peer}", flush=True)


def run_server(host: str, port: int, shared_secret: str) -> None:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_sock:
        server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_sock.bind((host, port))
        server_sock.listen()

        print(f"[listening] {host}:{port}", flush=True)
        while True:
            client_sock, address = server_sock.accept()
            try:
                handle_client(client_sock, address, shared_secret)
            except (ConstructError, ProtocolError) as exc:
                print(f"[protocol error] {address[0]}:{address[1]}: {exc}", flush=True)
            except Exception as exc:
                print(f"[error] {address[0]}:{address[1]}: {exc}", flush=True)


if __name__ == "__main__":
    try:
        run_server(SERVER_HOST, SERVER_PORT, SHARED_SECRET)
    except KeyboardInterrupt:
        print("\n[stopped]", flush=True)
