package adif

import (
	"bufio"
	"bytes"
	"strings"
	"testing"
	"time"

	"hamlog/internal/qso"
)

// TestPromotedFieldsRoundTrip writes a QSO carrying the ADIF 3.1.7 promoted
// fields, parses it back, and checks they survive — guarding the export
// writeRecord ↔ import recordToQSO field-name mapping against typos.
func TestPromotedFieldsRoundTrip(t *testing.T) {
	dist := 1234.5
	rxp := 5.0
	a := 12.0
	in := qso.QSO{
		Callsign: "EA8ABC", Band: "20m", Mode: "SSB",
		QSODate: time.Date(2026, 6, 6, 12, 0, 0, 0, time.UTC),
		SIG: "POTA", SIGInfo: "US-0001", MySIG: "WWFF", MySIGInfo: "ONFF-0001",
		WWFFRef: "ONFF-0001", MyWWFFRef: "F-FFF-0001",
		Distance: &dist, RXPower: &rxp, AIndex: &a,
		SKCC: "12345S", FISTS: "999", TenTen: "55555",
		ContactedOp: "EA8XYZ", EqCall: "EA8OLD", PFX: "EA8", MyName: "Greg",
		Class: "1A", DarcDOK: "A01", MyDarcDOK: "B02", Region: "IV",
		SilentKey: "N", SWL: "N", QSOComplete: "Y", QSORandom: "Y",
		CreditGranted: "DXCC", CreditSubmitted: "WAS",
		MyARRLSect: "EMA", MyVUCCGrids: "FN20,FN21",
	}

	var buf bytes.Buffer
	bw := bufio.NewWriter(&buf)
	bw.WriteString("<EOH>\n")
	writeRecord(bw, in, true)
	bw.Flush()

	var rec Record
	if err := Parse(strings.NewReader(buf.String()), func(r Record) error { rec = r; return nil }); err != nil {
		t.Fatalf("parse: %v", err)
	}
	out, ok := recordToQSO(rec)
	if !ok {
		t.Fatal("recordToQSO returned !ok")
	}

	checks := map[string]struct{ got, want string }{
		"SIG":              {out.SIG, in.SIG},
		"SIG_INFO":         {out.SIGInfo, in.SIGInfo},
		"MY_SIG":           {out.MySIG, in.MySIG},
		"MY_SIG_INFO":      {out.MySIGInfo, in.MySIGInfo},
		"WWFF_REF":         {out.WWFFRef, in.WWFFRef},
		"MY_WWFF_REF":      {out.MyWWFFRef, in.MyWWFFRef},
		"SKCC":             {out.SKCC, in.SKCC},
		"FISTS":            {out.FISTS, in.FISTS},
		"TEN_TEN":          {out.TenTen, in.TenTen},
		"CONTACTED_OP":     {out.ContactedOp, in.ContactedOp},
		"EQ_CALL":          {out.EqCall, in.EqCall},
		"PFX":              {out.PFX, in.PFX},
		"MY_NAME":          {out.MyName, in.MyName},
		"CLASS":            {out.Class, in.Class},
		"DARC_DOK":         {out.DarcDOK, in.DarcDOK},
		"MY_DARC_DOK":      {out.MyDarcDOK, in.MyDarcDOK},
		"REGION":           {out.Region, in.Region},
		"SILENT_KEY":       {out.SilentKey, in.SilentKey},
		"SWL":              {out.SWL, in.SWL},
		"QSO_COMPLETE":     {out.QSOComplete, in.QSOComplete},
		"QSO_RANDOM":       {out.QSORandom, in.QSORandom},
		"CREDIT_GRANTED":   {out.CreditGranted, in.CreditGranted},
		"CREDIT_SUBMITTED": {out.CreditSubmitted, in.CreditSubmitted},
		"MY_ARRL_SECT":     {out.MyARRLSect, in.MyARRLSect},
		"MY_VUCC_GRIDS":    {out.MyVUCCGrids, in.MyVUCCGrids},
	}
	for tag, c := range checks {
		if c.got != c.want {
			t.Errorf("%s round-trip = %q, want %q", tag, c.got, c.want)
		}
	}
	if out.Distance == nil || *out.Distance != dist {
		t.Errorf("DISTANCE round-trip = %v, want %v", out.Distance, dist)
	}
	if out.RXPower == nil || *out.RXPower != rxp {
		t.Errorf("RX_PWR round-trip = %v, want %v", out.RXPower, rxp)
	}
	if out.AIndex == nil || *out.AIndex != a {
		t.Errorf("A_INDEX round-trip = %v, want %v", out.AIndex, a)
	}
}

// TestStandardExportDropsNonStandard verifies that standard mode strips
// vendor/APP tags while full mode keeps them.
func TestStandardExportDropsNonStandard(t *testing.T) {
	q := qso.QSO{
		Callsign: "F4BPO", Band: "20m", Mode: "CW",
		Extras: map[string]string{
			"APP_LOG4OM_FOO": "x",
			"DARC_DOK":       "A01", // standard → kept in both
			"MY_VENDOR_TAG":  "y",   // non-standard → dropped in standard mode
		},
	}

	standard := renderRecord(q, false)
	if strings.Contains(standard, "APP_LOG4OM_FOO") || strings.Contains(standard, "MY_VENDOR_TAG") {
		t.Errorf("standard export should drop non-standard tags:\n%s", standard)
	}

	full := renderRecord(q, true)
	if !strings.Contains(full, "APP_LOG4OM_FOO") || !strings.Contains(full, "MY_VENDOR_TAG") {
		t.Errorf("full export should keep all extras:\n%s", full)
	}
}

func renderRecord(q qso.QSO, includeApp bool) string {
	var buf bytes.Buffer
	bw := bufio.NewWriter(&buf)
	writeRecord(bw, q, includeApp)
	bw.Flush()
	return buf.String()
}
