package service

import (
	"context"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"sync"

	pb "spektr/internal/grpc/pb"
	"spektr/internal/uart"
)

type Service struct {
	mu       sync.Mutex
	uart     *uart.UART
	lastFreq uint32
}

var (
	versionRe = regexp.MustCompile(`(?i)HW[:=]\s*([^\s]+).*?(?:FW|SW)[:=]\s*([^\s]+)`)
	freqRe    = regexp.MustCompile(`(?i)\bf\d{7,9}\b`)
)

func New(u *uart.UART) *Service {
	return &Service{uart: u}
}

func (s *Service) Handle(ctx context.Context, msg *pb.ClientMessage) *pb.ServerMessage {
	_ = ctx

	switch payload := msg.GetPayload().(type) {
	case *pb.ClientMessage_Av:
		return s.handleAV(msg.GetRequestId())
	case *pb.ClientMessage_SetFrequency:
		return s.handleSetFrequency(msg.GetRequestId(), payload.SetFrequency.GetFrequency())
	case *pb.ClientMessage_GetFrequency:
		return s.handleGetFrequency(msg.GetRequestId())
	default:
		return errorMessage(msg.GetRequestId(), "unknown command")
	}
}

func (s *Service) handleAV(requestID string) *pb.ServerMessage {
	s.mu.Lock()
	defer s.mu.Unlock()

	line, err := s.uart.Exec("av")
	if err != nil {
		log.Printf("uart av error: %v", err)
		return errorMessage(requestID, err.Error())
	}
	log.Printf("uart av raw: %q", line)

	hw, sw := parseVersion(line)
	if hw == "" && sw == "" {
		hw = line
	}
	return &pb.ServerMessage{
		RequestId: requestID,
		Payload: &pb.ServerMessage_Av{
			Av: &pb.AvResponse{Ok: true, Hw: hw, Sw: sw},
		},
	}
}

func (s *Service) handleSetFrequency(requestID string, frequency uint32) *pb.ServerMessage {
	s.mu.Lock()
	defer s.mu.Unlock()

	cmd := encodeFrequencyCommand(frequency)
	if err := s.uart.Write(cmd); err != nil {
		log.Printf("uart af write error: %v", err)
		return errorMessage(requestID, err.Error())
	}
	log.Printf("uart af sent: %q", cmd)

	s.lastFreq = frequency

	return &pb.ServerMessage{
		RequestId: requestID,
		Payload: &pb.ServerMessage_SetFrequency{
			SetFrequency: &pb.SetFrequencyResponse{Ok: true},
		},
	}
}

func (s *Service) handleGetFrequency(requestID string) *pb.ServerMessage {
	s.mu.Lock()
	defer s.mu.Unlock()

	line, err := s.uart.Exec("ar")
	if err != nil {
		log.Printf("uart ar error: %v", err)
		return errorMessage(requestID, err.Error())
	}
	log.Printf("uart ar raw: %q", line)

	frequency, err := decodeFrequencyResponse(line)
	if err != nil {
		return errorMessage(requestID, err.Error())
	}

	s.lastFreq = frequency
	return &pb.ServerMessage{
		RequestId: requestID,
		Payload: &pb.ServerMessage_GetFrequency{
			GetFrequency: &pb.GetFrequencyResponse{Frequency: frequency},
		},
	}
}

func errorMessage(requestID, message string) *pb.ServerMessage {
	return &pb.ServerMessage{
		RequestId: requestID,
		Payload: &pb.ServerMessage_Error{
			Error: &pb.Error{Message: message},
		},
	}
}

func parseVersion(line string) (string, string) {
	match := versionRe.FindStringSubmatch(line)
	if len(match) == 3 {
		return match[1], match[2]
	}
	return "", ""
}

func encodeFrequencyCommand(frequency uint32) string {
	return fmt.Sprintf("af%07d", frequency*100)
}

func decodeFrequencyResponse(line string) (uint32, error) {
	match := freqRe.FindString(line)
	if match == "" {
		return 0, fmt.Errorf("invalid frequency response %q", line)
	}

	value, err := strconv.ParseUint(match[1:], 10, 32)
	if err != nil {
		return 0, fmt.Errorf("invalid frequency response %q: %w", line, err)
	}

	return uint32(value / 100), nil
}
