// Copyright 2024 CloudDetail
// SPDX-License-Identifier: Apache-2.0

package clickhouse

import (
	"fmt"
)

type FieldBuilder struct {
	fields []string
}

type QueryBuilder struct {
	where  []string
	values []interface{}
}

type ByLimitBuilder struct {
	groupBy []string
	order   []string
	limit   int
	offset  int
}

func NewFieldBuilder() *FieldBuilder {
	return &FieldBuilder{
		fields: make([]string, 0),
	}
}

func NewQueryBuilder() *QueryBuilder {
	return &QueryBuilder{
		where:  make([]string, 0),
		values: make([]interface{}, 0),
	}
}

func NewByLimitBuilder() *ByLimitBuilder {
	return &ByLimitBuilder{
		order:   make([]string, 0),
		groupBy: make([]string, 0),
		limit:   0,
		offset:  0,
	}
}

func (builder *FieldBuilder) Alias(key string, alias string) *FieldBuilder {
	builder.fields = append(builder.fields, fmt.Sprintf("%s as %s", key, alias))
	return builder
}

func (builder *FieldBuilder) Fields(keys ...string) *FieldBuilder {
	builder.fields = append(builder.fields, keys...)
	return builder
}

// Return the search field
func (builder *FieldBuilder) String() string {
	labels := ""
	for i, field := range builder.fields {
		if i > 0 {
			labels += ", "
		}
		labels += field
	}
	return labels
}

func (builder *QueryBuilder) Between(key string, from interface{}, to interface{}) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s BETWEEN ? AND ?", key))
	builder.values = append(builder.values, from, to)
	return builder
}

func (builder *QueryBuilder) Equals(key string, value interface{}) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s = ?", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) NotEquals(key string, value interface{}) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s != ?", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) GreaterThan(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s > ?", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) NotGreaterThan(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s <= ?", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) LessThan(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s < ?", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) NotLessThan(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s >= ?", key))
	builder.values = append(builder.values, value)
	return builder
}

// The key in (values) statement in the SQL statement is generated by combination. The value array is inside the value array.
func (builder *QueryBuilder) In(key string, values any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s IN ?", key))
	builder.values = append(builder.values, values)
	return builder
}

func (builder *QueryBuilder) NotIn(key string, values any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s NOT IN ?", key))
	builder.values = append(builder.values, values)
	return builder
}

func (builder *QueryBuilder) Like(key string, values any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s LIKE ?", key))
	builder.values = append(builder.values, values)
	return builder
}

func (builder *QueryBuilder) NotLike(key string, values any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s NOT LIKE ?", key))
	builder.values = append(builder.values, values)
	return builder
}

func (builder *QueryBuilder) Exists(key string) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s EXISTS", key))
	return builder
}

func (builder *QueryBuilder) NotExists(key string) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s NOT EXISTS", key))
	return builder
}

func (builder *QueryBuilder) Contains(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("POSITION(%s, ?) > 0", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) NotContains(key string, value any) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("POSITION(%s, ?) = 0", key))
	builder.values = append(builder.values, value)
	return builder
}

func (builder *QueryBuilder) InStrings(key string, values []string) *QueryBuilder {
	builder.where = append(builder.where, fmt.Sprintf("%s in ?", key))
	builder.values = append(builder.values, values)
	return builder
}

// And Add a series of conditional whereSQL to the QueryBuilder as And
// Nil is treated as ALWAYS_FALSE
func (builder *QueryBuilder) And(where *whereSQL) *QueryBuilder {
	if where == nil || where == ALWAYS_FALSE {
		builder.where = append(builder.where, "FALSE")
		return builder
	} else if where == ALWAYS_TRUE {
		return builder
	}
	builder.where = append(builder.where, where.Wheres)
	builder.values = append(builder.values, where.Values...)
	return builder
}

func (builder *QueryBuilder) EqualsNotEmpty(key string, value string) *QueryBuilder {
	if value != "" {
		builder.where = append(builder.where, fmt.Sprintf("%s = ?", key))
		builder.values = append(builder.values, value)
	}
	return builder
}

func (builder *QueryBuilder) Statement(where string) *QueryBuilder {
	builder.where = append(builder.where, where)
	return builder
}

// Return the query condition
func (builder *QueryBuilder) String() string {
	whereSql := ""
	for i, where := range builder.where {
		if i == 0 {
			whereSql += "WHERE "
		} else {
			whereSql += " AND "
		}
		whereSql += where
	}
	return whereSql
}

func (builder *ByLimitBuilder) GroupBy(keys ...string) *ByLimitBuilder {
	builder.groupBy = append(builder.groupBy, keys...)
	return builder
}

func (builder *ByLimitBuilder) OrderBy(key string, asc bool) *ByLimitBuilder {
	order := "DESC"
	if asc {
		order = "ASC"
	}
	builder.order = append(builder.order, fmt.Sprintf("%s %s", key, order))
	return builder
}

func (builder *ByLimitBuilder) Limit(limit int) *ByLimitBuilder {
	builder.limit = limit
	return builder
}

func (builder *ByLimitBuilder) Offset(offset int) *ByLimitBuilder {
	builder.offset = offset
	return builder
}

// Return GroupBy, OrderBy, and Limit
func (builder *ByLimitBuilder) String() string {
	sql := ""
	for i, key := range builder.groupBy {
		if i == 0 {
			sql += " GROUP BY "
		} else {
			sql += ", "
		}
		sql += key
	}
	for i, order := range builder.order {
		if i == 0 {
			sql += " ORDER BY "
		} else {
			sql += ", "
		}
		sql += order
	}
	if builder.limit > 0 {
		sql += fmt.Sprintf(" LIMIT %d", builder.limit)
	}
	if builder.offset > 0 {
		sql += fmt.Sprintf(" OFFSET %d", builder.offset)
	}
	return sql
}

func NewQueryCondition(st, et int64, timeField, query string) string {
	return fmt.Sprintf("`%s` >= toDateTime(%d) AND `%s` < toDateTime(%d) AND %s", timeField, st/1000000, timeField, et/1000000, query)
}
