package schema

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestMigrationLoopPlan(t *testing.T) {
	raw := `{
	  "futureTopLevelField": true,
	  "runtime": {
	    "region": "prod",
	    "vmcluster": "vmcluster",
	    "vminsert": "http://insert",
	    "window": {"start":"s","end":"e","stepInterval":"1d"},
	    "verification": {"timeoutSeconds":60,"tolerance":0.01}
	  },
	  "vmselect": {
	    "origin": "http://o",
	    "migration": "http://m",
	    "candidate": "http://c"
	  },
	  "tenants": [{
	    "name":"t1",
	    "cmds":[{"step":"manifest","cmd":"operator command","action":{"kind":"manifest","mode":"ignored","futureActionField":42,"tenantsFile":"t1.json","manifest":"t1-manifest.json"}}]
	  }]
	}`
	var p MigrationLoopPlan
	assert.NoError(t, json.Unmarshal([]byte(raw), &p))

	assert.NotNil(t, p.Runtime)
	assert.Equal(t, "prod", p.Runtime.Region)
	assert.Equal(t, "1d", p.Runtime.Window.StepInterval)

	assert.NotNil(t, p.Vmselect)
	assert.Equal(t, "http://o", p.Vmselect.Origin)
	assert.Equal(t, "http://m", p.Vmselect.Migration)
	assert.Equal(t, "http://c", p.Vmselect.Candidate)

	assert.Len(t, p.Tenants, 1)
	assert.Len(t, p.Tenants[0].Cmds, 1)
	action := p.Tenants[0].Cmds[0].Action
	assert.NotNil(t, action)
	assert.Equal(t, ActionManifest, action.Kind)
	assert.Equal(t, "t1.json", action.TenantsFile)
	assert.Equal(t, "t1-manifest.json", action.Manifest)
}

func TestValidateTenantURLs(t *testing.T) {
	f := func(t *testing.T, plan *MigrationLoopPlan, wantErr string) {
		t.Helper()
		err := plan.ValidateTenantURLs()
		if wantErr == "" {
			assert.NoError(t, err)
			return
		}
		assert.ErrorContains(t, err, wantErr)
	}
	base := func(mutate func(p *MigrationLoopPlan)) *MigrationLoopPlan {
		p := &MigrationLoopPlan{
			Runtime: &MigrationLoopRuntime{Vminsert: "http://i/insert/{tenant}/prometheus"},
			Vmselect: &MigrationLoopVmselect{
				Origin: "http://o/{tenant}/prometheus",
			},
		}
		mutate(p)
		return p
	}

	f(t, base(func(*MigrationLoopPlan) {}), "")
	f(t, base(func(p *MigrationLoopPlan) { p.Runtime.Vminsert = "http://i/insert/0/prometheus" }),
		"vminsert URL template must contain {tenant}")
	f(t, base(func(p *MigrationLoopPlan) { p.Vmselect.Origin = "http://o/select/prometheus" }),
		"vmselect.origin URL template must contain {tenant}")
	f(t, base(func(p *MigrationLoopPlan) { p.Vmselect.Migration = "http://m/prometheus" }),
		"vmselect.migration URL template must contain {tenant}")
	f(t, base(func(p *MigrationLoopPlan) { p.Vmselect.Candidate = "http://c/prometheus" }),
		"vmselect.candidate URL template must contain {tenant}")
	f(t, &MigrationLoopPlan{}, "")
	f(t, &MigrationLoopPlan{Vmselect: &MigrationLoopVmselect{}}, "")
}

func TestValidateLoopStepSequence(t *testing.T) {
	assert.NoError(t, ValidateLoopStepSequence([]string{"manifest", "migrate", "verify"}))
	for _, steps := range [][]string{
		nil,
		{"manifest"},
		{"manifest", "verify"},
		{"manifest", "verify", "migrate"},
		{"manifest", "migrate", "migrate", "verify"},
		{"manifest", "precheck", "migrate", "verify"},
		{"manifest", "migrate", "verify", "summary-status"},
	} {
		err := ValidateLoopStepSequence(steps)
		assert.ErrorContains(t, err, "steps must be exactly manifest -> migrate -> verify", "steps=%v", steps)
	}
}

func TestVmctlTuneValidate(t *testing.T) {
	f := func(t *testing.T, tune *VmctlTune, wantErr string) {
		t.Helper()
		err := tune.Validate()
		if wantErr == "" {
			assert.NoError(t, err)
			return
		}
		assert.ErrorContains(t, err, wantErr)
	}
	f(t, &VmctlTune{}, "")
	f(t, &VmctlTune{Concurrency: new(1), RateLimitBytes: new(0)}, "")
	f(t, &VmctlTune{Concurrency: new(0)}, "concurrency must be >= 1")
	f(t, &VmctlTune{Concurrency: new(-3)}, "concurrency must be >= 1")
	f(t, &VmctlTune{RateLimitBytes: new(-1)}, "rateLimitBytes must be >= 0")
}
