package resolver

import (
	"context"
	"fmt"
	"io"
	"sort"
	"strings"
	"time"
)

// SplitHorizonResult holds the comparison between an authoritative DNS answer
// and the answer from a local resolver.
type SplitHorizonResult struct {
	AuthoritativeIPs    []string // IPs from the authoritative nameserver
	AuthoritativeSource string   // NS hostname that provided the authoritative answer
	LocalIPs            []string // IPs from the local resolver (nil if query failed / NXDOMAIN)
	LocalSource         string   // Local resolver address that was queried (empty if all failed)
	HasConflict         bool     // True when both sets are non-nil and differ
}

// CheckSplitHorizon queries local resolvers for hostname and compares the result
// against authoritativeIPs.
//
// Local resolvers are tried in priority order; the first successful response is used.
// If all local resolvers fail or return NXDOMAIN, HasConflict is false (no conflict).
// w receives Stage 2.5 diagnostic lines (pass io.Discard to suppress).
func CheckSplitHorizon(ctx context.Context, w io.Writer, localResolvers []string, hostname string, authoritativeIPs []string, authoritativeSource string, timeout time.Duration) SplitHorizonResult {
	result := SplitHorizonResult{
		AuthoritativeIPs:    authoritativeIPs,
		AuthoritativeSource: authoritativeSource,
	}

	if len(localResolvers) > 0 {
		fmt.Fprintf(w, "[dns] Stage 2.5: Split-horizon cross-check against local resolvers [%s]\n",
			strings.Join(localResolvers, ", "))
	}

	for _, lr := range localResolvers {
		queryCtx, cancel := context.WithTimeout(ctx, timeout)
		ips, err := queryA(queryCtx, lr, hostname, timeout)
		cancel()
		if err != nil {
			continue // timeout, NXDOMAIN, etc. — not a conflict
		}
		if len(ips) == 0 {
			continue
		}
		result.LocalIPs = ips
		result.LocalSource = lr
		result.HasConflict = !ipSetsEqual(authoritativeIPs, ips)
		if result.HasConflict {
			fmt.Fprintf(w, "[dns]   %s \u2192 %s (differs from authoritative %s)\n",
				lr, strings.Join(ips, ", "), strings.Join(authoritativeIPs, ", "))
			fmt.Fprintf(w, "[dns] CONFLICT: authoritative and local resolvers disagree\n")
		} else {
			fmt.Fprintf(w, "[dns]   %s \u2192 %s (matches authoritative)\n", lr, strings.Join(ips, ", "))
			fmt.Fprintf(w, "[dns] No conflict detected\n")
		}
		return result
	}

	// All local resolvers failed — no conflict detectable.
	return result
}

// ipSetsEqual reports whether a and b contain the same IPs regardless of order.
func ipSetsEqual(a, b []string) bool {
	sa := sortedDedup(a)
	sb := sortedDedup(b)
	if len(sa) != len(sb) {
		return false
	}
	for i := range sa {
		if sa[i] != sb[i] {
			return false
		}
	}
	return true
}

// sortedDedup returns a sorted, deduplicated copy of ips.
func sortedDedup(ips []string) []string {
	seen := make(map[string]bool, len(ips))
	deduped := make([]string, 0, len(ips))
	for _, ip := range ips {
		if !seen[ip] {
			seen[ip] = true
			deduped = append(deduped, ip)
		}
	}
	sort.Strings(deduped)
	return deduped
}
