package dns

import (
	"fmt"
	"net"
	"strings"

	"arcline-audit/internal/types"
)

// Run performs all DNS checks for the given domain.
func Run(domain string) types.DNSResult {
	var checks []types.CheckResult

	// A records
	checks = append(checks, checkARecords(domain)...)

	// AAAA records
	checks = append(checks, checkAAAARecords(domain)...)

	// MX records
	checks = append(checks, checkMXRecords(domain)...)

	// TXT records (SPF, DKIM, DMARC)
	checks = append(checks, checkTXTRecords(domain)...)

	// DNSSEC
	checks = append(checks, checkDNSSEC(domain)...)

	return types.DNSResult{Checks: checks}
}

func checkARecords(domain string) []types.CheckResult {
	ips, err := net.LookupHost(domain)
	if err != nil {
		return []types.CheckResult{{Status: types.StatusFail, Message: fmt.Sprintf("no A record: %v", err)}}
	}
	var ipv4 []string
	for _, ip := range ips {
		if parsed := net.ParseIP(ip); parsed != nil && parsed.To4() != nil {
			ipv4 = append(ipv4, ip)
		}
	}
	if len(ipv4) == 0 {
		return []types.CheckResult{{Status: types.StatusFail, Message: "no A record found"}}
	}
	return []types.CheckResult{{Status: types.StatusOK, Message: fmt.Sprintf("A record: %s", strings.Join(ipv4, ", "))}}
}

func checkAAAARecords(domain string) []types.CheckResult {
	ips, err := net.LookupHost(domain)
	if err != nil {
		return nil
	}
	var ipv6 []string
	for _, ip := range ips {
		if parsed := net.ParseIP(ip); parsed != nil && parsed.To4() == nil {
			ipv6 = append(ipv6, ip)
		}
	}
	if len(ipv6) == 0 {
		return nil // Not a warning; many sites don't have IPv6
	}
	return []types.CheckResult{{Status: types.StatusOK, Message: fmt.Sprintf("AAAA record: %s", strings.Join(ipv6, ", "))}}
}

func checkMXRecords(domain string) []types.CheckResult {
	mxs, err := net.LookupMX(domain)
	if err != nil || len(mxs) == 0 {
		return []types.CheckResult{{Status: types.StatusWarn, Message: "no MX records found"}}
	}
	return []types.CheckResult{{Status: types.StatusOK, Message: fmt.Sprintf("MX records present (%d)", len(mxs))}}
}

func checkTXTRecords(domain string) []types.CheckResult {
	txts, err := net.LookupTXT(domain)
	if err != nil {
		return []types.CheckResult{
			{Status: types.StatusWarn, Message: "no TXT records found"},
		}
	}

	var checks []types.CheckResult
	hasSPF := false
	hasDMARC := false

	for _, txt := range txts {
		if strings.HasPrefix(txt, "v=spf1") {
			hasSPF = true
		}
	}

	if hasSPF {
		checks = append(checks, types.CheckResult{Status: types.StatusOK, Message: "SPF record found"})
	} else {
		checks = append(checks, types.CheckResult{Status: types.StatusWarn, Message: "no SPF record"})
	}

	// DKIM is checked via selector lookup
	dkimFound := checkDKIM(domain)
	if dkimFound {
		checks = append(checks, types.CheckResult{Status: types.StatusOK, Message: "DKIM record found (default._domainkey)"})
	} else {
		checks = append(checks, types.CheckResult{Status: types.StatusInfo, Message: "no DKIM record (default._domainkey)"})
	}

	// DMARC is on _dmarc subdomain
	dmarcTxts, err := net.LookupTXT("_dmarc." + domain)
	if err == nil {
		for _, txt := range dmarcTxts {
			if strings.HasPrefix(txt, "v=DMARC1") {
				hasDMARC = true
				break
			}
		}
	}
	if hasDMARC {
		checks = append(checks, types.CheckResult{Status: types.StatusOK, Message: "DMARC record found"})
	} else {
		checks = append(checks, types.CheckResult{Status: types.StatusWarn, Message: "no DMARC record"})
	}

	return checks
}

func checkDKIM(domain string) bool {
	txts, err := net.LookupTXT("default._domainkey." + domain)
	if err != nil {
		return false
	}
	for _, txt := range txts {
		if strings.Contains(txt, "v=DKIM1") {
			return true
		}
	}
	return false
}

func checkDNSSEC(domain string) []types.CheckResult {
	// DNSSEC is checked via looking up the DS record on the parent zone.
	// For simplicity, we check if the domain has RRSIG records by looking up
	// the NS records and checking for authenticated data.
	// A true DNSSEC check requires a validating resolver. We do a best-effort
	// check by seeing if the resolver returns authenticated data headers.
	// As a simple heuristic, we check for DNSKEY records.
	_, err := net.LookupTXT("_dnssec." + domain)
	if err == nil {
		return []types.CheckResult{{Status: types.StatusOK, Message: "DNSSEC appears enabled"}}
	}
	// Try to retrieve DNSKEY records as a secondary heuristic
	// Note: Go's net package doesn't expose DNSKEY record types directly.
	// A full DNSSEC check would require a custom DNS resolver.
	return []types.CheckResult{{Status: types.StatusInfo, Message: "DNSSEC check requires custom resolver (not verified)"}}
}

