package service

import (
    "encoding/json"
    "net"
    "strings"
    "time"

	"safesight-control/internal/config"
	"safesight-control/internal/models"
	"github.com/google/uuid"
)

const discoveryMagicV1 = "RK3588SYS_DISCOVERY_V1"

type DiscoveryService struct {
    cfg      *config.Config
    registry *RegistryService
}

func NewDiscoveryService(cfg *config.Config, registry *RegistryService) *DiscoveryService {
    return &DiscoveryService{
        cfg:      cfg,
        registry: registry,
    }
}

func (s *DiscoveryService) SearchDefault() ([]*models.Device, error) {
    if s == nil || s.cfg == nil {
        return nil, nil
    }
    timeoutMs := s.cfg.DiscoveryTimeoutMs
    if timeoutMs <= 0 {
        timeoutMs = 1200
    }
    return s.Search(timeoutMs)
}

func (s *DiscoveryService) Search(timeoutMs int) ([]*models.Device, error) {
    reqID := uuid.NewString()
    payload, _ := json.Marshal(map[string]interface{}{
        "type":       "discover",
        "req_id":     reqID,
        "reply_port": 0,
    })
    msg := []byte(discoveryMagicV1 + "\n" + string(payload))

    // Listen for replies
    addr, err := net.ResolveUDPAddr("udp", ":0") // Random port for receiving
    if err != nil {
        return nil, err
    }
    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        return nil, err
    }
    defer conn.Close()

    // Send Broadcast to all interfaces
    interfaces, err := net.Interfaces()
    if err == nil {
        for _, iface := range interfaces {
            if iface.Flags&net.FlagBroadcast != 0 && iface.Flags&net.FlagUp != 0 {
                addrs, err := iface.Addrs()
                if err != nil {
                    continue
                }
                for _, addr := range addrs {
                    if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.To4() != nil {
                        // Calculate broadcast address
                        ip := ipnet.IP.To4()
                        mask := ipnet.Mask
                        broadcast := make(net.IP, len(ip))
                        for i := 0; i < len(ip); i++ {
                            broadcast[i] = ip[i] | ^mask[i]
                        }
                        broadcastAddr := &net.UDPAddr{
                            IP:   broadcast,
                            Port: s.cfg.DiscoveryPort,
                        }
                        _, _ = conn.WriteToUDP(msg, broadcastAddr)
                    }
                }
            }
        }
    }

    stop := time.After(time.Duration(timeoutMs) * time.Millisecond)
    found := make(map[string]*models.Device)

    for {
        select {
        case <-stop:
            // Convert map to slice
            list := make([]*models.Device, 0, len(found))
            for _, d := range found {
                list = append(list, d)
            }
            return list, nil
        default:
            conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
            buf := make([]byte, 2048)
            n, raddr, err := conn.ReadFromUDP(buf)
            if err != nil {
                continue
            }

            text := strings.TrimSpace(string(buf[:n]))
            lines := strings.SplitN(text, "\n", 3)
            if len(lines) < 2 {
                continue
            }
            if strings.TrimSpace(lines[0]) != discoveryMagicV1 {
                continue
            }

            var reply struct {
                Type                string   `json:"type"`
                ReqID               string   `json:"req_id"`
                DeviceID            string   `json:"device_id"`
                DeviceName          string   `json:"device_name"`
                Hostname            string   `json:"hostname"`
                IP                  string   `json:"ip"`
                AgentPort           int      `json:"agent_port"`
                MediaPort           int      `json:"media_port"`
                Version             string   `json:"version"`
                BuildID             string   `json:"build_id"`
                GitSha              string   `json:"git_sha"`
                UptimeSec           int64    `json:"uptime_sec"`
                InstanceName        string   `json:"instance_name"`
                InstanceDisplayName string   `json:"instance_display_name"`
                ConfigID            string   `json:"config_id"`
                ConfigVersion       string   `json:"config_version"`
                Template            string   `json:"template"`
                Profile             string   `json:"profile"`
                Overlays            []string `json:"overlays"`
            }
            if err := json.Unmarshal([]byte(strings.TrimSpace(lines[1])), &reply); err != nil {
                continue
            }
            if reply.Type != "discover_reply" || reply.ReqID != reqID || reply.DeviceID == "" {
                continue
            }

            dev := &models.Device{
                DeviceID:            reply.DeviceID,
                DeviceName:          reply.DeviceName,
                Hostname:            reply.Hostname,
                IP:                  reply.IP,
                AgentPort:           reply.AgentPort,
                MediaPort:           reply.MediaPort,
                Version:             reply.Version,
                BuildID:             reply.BuildID,
                GitSha:              reply.GitSha,
                UptimeSec:           reply.UptimeSec,
                InstanceName:        reply.InstanceName,
                InstanceDisplayName: reply.InstanceDisplayName,
            }
            if dev.IP == "" {
                dev.IP = raddr.IP.String()
            }

            s.registry.UpdateDevice(dev)
            found[dev.DeviceID] = dev
        }
    }
}
