package scheduler

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"git.toki-labs.com/toki/alt/packages/domain/market"
)

func testCapability() market.ProviderCapability {
	return market.ProviderCapability{
		Provider: market.ProviderKIS,
		Rules: []market.ProviderCapabilityRule{
			{
				Market: market.MarketKR,
				Venue:  market.VenueKRX,
				AssetTypes: map[market.AssetType]bool{
					market.AssetTypeEquity: true,
				},
				Timeframes: map[market.Timeframe]bool{
					market.TimeframeDaily: true,
				},
			},
		},
	}
}

func TestValidateConfigReportsNextWindowAndRejectedCombination(t *testing.T) {
	cfg, err := LoadConfig(strings.NewReader(`
schedules:
  - name: kr-core-daily
    provider: kis
    selector:
      kind: watchlist
      market: KR
      venue: KRX
      name: kr-core
      symbols: ["005930", "000660"]
    timeframe: daily
    cadence: daily
    timezone: Asia/Seoul
    backfill_window: 3d
    parallelism_limit: 2
  - name: kr-core-monthly
    provider: kis
    selector:
      kind: watchlist
      market: KR
      venue: KRX
      name: kr-core
      symbols: ["005930"]
    timeframe: monthly
    cadence: daily
    timezone: Asia/Seoul
    backfill_window: 3d
    parallelism_limit: 1
`))
	if err != nil {
		t.Fatalf("LoadConfig: %v", err)
	}
	now := time.Date(2026, 6, 21, 10, 30, 0, 0, time.UTC)
	result := ValidateConfig(cfg, ValidationOptions{
		Now:          now,
		Capabilities: CapabilityMap(testCapability()),
	})

	if len(result.Items) != 2 {
		t.Fatalf("items = %d, want 2", len(result.Items))
	}
	valid := result.Items[0]
	if valid.Status != "valid" {
		t.Fatalf("first status = %q, want valid: %+v", valid.Status, valid)
	}
	if valid.NextRun != "2026-06-22T00:00:00+09:00" {
		t.Errorf("next run = %q, want 2026-06-22T00:00:00+09:00", valid.NextRun)
	}
	if valid.Cadence != "24h0m0s" || valid.BackfillWindow != "72h0m0s" {
		t.Errorf("durations = %s/%s, want 24h0m0s/72h0m0s", valid.Cadence, valid.BackfillWindow)
	}
	rejected := result.Items[1]
	if rejected.Status != "rejected" {
		t.Fatalf("second status = %q, want rejected", rejected.Status)
	}
	if !strings.Contains(rejected.Reason, "unsupported timeframe") {
		t.Errorf("rejection reason %q missing unsupported timeframe", rejected.Reason)
	}
}

func TestWriteTextAndJSONL(t *testing.T) {
	validation := Validation{Items: []ValidationItem{
		{
			Schedule:         "kr-core-daily",
			Status:           "valid",
			Provider:         "kis",
			Selector:         "kr-core",
			Timeframe:        "daily",
			Cadence:          "24h0m0s",
			Timezone:         "Asia/Seoul",
			BackfillWindow:   "72h0m0s",
			ParallelismLimit: 2,
			NextRun:          "2026-06-22T00:00:00+09:00",
		},
		{Schedule: "bad", Status: "rejected", Reason: "unsupported provider/timeframe combination"},
	}}

	var text bytes.Buffer
	if err := WriteText(&text, validation); err != nil {
		t.Fatalf("WriteText: %v", err)
	}
	for _, want := range []string{
		"schedule=kr-core-daily status=valid",
		"next_run=2026-06-22T00:00:00+09:00",
		"schedule=bad status=rejected",
		"unsupported provider/timeframe combination",
	} {
		if !strings.Contains(text.String(), want) {
			t.Errorf("text output %q missing %q", text.String(), want)
		}
	}

	var jsonl bytes.Buffer
	if err := WriteJSONL(&jsonl, validation); err != nil {
		t.Fatalf("WriteJSONL: %v", err)
	}
	if !strings.Contains(jsonl.String(), `"schedule":"kr-core-daily"`) {
		t.Errorf("jsonl output %q missing valid schedule", jsonl.String())
	}
	if !strings.Contains(jsonl.String(), `"status":"rejected"`) {
		t.Errorf("jsonl output %q missing rejected status", jsonl.String())
	}
}

func TestRunCheckReturnsRejectedWhenAnyScheduleIsRejected(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "schedule.yaml")
	if err := os.WriteFile(path, []byte(`
schedules:
  - name: unsupported
    provider: kis
    selector:
      kind: watchlist
      market: KR
      venue: KRX
      name: kr-core
      symbols: ["005930"]
    timeframe: minute_1
    cadence: 24h
    timezone: Asia/Seoul
    backfill_window: 48h
    parallelism_limit: 1
`), 0644); err != nil {
		t.Fatalf("write fixture: %v", err)
	}

	var stdout, stderr bytes.Buffer
	code := RunCheck([]string{"--file", path, "--output", "jsonl", "--at", "2026-06-21T10:30:00Z"}, &stdout, &stderr, func() time.Time {
		return time.Date(2026, 6, 21, 10, 30, 0, 0, time.UTC)
	}, CapabilityMap(testCapability()))

	if code != exitRejected {
		t.Fatalf("exit code = %d, want %d (stderr=%q stdout=%q)", code, exitRejected, stderr.String(), stdout.String())
	}
	if !strings.Contains(stdout.String(), `"status":"rejected"`) {
		t.Errorf("stdout %q missing rejected JSONL", stdout.String())
	}
}
