package distribution

// Coordinate provenance on the map feed.
//
// Every facility coordinate in the system today is street-level: the addresses
// are seed data whose building numbers do not exist in OpenStreetMap, so the
// pins were resolved to the middle of the named street. That is fine for placing
// a warehouse in the right part of Greater Medan and useless for driving to it.
//
// The risk is a client that cannot tell the difference and renders all of them
// as ordinary pins. MapPlace.CoordinatePrecision exists so the client CAN tell,
// which only works if NULL is passed through as NULL rather than being defaulted
// to "exact" — and if a row with coordinates but no precision stays visibly
// unlabelled instead of quietly acquiring a confident one.

import (
	"testing"

	"github.com/TaruLab/keluargamart-backend/internal/db"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/shopspring/decimal"
)

func dec(d string) decimal.NullDecimal {
	return decimal.NullDecimal{Decimal: decimal.RequireFromString(d), Valid: true}
}

func TestPlaceFromWarehouseRowCarriesPrecision(t *testing.T) {
	cases := []struct {
		name string
		prec pgtype.Text
		want *string
	}{
		{"street level", pgtype.Text{String: "street", Valid: true}, strPtr("street")},
		{"surveyed", pgtype.Text{String: "exact", Valid: true}, strPtr("exact")},
		{"city centroid", pgtype.Text{String: "city", Valid: true}, strPtr("city")},
		// The important one: a coordinate with no recorded provenance must not
		// acquire one. Defaulting this to "exact" is the exact bug the field
		// was added to prevent, and it would look correct in the response.
		{"unknown stays unknown", pgtype.Text{Valid: false}, nil},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			mp := placeFromWarehouseRow(db.ListWarehouseLocationsRow{
				ID: 1, Code: "WH1", Name: "Gudang Pusat",
				Latitude: dec("3.5917631"), Longitude: dec("98.6254998"),
				CoordinatePrecision: tc.prec,
			})
			if mp.Latitude == nil || mp.Longitude == nil {
				t.Fatal("coordinates should be present")
			}
			assertStrPtrEq(t, "warehouse precision", mp.CoordinatePrecision, tc.want)
		})
	}
}

func TestPlaceFromStoreRowCarriesPrecision(t *testing.T) {
	mp := placeFromStoreRow(db.ListStoreLocationsRow{
		ID: 2, Code: "KM-PST", Name: "KeluargaMart Pusat",
		Latitude: dec("3.5769372"), Longitude: dec("98.6677771"),
		CoordinatePrecision: pgtype.Text{String: "street", Valid: true},
	})
	if mp.CoordinatePrecision == nil || *mp.CoordinatePrecision != "street" {
		t.Fatalf("store precision = %v", mp.CoordinatePrecision)
	}
}

// A facility with no coordinates must report no coordinates. It is tempting to
// pair the new field with the existing one and return a precision string for
// rows that have nothing to place, but the precision describes a POSITION, and
// there is no position to describe.
func TestPlaceWithoutCoordinatesHasNoPrecision(t *testing.T) {
	wh := placeFromWarehouseRow(db.ListWarehouseLocationsRow{
		ID: 9, Code: "WH-TEST", Name: "Test Toko Baru - Gudang",
		CoordinatePrecision: pgtype.Text{String: "street", Valid: true},
	})
	if wh.Latitude != nil || wh.Longitude != nil {
		t.Fatalf("expected no coordinates, got %v,%v", wh.Latitude, wh.Longitude)
	}

	st := placeFromStoreRow(db.ListStoreLocationsRow{
		ID: 7, Code: "TEST", Name: "Test Toko Baru",
		CoordinatePrecision: pgtype.Text{String: "street", Valid: true},
	})
	if st.Latitude != nil || st.Longitude != nil {
		t.Fatalf("expected no coordinates, got %v,%v", st.Latitude, st.Longitude)
	}
}

// Half a position is not a position. A row with a latitude but no longitude has
// nothing to plot, and BaseMap skips it — so precision must not be surfaced
// either, or a client would label a point it is about to discard.
func TestPartialCoordinatesAreTreatedAsAbsent(t *testing.T) {
	mp := placeFromWarehouseRow(db.ListWarehouseLocationsRow{
		ID: 1, Code: "WH1",
		Latitude:            dec("3.5917631"),
		CoordinatePrecision: pgtype.Text{String: "street", Valid: true},
	})
	if mp.Latitude != nil || mp.Longitude != nil {
		t.Fatalf("half a position should plot as absent, got %v,%v", mp.Latitude, mp.Longitude)
	}
}

func assertStrPtrEq(t *testing.T, what string, got, want *string) {
	t.Helper()
	if want == nil {
		if got != nil {
			t.Fatalf("%s = %q, want nil", what, *got)
		}
		return
	}
	if got == nil {
		t.Fatalf("%s = nil, want %q", what, *want)
	}
	if *got != *want {
		t.Fatalf("%s = %q, want %q", what, *got, *want)
	}
}
