package main

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"spektr/internal/config"
	"spektr/internal/discovery"
	grpcclient "spektr/internal/grpc"
	pb "spektr/internal/grpc/pb"
)

func main() {
	configPath := flag.String("config", "configs/config.yaml", "path to config file")
	addrOverride := flag.String("addr", "192.168.1.10:50051", "explicit gRPC address")
	flag.Parse()

	cfg, err := config.Load(*configPath)
	if err != nil {
		log.Fatal(err)
	}

	scanner := bufio.NewScanner(os.Stdin)
	for {
		addr := resolveAddr(cfg, *addrOverride)
		client, err := waitForClient(addr)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println("Подключено к", addr)
		printHelp()

		lostConnection := false
		for {
			fmt.Print("> ")
			if !scanner.Scan() {
				_ = client.Close()
				return
			}

			line := strings.TrimSpace(scanner.Text())
			switch {
			case line == "", line == "help":
				printHelp()
			case line == "exit":
				_ = client.Close()
				return
			case line == "av":
				lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_Av{Av: &pb.AvCommand{}}})
			case line == "get":
				lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_GetFrequency{GetFrequency: &pb.GetFrequencyCommand{}}})
			case strings.HasPrefix(line, "af "):
				value := strings.TrimSpace(strings.TrimPrefix(line, "af "))
				mhz, err := strconv.ParseUint(value, 10, 32)
				if err != nil {
					fmt.Println("invalid frequency")
					continue
				}
				lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_SetFrequency{SetFrequency: &pb.SetFrequencyCommand{Frequency: uint32(mhz)}}})
			default:
				fmt.Println("unknown command")
			}

			if lostConnection {
				_ = client.Close()
				fmt.Println("Соединение потеряно, ожидаю демон...")
				time.Sleep(2 * time.Second)
				break
			}
		}
	}
}

func resolveAddr(cfg config.Config, override string) string {
	if override != "" {
		return override
	}

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	discovered, err := discovery.Discover(ctx, cfg.DiscoveryPort)
	if err == nil && discovered != "" {
		return discovered
	}

	return cfg.GRPCAdvertiseAddr
}

func waitForClient(addr string) (*grpcclient.Client, error) {
	for {
		dialCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		client, err := grpcclient.Dial(dialCtx, addr)
		cancel()
		if err == nil {
			return client, nil
		}

		fmt.Println("Демон недоступен, жду подключение...")
		time.Sleep(2 * time.Second)
	}
}

func printHelp() {
	fmt.Println("Доступные команды:")
	fmt.Println("  av          - проверить связь с устройством")
	fmt.Println("  af <мгц>    - установить частоту, например: af 1880")
	fmt.Println("  get         - показать текущую частоту")
	fmt.Println("  help        - показать список команд")
	fmt.Println("  exit        - выйти из CLI")
}

func sendAndPrint(client *grpcclient.Client, msg *pb.ClientMessage) bool {
	resp, err := client.Send(msg)
	if err != nil {
		fmt.Println("error:", err)
		return false
	}

	switch payload := resp.GetPayload().(type) {
	case *pb.ServerMessage_Av:
		fmt.Printf("HW: %s FW: %s\n", payload.Av.GetHw(), payload.Av.GetSw())
	case *pb.ServerMessage_SetFrequency:
		fmt.Println("ok")
	case *pb.ServerMessage_GetFrequency:
		fmt.Printf("%s\n", formatFrequencyResponse(payload.GetFrequency.GetFrequency()))
	case *pb.ServerMessage_Error:
		fmt.Println("error:", payload.Error.GetMessage())
	default:
		fmt.Println("unknown response")
	}

	return true
}

func requestID() string {
	return strconv.FormatInt(time.Now().UnixNano(), 10)
}

func formatFrequencyResponse(freq uint32) string {
	return fmt.Sprintf("f%07d", freq*100)
}
