package main

import (
	"os"
	"testing"
	"time"
)

func TestParseSlowLog(t *testing.T) {
	// Open the test log file
	file, err := os.Open("1770168784-821a5d37-488b-439e-ba01-30654f0c52d0.log")
	if err != nil {
		t.Fatalf("Failed to open test file: %v", err)
	}
	defer file.Close()

	parser := NewStreamParser(file)
	var slowLogs []SlowLog

	for {
		record, err := parser.ReadNext()
		if err != nil {
			t.Logf("Parse error (continuing): %v", err)
			continue
		}
		if record == nil {
			break
		}
		slowLogs = append(slowLogs, *record)
	}

	t.Logf("Parsed %d slow log records", len(slowLogs))

	// Check first record
	if len(slowLogs) == 0 {
		t.Fatal("No records parsed")
	}

	first := slowLogs[0]
	t.Logf("First record:")
	t.Logf("  Time: %v", first.Time)
	t.Logf("  User: %s", first.User)
	t.Logf("  Host: %s", first.Host)
	t.Logf("  ConnID: %d", first.ConnID)
	t.Logf("  QueryTime: %f", first.QueryTime)
	t.Logf("  Query: %s", first.Query)
	t.Logf("  TxnStartTs: %d", first.TxnStartTs)

	// Validate first record
	expectedTime, _ := time.Parse(time.RFC3339, "2026-02-04T01:30:02.659792948Z")
	if first.Time.IsZero() {
		t.Error("Time is zero")
	} else {
		// Allow 1 second difference due to precision loss (nanoseconds -> microseconds)
		diff := first.Time.Sub(expectedTime.UTC())
		if diff < 0 {
			diff = -diff
		}
		if diff > time.Second {
			t.Errorf("Time mismatch: got %v, expected %v (diff: %v)", first.Time, expectedTime.UTC(), diff)
		}
	}

	if first.User != "4BpdnosF5NcrCzd.app" {
		t.Errorf("User mismatch: got %s, expected 4BpdnosF5NcrCzd.app", first.User)
	}

	if first.Host != "10.0.94.96" {
		t.Errorf("Host mismatch: got %s, expected 10.0.94.96", first.Host)
	}

	if first.ConnID != 219015796 {
		t.Errorf("ConnID mismatch: got %d, expected 219015796", first.ConnID)
	}

	if first.QueryTime != 0.425029799 {
		t.Errorf("QueryTime mismatch: got %f, expected 0.425029799", first.QueryTime)
	}

	if first.TxnStartTs != 464039078055379012 {
		t.Errorf("TxnStartTs mismatch: got %d, expected 464039078055379012", first.TxnStartTs)
	}

	// Check for records with zero time
	zeroTimeCount := 0
	for i, log := range slowLogs {
		if log.Time.IsZero() {
			zeroTimeCount++
			t.Logf("Record %d has zero time: Query=%s", i, log.Query)
		}
	}

	if zeroTimeCount > 0 {
		t.Errorf("Found %d records with zero time", zeroTimeCount)
	}

	// Check all records have valid time
	for i, log := range slowLogs {
		if log.Time.IsZero() {
			t.Errorf("Record %d has zero time", i)
		}
		if log.Time.Before(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)) {
			t.Errorf("Record %d has invalid time: %v", i, log.Time)
		}
	}
}

func TestCSVOutput(t *testing.T) {
	// Open the test log file
	file, err := os.Open("1770168784-821a5d37-488b-439e-ba01-30654f0c52d0.log")
	if err != nil {
		t.Fatalf("Failed to open test file: %v", err)
	}
	defer file.Close()

	parser := NewStreamParser(file)
	var slowLogs []SlowLog

	for {
		record, err := parser.ReadNext()
		if err != nil {
			continue
		}
		if record == nil {
			break
		}
		slowLogs = append(slowLogs, *record)
	}

	if len(slowLogs) == 0 {
		t.Fatal("No records parsed")
	}

	// Test CSV writing
	tmpFile := "/tmp/test_slowlog.csv"
	defer os.Remove(tmpFile)

	if err := writeSlowLogsToCSV(tmpFile, slowLogs); err != nil {
		t.Fatalf("Failed to write CSV: %v", err)
	}

	// Verify file exists and has content
	info, err := os.Stat(tmpFile)
	if err != nil {
		t.Fatalf("CSV file not created: %v", err)
	}

	if info.Size() == 0 {
		t.Fatal("CSV file is empty")
	}

	t.Logf("CSV file created successfully: %d bytes, %d records", info.Size(), len(slowLogs))

	// Verify first record has correct fields
	first := slowLogs[0]
	if first.Time.IsZero() {
		t.Error("First record has zero time")
	}
	if first.User == "" {
		t.Error("First record has empty User")
	}
	if first.Host == "" {
		t.Error("First record has empty Host")
	}
	if first.Query == "" {
		t.Error("First record has empty Query")
	}
	if first.ConnID == 0 {
		t.Error("First record has zero ConnID")
	}

	t.Logf("First record validation passed:")
	t.Logf("  Time: %v", first.Time.Format(time.RFC3339))
	t.Logf("  User: %s", first.User)
	t.Logf("  Host: %s", first.Host)
	t.Logf("  ConnID: %d", first.ConnID)
	t.Logf("  Query: %s", first.Query[:50]+"...")
}

func TestParseSlowLogFile2(t *testing.T) {
	// Test the second log file
	file, err := os.Open("1770206288-0f1fcbc5-29ef-4ade-a0e4-25f2a2c7d9e6.log")
	if err != nil {
		t.Fatalf("Failed to open test file: %v", err)
	}
	defer file.Close()

	parser := NewStreamParser(file)
	var slowLogs []SlowLog

	for {
		record, err := parser.ReadNext()
		if err != nil {
			t.Logf("Parse error (continuing): %v", err)
			continue
		}
		if record == nil {
			break
		}
		slowLogs = append(slowLogs, *record)
	}

	t.Logf("Parsed %d slow log records", len(slowLogs))

	if len(slowLogs) == 0 {
		t.Fatal("No records parsed")
	}

	// Check last record
	last := slowLogs[len(slowLogs)-1]
	t.Logf("Last record:")
	t.Logf("  Time: %v", last.Time)
	t.Logf("  User: %s", last.User)
	t.Logf("  Host: %s", last.Host)
	t.Logf("  ConnID: %d", last.ConnID)
	t.Logf("  Query: %s", last.Query)
	t.Logf("  Query length: %d", len(last.Query))

	if last.Query == "" {
		t.Error("Last record has empty Query field")
	}

	// Check all records
	for i, log := range slowLogs {
		if log.Query == "" {
			t.Errorf("Record %d has empty Query", i)
		}
		if log.Time.IsZero() {
			t.Errorf("Record %d has zero time", i)
		}
	}
}

func TestParseDeployType(t *testing.T) {
	tests := []struct {
		input   string
		want    string
		wantErr bool
	}{
		{"", deployTypeServerless, false},
		{"serverless", deployTypeServerless, false},
		{"dedicated", deployTypeDedicated, false},
		{"premium", deployTypePremium, false},
		{" Premium ", deployTypePremium, false},
		{"invalid", "", true},
	}
	for _, tt := range tests {
		got, err := parseDeployType(tt.input)
		if tt.wantErr {
			if err == nil {
				t.Fatalf("parseDeployType(%q) expected error", tt.input)
			}
			continue
		}
		if err != nil {
			t.Fatalf("parseDeployType(%q) unexpected error: %v", tt.input, err)
		}
		if got != tt.want {
			t.Fatalf("parseDeployType(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestParseLogKind(t *testing.T) {
	tests := []struct {
		input   string
		want    string
		wantErr bool
	}{
		{"", logKindSlowlog, false},
		{"slowquery", logKindSlowlog, false},
		{"slowlog", logKindSlowlog, false},
		{"sqlstatement", logKindStatement, false},
		{"statement", logKindStatement, false},
		{"invalid", "", true},
	}
	for _, tt := range tests {
		got, err := parseLogKind(tt.input)
		if tt.wantErr {
			if err == nil {
				t.Fatalf("parseLogKind(%q) expected error", tt.input)
			}
			continue
		}
		if err != nil {
			t.Fatalf("parseLogKind(%q) unexpected error: %v", tt.input, err)
		}
		if got != tt.want {
			t.Fatalf("parseLogKind(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestBuildAllInOneSrcPrefix(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"0/prod-devtier/serverless", "0/prod-devtier/serverless-all-in-one"},
		{"0/prod-devtier/serverless/", "0/prod-devtier/serverless-all-in-one"},
		{"0/prod-devtier/serverless-all-in-one", "0/prod-devtier/serverless-all-in-one"},
	}
	for _, tt := range tests {
		got := buildAllInOneSrcPrefix(tt.input)
		if got != tt.want {
			t.Fatalf("buildAllInOneSrcPrefix(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}

func TestBuildSourceLogPath(t *testing.T) {
	srcPrefix = "0/prod-devtier/serverless"
	cfg := func(deployType, clusterID, logKind string) *ClusterTaskConfig {
		return &ClusterTaskConfig{
			TenantID:   "tenant-1",
			ProjectID:  "project-1",
			ClusterID:  clusterID,
			DeployType: deployType,
			LogKind:    logKind,
		}
	}

	tests := []struct {
		name       string
		deployType string
		clusterID  string
		logKind    string
		want       string
	}{
		{
			name:       "serverless slowlog",
			deployType: deployTypeServerless,
			clusterID:  "cluster-1",
			logKind:    logKindSlowlog,
			want:       "0/prod-devtier/serverless/tenant-1/project-1/cluster-1/logs/slowlog/",
		},
		{
			name:       "dedicated statement",
			deployType: deployTypeDedicated,
			clusterID:  "cluster-1",
			logKind:    logKindStatement,
			want:       "0/prod-devtier/serverless/tenant-1/project-1/cluster-1/logs/statement/",
		},
		{
			name:       "premium statement",
			deployType: deployTypePremium,
			clusterID:  "cluster-1",
			logKind:    logKindStatement,
			want:       "0/prod-devtier/serverless/cluster-1/logs/statement/",
		},
		{
			name:       "serverless all statement",
			deployType: deployTypeServerless,
			clusterID:  clusterIDAll,
			logKind:    logKindStatement,
			want:       "0/prod-devtier/serverless-all-in-one/logs/statement/",
		},
		{
			name:       "serverless all slowlog",
			deployType: deployTypeServerless,
			clusterID:  clusterIDAll,
			logKind:    logKindSlowlog,
			want:       "0/prod-devtier/serverless-all-in-one/logs/slowlog/",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := buildSourceLogPath(cfg(tt.deployType, tt.clusterID, tt.logKind))
			if got != tt.want {
				t.Fatalf("buildSourceLogPath() = %q, want %q", got, tt.want)
			}
		})
	}
}

func TestBuildStoragePathsAllStatement(t *testing.T) {
	srcPrefix = "0/prod-devtier/serverless"
	shareDataPrefix = "share_data"
	cfg := &ClusterTaskConfig{
		TenantID:   "1372813089197511287",
		ClusterID:  clusterIDAll,
		DeployType: deployTypeServerless,
		LogKind:    logKindStatement,
	}
	paths := buildStoragePaths(cfg)
	wantSource := "0/prod-devtier/serverless-all-in-one/logs/statement/"
	wantTarget := "log_data/data/1372813089197511287/all/statement/"
	wantMark := "share_data/meta/1372813089197511287/all/logs/statement/pos/"
	if paths.SourceLogPath != wantSource {
		t.Fatalf("SourceLogPath = %q, want %q", paths.SourceLogPath, wantSource)
	}
	if paths.TargetFilePath != wantTarget {
		t.Fatalf("TargetFilePath = %q, want %q", paths.TargetFilePath, wantTarget)
	}
	if paths.SourceMarkPointPath != wantMark {
		t.Fatalf("SourceMarkPointPath = %q, want %q", paths.SourceMarkPointPath, wantMark)
	}
}

func TestCreateTaskConfigsFromConfigMapAll(t *testing.T) {
	vendor = "aws"
	region = "us-west-2"
	srcBucket = "src"
	dstBucket = "dst"
	cfg := &Config{
		Tenants: map[string]map[string]TenantConf{
			"1372813089197511287": {
				"all": {
					DataFormat: "gzip-csv",
					Frequency:  "5m",
					TaskType:   "sqlstatement",
				},
			},
		},
	}
	configs, err := createTaskConfigsFromConfigMap(cfg)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(configs) != 1 {
		t.Fatalf("expected 1 config, got %d", len(configs))
	}
	c := configs[0]
	if c.LogKind != logKindStatement {
		t.Fatalf("LogKind = %q, want %q", c.LogKind, logKindStatement)
	}
	if c.FilterTenantID != "1372813089197511287" {
		t.Fatalf("FilterTenantID = %q, want tenant id", c.FilterTenantID)
	}
	if c.DeployType != deployTypeServerless {
		t.Fatalf("DeployType = %q, want serverless", c.DeployType)
	}
}

func TestBatchTimeRangeSkipsZeroTime(t *testing.T) {
	valid := time.Date(2025, 12, 9, 21, 57, 12, 0, time.UTC)
	batch := []SlowLog{
		{Time: time.Time{}},
		{Time: valid},
		{Time: valid.Add(time.Minute)},
	}

	start, end := batchTimeRange(batch)
	if start != valid {
		t.Fatalf("start = %v, want %v", start, valid)
	}
	if end != valid.Add(time.Minute) {
		t.Fatalf("end = %v, want %v", end, valid.Add(time.Minute))
	}
}
