package protocol

import (
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"sync"
)

// Handler traite une requête et retourne une réponse.
type Handler func(ctx context.Context, req *Request) *Response

// Server écoute sur un Unix socket et dispatch les requêtes.
type Server struct {
	socketPath string
	handler    Handler
	listener   net.Listener
	wg         sync.WaitGroup
	ctx        context.Context
	cancel     context.CancelFunc
}

// NewServer crée un nouveau serveur.
func NewServer(socketPath string, handler Handler) *Server {
	ctx, cancel := context.WithCancel(context.Background())
	return &Server{
		socketPath: socketPath,
		handler:    handler,
		ctx:        ctx,
		cancel:     cancel,
	}
}

// Start démarre le serveur.
func (s *Server) Start() error {
	// Supprimer le socket existant
	if err := os.RemoveAll(s.socketPath); err != nil {
		return fmt.Errorf("remove socket: %w", err)
	}

	listener, err := net.Listen("unix", s.socketPath)
	if err != nil {
		return fmt.Errorf("listen: %w", err)
	}
	s.listener = listener

	// Permissions socket
	if err := os.Chmod(s.socketPath, 0660); err != nil {
		return fmt.Errorf("chmod socket: %w", err)
	}

	log.Printf("[server] listening on %s", s.socketPath)

	go s.acceptLoop()
	return nil
}

// Stop arrête le serveur proprement.
func (s *Server) Stop() {
	s.cancel()
	if s.listener != nil {
		s.listener.Close()
	}
	s.wg.Wait()
	os.RemoveAll(s.socketPath)
	log.Printf("[server] stopped")
}

// acceptLoop accepte les connexions entrantes.
func (s *Server) acceptLoop() {
	for {
		conn, err := s.listener.Accept()
		if err != nil {
			select {
			case <-s.ctx.Done():
				return
			default:
				log.Printf("[server] accept error: %v", err)
				continue
			}
		}
		s.wg.Add(1)
		go s.handleConn(conn)
	}
}

// handleConn gère une connexion client.
func (s *Server) handleConn(conn net.Conn) {
	defer s.wg.Done()
	defer conn.Close()

	for {
		select {
		case <-s.ctx.Done():
			return
		default:
		}

		// Lire la requête
		data, err := readMessage(conn)
		if err != nil {
			if err != io.EOF {
				log.Printf("[server] read error: %v", err)
			}
			return
		}

		// Décoder la requête
		req, err := DecodeRequest(data)
		if err != nil {
			resp := Failure("", "DECODE_ERROR", err.Error())
			writeResponse(conn, resp)
			continue
		}

		// Traiter la requête
		resp := s.handler(s.ctx, req)

		// Envoyer la réponse
		if err := writeResponse(conn, resp); err != nil {
			log.Printf("[server] write error: %v", err)
			return
		}
	}
}

// readMessage lit un message length-prefixed.
func readMessage(r io.Reader) ([]byte, error) {
	// Lire les 4 bytes de longueur
	lengthBuf := make([]byte, 4)
	if _, err := io.ReadFull(r, lengthBuf); err != nil {
		return nil, err
	}

	length := binary.BigEndian.Uint32(lengthBuf)
	if length == 0 || length > 10*1024*1024 { // Max 10MB
		return nil, fmt.Errorf("invalid message length: %d", length)
	}

	// Lire le payload
	data := make([]byte, length)
	if _, err := io.ReadFull(r, data); err != nil {
		return nil, err
	}

	return data, nil
}

// writeResponse écrit une réponse.
func writeResponse(w io.Writer, resp *Response) error {
	data, err := Encode(resp)
	if err != nil {
		return err
	}
	return writeMessage(w, data)
}

// writeMessage écrit un message length-prefixed.
func writeMessage(w io.Writer, data []byte) error {
	// Écrire la longueur
	lengthBuf := make([]byte, 4)
	binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
	if _, err := w.Write(lengthBuf); err != nil {
		return err
	}

	// Écrire le payload
	_, err := w.Write(data)
	return err
}
