package dialect

import (
	"testing"
	"time"

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

func TestExtractLiteralDateScope(t *testing.T) {
	tests := []struct {
		name      string
		predicate string
		start     string
		end       string
	}{
		{name: "equals", predicate: "date = '2026-07-30'", start: "2026-07-30", end: "2026-07-31"},
		{name: "qualified comparisons", predicate: "t.date >= '2026-07-30' AND t.date <= '2026-08-02'", start: "2026-07-30", end: "2026-08-03"},
		{name: "exclusive comparisons", predicate: "date > '2026-07-30' AND date < '2026-08-02'", start: "2026-07-31", end: "2026-08-02"},
		{name: "reversed operands", predicate: "'2026-07-30' <= date AND '2026-08-02' >= date", start: "2026-07-30", end: "2026-08-03"},
		{name: "between", predicate: "date BETWEEN '2026-07-30' AND '2026-08-02'", start: "2026-07-30", end: "2026-08-03"},
		{name: "in", predicate: "date IN ('2026-07-30', '2026-08-02')", start: "2026-07-30", end: "2026-08-03"},
		{name: "bounded or", predicate: "date = '2026-07-30' OR date = '2026-08-02'", start: "2026-07-30", end: "2026-08-03"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			scope, err := ExtractLiteralDateScope("SELECT * FROM topsql t WHERE "+tt.predicate, "date", 31)
			require.NoError(t, err)
			require.Equal(t, mustDate(t, tt.start), scope.Start)
			require.Equal(t, mustDate(t, tt.end), scope.End)
		})
	}
}

func TestExtractLiteralDateScopeRejectsUnsafeRanges(t *testing.T) {
	tests := []struct {
		name      string
		predicate string
		contains  string
	}{
		{name: "missing upper", predicate: "date >= '2026-07-30'", contains: "finite literal"},
		{name: "missing lower", predicate: "date < '2026-08-02'", contains: "finite literal"},
		{name: "dynamic", predicate: "date = CURRENT_DATE()", contains: "literal YYYY-MM-DD"},
		{name: "invalid date", predicate: "date = '2026-02-30'", contains: "invalid date partition literal"},
		{name: "reversed", predicate: "date >= '2026-08-02' AND date <= '2026-07-30'", contains: "empty or reversed"},
		{name: "unbounded or", predicate: "date = '2026-07-30' OR component = 'tidb'", contains: "unbounded OR"},
		{name: "too wide", predicate: "date >= '2026-07-01' AND date <= '2026-08-01'", contains: "exceeds 31 days"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := ExtractLiteralDateScope("SELECT * FROM topsql WHERE "+tt.predicate, "date", 31)
			require.ErrorContains(t, err, tt.contains)
		})
	}
}

func TestPartitionValidationAcceptsLiteralComparisonRange(t *testing.T) {
	sql := "SELECT * FROM topsql t WHERE t.date >= '2026-07-30' AND t.date <= '2026-08-02'"
	require.NoError(t, ValidatePartitionFiltersForAllTableScans(sql, []string{"date"}))
	_, err := RewriteForEngine(sql, "bigquery", &RewriteOptions{PartitionColumns: []string{"date"}})
	require.NoError(t, err)
}

func mustDate(t *testing.T, value string) time.Time {
	t.Helper()
	date, err := time.Parse("2006-01-02", value)
	require.NoError(t, err)
	return date.UTC()
}
