package legacysql

import (
	"fmt"
	"testing"
	"time"
)

func TestDBTime_ValueAndString_ZeroAndNonZero(t *testing.T) {
	zero := NewDBTime(time.Time{})
	if v, err := zero.Value(); err != nil {
		t.Fatalf("unexpected error for zero Value: %v", err)
	} else if v != nil {
		t.Fatalf("expected nil driver.Value for zero time, got %#v", v)
	}
	if s := zero.String(); s != "" {
		t.Fatalf("expected empty string for zero DBTime.String(), got %q", s)
	}

	tt := time.Date(2023, 10, 5, 13, 14, 15, 0, time.UTC)
	dt := NewDBTime(tt)

	val, err := dt.Value()
	if err != nil {
		t.Fatalf("unexpected error for non-zero Value: %v", err)
	}
	strVal, ok := val.(string)
	if !ok {
		t.Fatalf("expected Value to be string, got %T", val)
	}
	expected := tt.Format(time.DateTime)
	if strVal != expected {
		t.Fatalf("Value() = %q, want %q", strVal, expected)
	}

	if s := dt.String(); s != expected {
		t.Fatalf("String() = %q, want %q", s, expected)
	}
}

func TestDBTime_Scan_VariousInputs(t *testing.T) {
	base := time.Date(2022, 12, 31, 23, 59, 59, 0, time.UTC)
	layout := time.DateTime
	formatted := base.Format(layout)

	tests := []struct {
		name        string
		input       interface{}
		wantTime    time.Time
		wantIsZero  bool
		expectError bool
	}{
		{"nil sets zero", nil, time.Time{}, true, false},
		{"scan from string", formatted, base, false, false},
		{"scan from []byte", []byte(formatted), base, false, false},
		{"scan from time.Time", base, base, false, false},
		{"invalid parse", "not-a-time", time.Time{}, true, true},
		{"unsupported type", 12345, time.Time{}, true, true},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			var dt DBTime
			err := dt.Scan(tc.input)
			if tc.expectError {
				if err == nil {
					t.Fatalf("expected error, got nil")
				}
				// when expecting error, no further checks
				return
			}
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if tc.wantIsZero {
				if !dt.IsZero() {
					t.Fatalf("expected DBTime to be zero, got %v", dt.Time)
				}
				return
			}
			if dt.IsZero() {
				t.Fatalf("expected non-zero time, got zero")
			}
			// Compare times using Equal to ignore monotonic clock bits
			if !dt.Equal(tc.wantTime) {
				t.Fatalf("scanned time = %v, want %v", dt.Time, tc.wantTime)
			}
		})
	}
}

func TestDBTime_Scan_ParseErrorDetails(t *testing.T) {
	var dt DBTime
	err := dt.Scan("garbage")
	if err == nil {
		t.Fatalf("expected parse error, got nil")
	}
	// error message should mention parse
	if !contains(err.Error(), "could not parse time") {
		t.Fatalf("unexpected error message: %v", err)
	}
}

func TestDBTime_Scan_UnsupportedTypeMessage(t *testing.T) {
	var dt DBTime
	typ := struct{}{}
	err := dt.Scan(typ)
	if err == nil {
		t.Fatalf("expected unsupported-type error, got nil")
	}
	want := fmt.Sprintf("could not scan type %T into DBTime", typ)
	if err.Error() != want {
		t.Fatalf("error = %q, want %q", err.Error(), want)
	}
}

// small helper to avoid importing strings for a single contains check
func contains(s, substr string) bool {
	return len(substr) == 0 || (len(s) >= len(substr) && indexOf(s, substr) >= 0)
}

func indexOf(s, substr string) int {
	n := len(s)
	m := len(substr)
	if m == 0 {
		return 0
	}
	for i := 0; i <= n-m; i++ {
		if s[i:i+m] == substr {
			return i
		}
	}
	return -1
}
