package plan

import (
	"math"
	"strings"
	"testing"
	"time"
)

func TestParsePlanDurationChecksISOMultiplicationAndAddition(t *testing.T) {
	maxWholeSecond := time.Duration((math.MaxInt64 / int64(time.Second)) * int64(time.Second))
	tests := []struct {
		name    string
		raw     string
		want    time.Duration
		wantErr bool
	}{
		{name: "ISO ordinary", raw: "PT1H30M5S", want: time.Hour + 30*time.Minute + 5*time.Second},
		{name: "ISO maximum whole second", raw: "PT2562047H47M16S", want: maxWholeSecond},
		{name: "ISO multiplication overflow", raw: "PT5124096H", wantErr: true},
		{name: "ISO addition overflow at minutes", raw: "PT2562047H48M", wantErr: true},
		{name: "ISO addition overflow at seconds", raw: "PT2562047H47M17S", wantErr: true},
		{name: "ISO numeric overflow", raw: "PT9223372036854775808S", wantErr: true},
		{name: "ISO repeated unit", raw: "PT1H2H", wantErr: true},
		{name: "ISO out of order unit", raw: "PT1M1H", wantErr: true},
		{name: "Go maximum duration remains supported", raw: "2562047h47m16.854775807s", want: time.Duration(math.MaxInt64)},
		{name: "Go duration overflow remains rejected", raw: "2562047h47m16.854775808s", wantErr: true},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			got, err := parsePlanDuration(test.raw)
			if test.wantErr {
				if err == nil {
					t.Fatalf("parsePlanDuration(%q) = %v, want error", test.raw, got)
				}
				return
			}
			if err != nil {
				t.Fatalf("parsePlanDuration(%q) error = %v", test.raw, err)
			}
			if got != test.want {
				t.Fatalf("parsePlanDuration(%q) = %v, want %v", test.raw, got, test.want)
			}
		})
	}
}

func TestCompileRejectsOverflowingISODurationWithoutArtifact(t *testing.T) {
	for _, rawDuration := range []string{
		"PT5124096H",
		"PT2562047H48M",
		"PT2562047H47M17S",
	} {
		t.Run(rawDuration, func(t *testing.T) {
			raw := strings.Replace(waitWithoutRetrySource, "timeout: PT1M", "timeout: "+rawDuration, 1)
			artifact, diagnostics, err := Compile([]byte(raw))
			if err != nil {
				t.Fatalf("Compile() error = %v", err)
			}
			if artifact != nil {
				t.Fatalf("Compile() artifact = %#v, want nil", artifact)
			}
			diagnostic, ok := findCode(diagnostics, "node.invalid_duration")
			if !ok {
				t.Fatalf("Compile() diagnostics = %#v, want node.invalid_duration", diagnostics)
			}
			if diagnostic.NodeID != "pause" {
				t.Fatalf("Compile() duration diagnostic = %#v, want node pause", diagnostic)
			}
		})
	}
}
