package timeexpr

import (
	"testing"
	"time"
)

func TestParseNowExpressionToUnixNano(t *testing.T) {
	t.Parallel()

	fixedNow := time.Date(2026, time.April, 4, 12, 0, 0, 0, time.UTC)

	testCases := []struct {
		name        string
		input       string
		wantMatched bool
		wantValue   int64
		wantErr     string
	}{
		{
			name:        "not now expression",
			input:       "1712123000",
			wantMatched: false,
		},
		{
			name:        "now",
			input:       "now",
			wantMatched: true,
			wantValue:   fixedNow.UnixNano(),
		},
		{
			name:        "now minus duration",
			input:       "now-30m",
			wantMatched: true,
			wantValue:   fixedNow.Add(-30 * time.Minute).UnixNano(),
		},
		{
			name:        "now minus compound duration",
			input:       "now-3h30m",
			wantMatched: true,
			wantValue:   fixedNow.Add(-(3*time.Hour + 30*time.Minute)).UnixNano(),
		},
		{
			name:        "now minus days",
			input:       "now-1d",
			wantMatched: true,
			wantValue:   fixedNow.Add(-24 * time.Hour).UnixNano(),
		},
		{
			name:        "now minus days and hours",
			input:       "now-1d3h",
			wantMatched: true,
			wantValue:   fixedNow.Add(-(24*time.Hour + 3*time.Hour)).UnixNano(),
		},
		{
			name:        "now minus zero seconds preserves now",
			input:       "now-0s",
			wantMatched: true,
			wantValue:   fixedNow.UnixNano(),
		},
		{
			name:        "now minus zero days preserves now",
			input:       "now-0d",
			wantMatched: true,
			wantValue:   fixedNow.UnixNano(),
		},
		{
			name:        "plus is not supported",
			input:       "now+30m",
			wantMatched: true,
			wantErr:     `invalid now expression "now+30m" (only "now" and "now-<duration>" are supported)`,
		},
		{
			name:        "missing duration",
			input:       "now-",
			wantMatched: true,
			wantErr:     `invalid now expression "now-" (missing duration after "now-")`,
		},
		{
			name:        "invalid duration",
			input:       "now-abc",
			wantMatched: true,
			wantErr:     `invalid now expression "now-abc" (duration must be like "30m", "3h30m", or "1d")`,
		},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			got, matched, err := ParseNowExpressionToUnixNano(tc.input, fixedNow)
			if matched != tc.wantMatched {
				t.Fatalf("matched = %t, want %t", matched, tc.wantMatched)
			}

			if tc.wantErr != "" {
				if err == nil {
					t.Fatalf("error = nil, want %q", tc.wantErr)
				}
				if gotErr := err.Error(); gotErr != tc.wantErr {
					t.Fatalf("error = %q, want %q", gotErr, tc.wantErr)
				}
				return
			}

			if err != nil {
				t.Fatalf("error = %v", err)
			}
			if got != tc.wantValue {
				t.Fatalf("value = %d, want %d", got, tc.wantValue)
			}
		})
	}
}

func TestParseDuration(t *testing.T) {
	t.Parallel()

	testCases := []struct {
		name    string
		input   string
		want    time.Duration
		wantErr string
	}{
		{
			name:  "standard duration",
			input: "3h30m",
			want:  3*time.Hour + 30*time.Minute,
		},
		{
			name:  "day unit",
			input: "1d",
			want:  24 * time.Hour,
		},
		{
			name:  "compound day unit",
			input: "1d3h",
			want:  27 * time.Hour,
		},
		{
			name:    "empty",
			input:   "",
			wantErr: "duration must not be empty",
		},
		{
			name:    "negative standard duration",
			input:   "-1h",
			wantErr: "duration must be positive",
		},
		{
			name:    "negative day duration",
			input:   "-1d",
			wantErr: "duration must be positive",
		},
		{
			name:    "zero standard duration",
			input:   "0s",
			wantErr: "duration must be positive",
		},
		{
			name:    "zero day duration",
			input:   "0d",
			wantErr: "duration must be positive",
		},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			got, err := ParseDuration(tc.input)
			if tc.wantErr != "" {
				if err == nil {
					t.Fatalf("error = nil, want %q", tc.wantErr)
				}
				if gotErr := err.Error(); gotErr != tc.wantErr {
					t.Fatalf("error = %q, want %q", gotErr, tc.wantErr)
				}
				return
			}

			if err != nil {
				t.Fatalf("error = %v", err)
			}
			if got != tc.want {
				t.Fatalf("duration = %s, want %s", got, tc.want)
			}
		})
	}
}
