package sqlschema

import (
	"strings"

	"github.com/SigNoz/signoz/pkg/valuer"
)

var (
	IndexTypeUnique = IndexType{s: valuer.NewString("uq")}
	IndexTypeIndex  = IndexType{s: valuer.NewString("ix")}
)

type IndexType struct{ s valuer.String }

func (i IndexType) String() string {
	return i.s.String()
}

type Index interface {
	// The name of the index.
	//   - Indexes are named as `ix_<table_name>_<column_names>`. The column names are separated by underscores.
	//   - Unique constraints are named as `uq_<table_name>_<column_names>`. The column names are separated by underscores.
	// The name is autogenerated and should not be set by the user.
	Name() string

	// Add name to the index. This is typically used to override the autogenerated name because the database might have a different name.
	Named(name string) Index

	// The type of the index.
	Type() IndexType

	// The columns that the index is applied to.
	Columns() []ColumnName

	// The SQL representation of the index.
	ToCreateSQL(fmter SQLFormatter) []byte

	// Drop the index.
	ToDropSQL(fmter SQLFormatter) []byte
}

type UniqueIndex struct {
	TableName   TableName
	ColumnNames []ColumnName
	name        string
}

func (index *UniqueIndex) Name() string {
	if index.name != "" {
		return index.name
	}

	var b strings.Builder
	b.WriteString(IndexTypeUnique.String())
	b.WriteString("_")
	b.WriteString(string(index.TableName))
	b.WriteString("_")
	for i, column := range index.ColumnNames {
		if i > 0 {
			b.WriteString("_")
		}
		b.WriteString(string(column))
	}
	return b.String()
}

func (index *UniqueIndex) Named(name string) Index {
	copyOfColumnNames := make([]ColumnName, len(index.ColumnNames))
	copy(copyOfColumnNames, index.ColumnNames)

	return &UniqueIndex{
		TableName:   index.TableName,
		ColumnNames: copyOfColumnNames,
		name:        name,
	}
}

func (*UniqueIndex) Type() IndexType {
	return IndexTypeUnique
}

func (index *UniqueIndex) Columns() []ColumnName {
	return index.ColumnNames
}

func (index *UniqueIndex) ToCreateSQL(fmter SQLFormatter) []byte {
	sql := []byte{}

	sql = append(sql, "CREATE UNIQUE INDEX IF NOT EXISTS "...)
	sql = fmter.AppendIdent(sql, index.Name())
	sql = append(sql, " ON "...)
	sql = fmter.AppendIdent(sql, string(index.TableName))
	sql = append(sql, " ("...)

	for i, column := range index.ColumnNames {
		if i > 0 {
			sql = append(sql, ", "...)
		}

		sql = fmter.AppendIdent(sql, string(column))
	}

	sql = append(sql, ")"...)

	return sql
}

func (index *UniqueIndex) ToDropSQL(fmter SQLFormatter) []byte {
	sql := []byte{}

	sql = append(sql, "DROP INDEX IF EXISTS "...)
	sql = fmter.AppendIdent(sql, index.Name())

	return sql
}
