package export

import (
	"testing"

	"github.com/stretchr/testify/require"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestValidateSnapshotTSO(t *testing.T) {
	tests := []struct {
		name        string
		snapshotTSO uint64
		wantErr     bool
	}{
		{
			name:        "rejects zero when explicitly specified",
			snapshotTSO: 0,
			wantErr:     true,
		},
		{
			name:        "rejects a TSO without physical timestamp",
			snapshotTSO: 1,
			wantErr:     true,
		},
		{
			name:        "accepts a TSO with logical bits",
			snapshotTSO: (uint64(1710000000123) << tsoPhysicalShiftBits) | 42,
			wantErr:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateSnapshotTSO(tt.snapshotTSO)
			if tt.wantErr {
				require.Error(t, err)
				return
			}
			require.NoError(t, err)
		})
	}
}

func TestValidateCSVDialect(t *testing.T) {
	unknownDialect := globalngv1.ExportOptions_ExportCSVDialect_Enum(99)

	tests := []struct {
		name      string
		csvFormat *globalngv1.ExportOptions_CSVFormat
		wantErr   bool
	}{
		{
			name: "accepts omitted CSV format",
		},
		{
			name:      "accepts omitted dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{},
		},
		{
			name:      "accepts default dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: globalngv1.ExportOptions_ExportCSVDialect_DEFAULT.Enum()},
		},
		{
			name:      "accepts Snowflake dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: globalngv1.ExportOptions_ExportCSVDialect_SNOWFLAKE.Enum()},
		},
		{
			name:      "accepts Redshift dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: globalngv1.ExportOptions_ExportCSVDialect_REDSHIFT.Enum()},
		},
		{
			name:      "accepts BigQuery dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: globalngv1.ExportOptions_ExportCSVDialect_BIGQUERY.Enum()},
		},
		{
			name:      "rejects explicitly unspecified dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: globalngv1.ExportOptions_ExportCSVDialect_EXPORT_CSV_DIALECT_UNSPECIFIED.Enum()},
			wantErr:   true,
		},
		{
			name:      "rejects unknown dialect",
			csvFormat: &globalngv1.ExportOptions_CSVFormat{Dialect: &unknownDialect},
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateCSVDialect(tt.csvFormat)
			if tt.wantErr {
				require.Error(t, err)
				require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())
				return
			}
			require.NoError(t, err)
		})
	}
}

func TestValidateParquetOptions(t *testing.T) {
	unknownCompression := globalngv1.ExportParquetCompressionType_Enum(99)
	tests := []struct {
		name    string
		opts    *globalngv1.ExportOptions
		wantErr bool
	}{
		{
			name: "accepts omitted parquet format and Dumpling default",
			opts: &globalngv1.ExportOptions{FileType: globalngv1.ExportOptions_PARQUET, Compression: globalngv1.ExportOptions_NONE},
		},
		{
			name: "accepts explicit parquet compression",
			opts: &globalngv1.ExportOptions{
				FileType:      globalngv1.ExportOptions_PARQUET,
				Compression:   globalngv1.ExportOptions_NONE,
				ParquetFormat: &globalngv1.ExportOptions_ParquetFormat{Compression: globalngv1.ExportParquetCompressionType_ZSTD},
			},
		},
		{
			name:    "rejects general compression for parquet",
			opts:    &globalngv1.ExportOptions{FileType: globalngv1.ExportOptions_PARQUET, Compression: globalngv1.ExportOptions_GZIP},
			wantErr: true,
		},
		{
			name: "rejects parquet format for CSV",
			opts: &globalngv1.ExportOptions{
				FileType:      globalngv1.ExportOptions_CSV,
				Compression:   globalngv1.ExportOptions_NONE,
				ParquetFormat: &globalngv1.ExportOptions_ParquetFormat{Compression: globalngv1.ExportParquetCompressionType_SNAPPY},
			},
			wantErr: true,
		},
		{
			name: "rejects unknown parquet compression",
			opts: &globalngv1.ExportOptions{
				FileType:      globalngv1.ExportOptions_PARQUET,
				Compression:   globalngv1.ExportOptions_NONE,
				ParquetFormat: &globalngv1.ExportOptions_ParquetFormat{Compression: unknownCompression},
			},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validateParquetOptions(tt.opts)
			if tt.wantErr {
				require.Error(t, err)
				require.Equal(t, bizerr.ErrCodeParams, err.ErrCode())
				return
			}
			require.NoError(t, err)
		})
	}
}
