package discovery

import (
	"context"
	"testing"
	"time"
)

func TestNewMDNSDiscoveryService(t *testing.T) {
	// Test with custom timeout
	service := NewMDNSDiscoveryService(10 * time.Second)
	if service.timeout != 10*time.Second {
		t.Errorf("Expected timeout 10s, got %v", service.timeout)
	}

	// Test with zero timeout (should use default)
	service = NewMDNSDiscoveryService(0)
	if service.timeout != defaultTimeout {
		t.Errorf("Expected default timeout %v, got %v", defaultTimeout, service.timeout)
	}
}

func TestMDNSDiscoverDevices(t *testing.T) {
	service := NewMDNSDiscoveryService(100 * time.Millisecond)

	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
	defer cancel()

	// Note: This test will attempt actual mDNS discovery
	// In a real network environment, this might find actual SoundTouch devices
	// In a test environment without devices, it should return an empty slice
	devices, _ := service.DiscoverDevices(ctx)

	// devices slice should be initialized (but might be empty)
	// We don't fail on errors as they may be due to network conditions in test environment
	if devices == nil {
		t.Error("Expected devices slice to be initialized, got nil")
	}

	// If devices are found, verify they have the required fields
	for _, device := range devices {
		if device.Host == "" {
			t.Error("Device host should not be empty")
		}

		if device.Port == 0 {
			t.Error("Device port should not be zero")
		}

		if device.Name == "" {
			t.Error("Device name should not be empty")
		}

		if device.InfoURL == "" {
			t.Error("Device info URL should not be empty")
		}
	}
}

func TestMDNSServiceEntryToDevice(t *testing.T) {
	service := NewMDNSDiscoveryService(5 * time.Second)

	// Test with nil entry
	device := service.serviceEntryToDevice(nil)
	if device != nil {
		t.Error("Expected nil device for nil entry")
	}

	// Note: Testing with actual zeroconf.ServiceEntry would require
	// creating mock objects or using a testing framework that can
	// create proper ServiceEntry instances. For now, we test the nil case.
}

func TestMDNSDiscoveryTimeout(t *testing.T) {
	service := NewMDNSDiscoveryService(100 * time.Millisecond)

	start := time.Now()
	ctx := context.Background()

	_, err := service.DiscoverDevices(ctx)
	duration := time.Since(start)

	// The discovery should not take significantly longer than the timeout
	// Allow some buffer for processing time
	maxExpected := 200 * time.Millisecond
	if duration > maxExpected {
		t.Errorf("Discovery took too long: %v, expected less than %v", duration, maxExpected)
	}

	// We don't check for error here because mDNS discovery might succeed quickly
	// or fail due to network conditions, both are acceptable in tests
	_ = err
}

func TestMDNSGetIPv4InterfaceUnknownName(t *testing.T) {
	service := NewMDNSDiscoveryServiceWithInterface(5*time.Second, "definitely-not-a-real-iface-xyz")
	if iface := service.getIPv4Interface(); iface != nil {
		t.Errorf("Expected nil for unknown interface name, got %q", iface.Name)
	}
}

func TestMDNSGetIPv4InterfaceExplicitMatchesAutoPick(t *testing.T) {
	auto := NewMDNSDiscoveryService(5 * time.Second).getIPv4Interface()
	if auto == nil {
		t.Skip("No suitable IPv4 interface available on this host")
	}

	explicit := NewMDNSDiscoveryServiceWithInterface(5*time.Second, auto.Name).getIPv4Interface()
	if explicit == nil {
		t.Fatalf("Expected explicit lookup of %q to succeed", auto.Name)
	}

	if explicit.Name != auto.Name {
		t.Errorf("Expected explicit interface %q, got %q", auto.Name, explicit.Name)
	}

	// Sanity: the resolved interface really has an IPv4 we could bind to.
	if !interfaceHasIPv4(explicit) {
		t.Errorf("Resolved interface %q has no IPv4 address", explicit.Name)
	}
}

func TestMDNSDiscoveryWithCancelledContext(t *testing.T) {
	service := NewMDNSDiscoveryService(5 * time.Second)

	ctx, cancel := context.WithCancel(context.Background())
	cancel() // Cancel immediately

	devices, err := service.DiscoverDevices(ctx)

	// Should handle cancelled context gracefully
	// devices should never be nil, even if cancelled
	if devices == nil {
		t.Error("Expected devices slice to be initialized, got nil")
	}

	// Error might or might not occur depending on timing and network conditions
	// This is acceptable for testing - we just ensure no panic and proper slice initialization
	_ = err
}
