// Copyright 2022 Molecula Corp. (DBA FeatureBase).
// SPDX-License-Identifier: Apache-2.0
package sql_test

import (
	"context"
	"math"
	"testing"

	pilosa "github.com/featurebasedb/featurebase/v3"
	"github.com/featurebasedb/featurebase/v3/sql"
	"github.com/featurebasedb/featurebase/v3/test"
	"vitess.io/vitess/go/vt/sqlparser"
)

func TestHandler(t *testing.T) {
	cluster := test.MustRunCluster(t, 1)
	defer cluster.Close()
	api := cluster.GetNode(0).API
	queryStr := "select * from nowhere"
	mapper := sql.NewMapper()
	query, err := mapper.MapSQL(queryStr)
	if err != nil {
		t.Fatal("failed to map SQL")
	}
	handler := sql.NewSelectHandler(api)
	_, err = handler.Handle(context.Background(), query)
	if err.Error() != "mapping select: handling: nowhere: index not found" {
		//expecting it to fail with index not found
		//can be more elaborate later
		t.Fatal(err)
	}

}

func TestSelectHandler_MapSelect(t *testing.T) {
	// it's load-bearing that this is the only cluster in this directory right
	// now, because that allows us to hardcode index names and not immediately
	// die.
	cluster := test.MustRunCluster(t, 1)
	defer cluster.Close()
	api := cluster.GetNode(0).API

	if _, err := api.CreateIndex(context.Background(), "i", pilosa.IndexOptions{}); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "i", "bytes", pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64)); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "i", "duration_time", pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64)); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "i", "timestamp", pilosa.OptFieldTypeTimestamp(pilosa.DefaultEpoch, pilosa.TimeUnitSeconds)); err != nil {
		t.Fatal(err)
	}

	if _, err := api.CreateIndex(context.Background(), "j", pilosa.IndexOptions{}); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "j", "bytes", pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64)); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "j", "bsi", pilosa.OptFieldTypeInt(math.MinInt64, math.MaxInt64), pilosa.OptFieldForeignIndex("i")); err != nil {
		t.Fatal(err)
	} else if _, err = api.CreateField(context.Background(), "j", "timestamp", pilosa.OptFieldTypeTimestamp(pilosa.DefaultEpoch, pilosa.TimeUnitSeconds)); err != nil {
		t.Fatal(err)
	}

	for _, tt := range []struct {
		name          string
		input         string
		output        string
		expectedError string
	}{
		{
			name:   "WhereTimestamp",
			input:  `SELECT * FROM i WHERE timestamp>"2000-01-01T00:00:00Z"`,
			output: `Extract(Row(timestamp>"2000-01-01T00:00:00Z"),Rows(bytes),Rows(duration_time),Rows(timestamp))`,
		},

		{
			name:   "WhereTimestampWithSpaces",
			input:  `SELECT * FROM i WHERE timestamp > "2000-01-01T00:00:00Z"`,
			output: `Extract(Row(timestamp>"2000-01-01T00:00:00Z"),Rows(bytes),Rows(duration_time),Rows(timestamp))`,
		},
		{
			name:   "InnerJoin",
			input:  `select count(*) from i INNER JOIN j ON i._id = j.bsi where i.bytes = 1 and j.bytes = 2`,
			output: `Count(Intersect(Row(bytes=1),Distinct(Row(bytes=2),index='j',field='bsi')))`,
		},
		{
			name:          "InnerJoinInvalidPrimary",
			input:         `select count(*) from z INNER JOIN j ON z._id = j.bsi where z.bytes = 1 and j.bytes = 2`,
			output:        "",
			expectedError: `handling: nonexistent index "z"`,
		},
		{
			name:          "InnerJoinInvalidSecondary",
			input:         `select count(*) from i INNER JOIN z ON i._id = z.bsi where i.bytes = 1 and z.bytes = 2`,
			output:        "",
			expectedError: `handling: nonexistent index "z"`,
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			query, err := sql.NewMapper().MapSQL(tt.input)
			if err != nil {
				t.Fatal(err)
			}

			h := sql.NewSelectHandler(api)
			mr, err := h.MapSelect(context.Background(), query.Statement.(*sqlparser.Select), query.Mask)
			if err != nil {
				if err.Error() != tt.expectedError {
					if tt.expectedError == "" {
						t.Fatalf("unexpected error %q", err.Error())
					} else {
						t.Fatalf("expected error %q, got %q", tt.expectedError, err.Error())
					}
				}
			} else if got, want := mr.Query, tt.output; got != want {
				if tt.expectedError != "" {
					t.Fatalf("expected error %q, got no error", tt.expectedError)
				}
				t.Fatalf("unexpected pql\npql:  %s\nwant: %s", got, want)
			}
		})
	}
}
