//go:build integration
// +build integration

package integration_test

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/everything-on-mysql/cassandra-on-mysql/pkg/cql"
)

func TestCustomIndexCreate(t *testing.T) {
	session := newSession(t, sessionOptions{disableHostLookup: true})

	keyspace := uniqueKeyspace()
	if err := session.Query(createKeyspaceCQL(keyspace)).Exec(); err != nil {
		t.Fatalf("create keyspace failed: %v", err)
	}
	t.Cleanup(func() {
		_ = session.Query(dropKeyspaceCQL(keyspace)).Exec()
	})

	table := "custom_idx_items"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int, name ascii, idx int, PRIMARY KEY (id))",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	index := "custom_name_idx"
	createIndex := fmt.Sprintf(
		"CREATE CUSTOM INDEX %s ON %s.%s (name) USING 'sai' WITH OPTIONS = {'mode': 'CONTAINS'}",
		index,
		keyspace,
		table,
	)

	if err := session.Query(createIndex).Exec(); err != nil {
		t.Fatalf("create custom index failed: %v", err)
	}
	if err := session.Query(createIndex).Exec(); err != nil {
		requireErrorCode(t, err, cql.ErrorCodeAlreadyExists)
	} else {
		t.Fatalf("expected duplicate custom index to fail")
	}
}

func TestDescribeIndexAndDuplicateColumn(t *testing.T) {
	session := newSession(t, sessionOptions{disableHostLookup: true})

	keyspace := uniqueKeyspace()
	if err := session.Query(createKeyspaceCQL(keyspace)).Exec(); err != nil {
		t.Fatalf("create keyspace failed: %v", err)
	}
	t.Cleanup(func() {
		_ = session.Query(dropKeyspaceCQL(keyspace)).Exec()
	})

	table := "index_visibility"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int, value int, PRIMARY KEY (id))",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	index := "idx_value"
	if err := session.Query(createIndexCQL(index, keyspace, table, "value")).Exec(); err != nil {
		t.Fatalf("create index failed: %v", err)
	}

	descQuery := fmt.Sprintf("DESCRIBE TABLE %s.%s", keyspace, table)
	waitFor(t, "describe table includes index", 5*time.Second, func() (bool, error) {
		rows, err := scanRows(session, descQuery)
		if err != nil {
			return false, err
		}
		if len(rows) == 0 {
			return false, nil
		}
		stmt := asString(rows[0]["create_statement"])
		return strings.Contains(stmt, "CREATE INDEX "+index), nil
	})

	dup := "idx_value2"
	err := session.Query(createIndexCQL(dup, keyspace, table, "value")).Exec()
	requireErrorCode(t, err, cql.ErrorCodeInvalid)
	if err == nil || !strings.Contains(err.Error(), "duplicate of existing index "+index) {
		t.Fatalf("unexpected duplicate index error: %v", err)
	}
}

func TestMultiColumnIndexCreateAndQuery(t *testing.T) {
	session := newSession(t, sessionOptions{disableHostLookup: true})

	keyspace := uniqueKeyspace()
	if err := session.Query(createKeyspaceCQL(keyspace)).Exec(); err != nil {
		t.Fatalf("create keyspace failed: %v", err)
	}
	t.Cleanup(func() {
		_ = session.Query(dropKeyspaceCQL(keyspace)).Exec()
	})

	table := "multi_idx_items"
	createTable := fmt.Sprintf(
		"CREATE TABLE %s.%s (id int PRIMARY KEY, user_id int, scenario int, is_archived boolean, unread_count int, last_message_time timestamp, is_pinned boolean, session_type int)",
		keyspace,
		table,
	)
	if err := session.Query(createTable).Exec(); err != nil {
		t.Fatalf("create table failed: %v", err)
	}

	index := "idx_multi_cols"
	createIndex := fmt.Sprintf(
		"CREATE INDEX %s ON %s.%s (user_id, scenario, is_archived)",
		index,
		keyspace,
		table,
	)
	if err := session.Query(createIndex).Exec(); err != nil {
		t.Fatalf("create multi-column index failed: %v", err)
	}

	descQuery := fmt.Sprintf("DESCRIBE TABLE %s.%s", keyspace, table)
	waitFor(t, "describe table includes multi-column index", 5*time.Second, func() (bool, error) {
		rows, err := scanRows(session, descQuery)
		if err != nil {
			return false, err
		}
		if len(rows) == 0 {
			return false, nil
		}
		stmt := asString(rows[0]["create_statement"])
		return strings.Contains(stmt, "CREATE INDEX "+index+" ON "+keyspace+"."+table+" (user_id, scenario, is_archived)"), nil
	})

	insert := fmt.Sprintf("INSERT INTO %s.%s (id, user_id, scenario, is_archived, unread_count, session_type) VALUES (?, ?, ?, ?, ?, ?)", keyspace, table)
	if err := session.Query(insert, 1, 10, 1, false, 3, 2).Exec(); err != nil {
		t.Fatalf("insert row1 failed: %v", err)
	}
	if err := session.Query(insert, 2, 10, 2, true, 1, 2).Exec(); err != nil {
		t.Fatalf("insert row2 failed: %v", err)
	}

	var id int
	selectEq := fmt.Sprintf("SELECT id FROM %s.%s WHERE user_id = ? AND scenario = ?", keyspace, table)
	if err := session.Query(selectEq, 10, 1).Scan(&id); err != nil {
		t.Fatalf("select equality query failed: %v", err)
	}
	if id != 1 {
		t.Fatalf("unexpected id: %d", id)
	}

	rows, err := scanRows(session, fmt.Sprintf("SELECT id FROM %s.%s WHERE user_id = 10 AND scenario > 1", keyspace, table))
	if err != nil {
		t.Fatalf("select range query failed: %v", err)
	}
	if len(rows) != 1 || asString(rows[0]["id"]) != "2" {
		t.Fatalf("unexpected range results: %+v", rows)
	}
}
