package mongo

import (
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// Index represents a MongoDB index
type Index struct {
	Name    string                `json:"name"`
	Keys    bson.D                `json:"keys"`
	Options *options.IndexOptions `json:"options"`
}

// NewIndex creates a new index with default options
func NewIndex(name string, keys bson.D) *Index {
	return &Index{
		Name:    name,
		Keys:    keys,
		Options: options.Index(),
	}
}

// WithUnique sets the unique option for the index
func (i *Index) WithUnique(unique bool) *Index {
	i.Options.SetUnique(unique)
	return i
}

// WithSparse sets the sparse option for the index
func (i *Index) WithSparse(sparse bool) *Index {
	i.Options.SetSparse(sparse)
	return i
}

// WithBackground sets the background option for the index
func (i *Index) WithBackground(background bool) *Index {
	i.Options.SetBackground(background)
	return i
}

// WithExpireAfterSeconds sets the TTL for the index
func (i *Index) WithExpireAfterSeconds(seconds int32) *Index {
	i.Options.SetExpireAfterSeconds(seconds)
	return i
}

// WithPartialFilterExpression sets the partial filter expression for the index
func (i *Index) WithPartialFilterExpression(filter bson.M) *Index {
	i.Options.SetPartialFilterExpression(filter)
	return i
}

// WithCollation sets the collation for the index
func (i *Index) WithCollation(collation *options.Collation) *Index {
	i.Options.SetCollation(collation)
	return i
}

// Common index builders

// CreateUniqueIndex creates a unique index
func CreateUniqueIndex(name string, keys bson.D) *Index {
	return NewIndex(name, keys).WithUnique(true)
}

// CreateTTLIndex creates a TTL index
func CreateTTLIndex(name string, field string, expireAfterSeconds int32) *Index {
	return NewIndex(name, bson.D{{Key: field, Value: 1}}).WithExpireAfterSeconds(expireAfterSeconds)
}

// CreateTextIndex creates a text index
func CreateTextIndex(name string, fields ...string) *Index {
	keys := bson.D{}
	for _, field := range fields {
		keys = append(keys, bson.E{Key: field, Value: "text"})
	}
	return NewIndex(name, keys)
}

// CreateCompoundIndex creates a compound index
func CreateCompoundIndex(name string, fields map[string]int) *Index {
	keys := bson.D{}
	for field, direction := range fields {
		keys = append(keys, bson.E{Key: field, Value: direction})
	}
	return NewIndex(name, keys)
}

// CreateGeospatialIndex creates a 2dsphere geospatial index
func CreateGeospatialIndex(name string, field string) *Index {
	return NewIndex(name, bson.D{{Key: field, Value: "2dsphere"}})
}

// CreateHashedIndex creates a hashed index
func CreateHashedIndex(name string, field string) *Index {
	return NewIndex(name, bson.D{{Key: field, Value: "hashed"}})
}

// Common index patterns

// DefaultIndexes returns common default indexes for most collections
func DefaultIndexes() []Index {
	return []Index{
		*CreateUniqueIndex("_id_", bson.D{{Key: "_id", Value: 1}}),
		*NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
		*NewIndex("updated_at_idx", bson.D{{Key: "updated_at", Value: -1}}),
	}
}

// UserIndexes returns common indexes for user collections
func UserIndexes() []Index {
	return []Index{
		*CreateUniqueIndex("_id_", bson.D{{Key: "_id", Value: 1}}),
		*CreateUniqueIndex("email_idx", bson.D{{Key: "email", Value: 1}}),
		*NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
		*NewIndex("updated_at_idx", bson.D{{Key: "updated_at", Value: -1}}),
		*NewIndex("is_active_idx", bson.D{{Key: "is_active", Value: 1}}),
	}
}

// TenderIndexes returns common indexes for tender collections
func TenderIndexes() []Index {
	return []Index{
		*CreateUniqueIndex("_id_", bson.D{{Key: "_id", Value: 1}}),
		*NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
		*NewIndex("updated_at_idx", bson.D{{Key: "updated_at", Value: -1}}),
		*NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
		*NewIndex("deadline_idx", bson.D{{Key: "deadline", Value: 1}}),
		*NewIndex("category_idx", bson.D{{Key: "category", Value: 1}}),
		*NewIndex("location_idx", bson.D{{Key: "location", Value: 1}}),
	}
}

// CompanyIndexes returns common indexes for company collections
func CompanyIndexes() []Index {
	return []Index{
		*CreateUniqueIndex("_id_", bson.D{{Key: "_id", Value: 1}}),
		*CreateUniqueIndex("name_idx", bson.D{{Key: "name", Value: 1}}),
		*NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
		*NewIndex("updated_at_idx", bson.D{{Key: "updated_at", Value: -1}}),
		*NewIndex("is_active_idx", bson.D{{Key: "is_active", Value: 1}}),
	}
}

// QueryBuilder provides a fluent interface for building MongoDB queries
type QueryBuilder struct {
	filter bson.M
}

// NewQueryBuilder creates a new query builder
func NewQueryBuilder() *QueryBuilder {
	return &QueryBuilder{
		filter: bson.M{},
	}
}

// Where adds a simple equality condition
func (qb *QueryBuilder) Where(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = value
	return qb
}

// WhereIn adds an $in condition
func (qb *QueryBuilder) WhereIn(field string, values []interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$in": values}
	return qb
}

// WhereNotIn adds a $nin condition
func (qb *QueryBuilder) WhereNotIn(field string, values []interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$nin": values}
	return qb
}

// WhereGreaterThan adds a $gt condition
func (qb *QueryBuilder) WhereGreaterThan(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$gt": value}
	return qb
}

// WhereGreaterThanOrEqual adds a $gte condition
func (qb *QueryBuilder) WhereGreaterThanOrEqual(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$gte": value}
	return qb
}

// WhereLessThan adds a $lt condition
func (qb *QueryBuilder) WhereLessThan(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$lt": value}
	return qb
}

// WhereLessThanOrEqual adds a $lte condition
func (qb *QueryBuilder) WhereLessThanOrEqual(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$lte": value}
	return qb
}

// WhereNotEqual adds a $ne condition
func (qb *QueryBuilder) WhereNotEqual(field string, value interface{}) *QueryBuilder {
	qb.filter[field] = bson.M{"$ne": value}
	return qb
}

// WhereExists adds an $exists condition
func (qb *QueryBuilder) WhereExists(field string, exists bool) *QueryBuilder {
	qb.filter[field] = bson.M{"$exists": exists}
	return qb
}

// WhereRegex adds a $regex condition
func (qb *QueryBuilder) WhereRegex(field string, pattern string, options string) *QueryBuilder {
	qb.filter[field] = bson.M{"$regex": pattern, "$options": options}
	return qb
}

// WhereText adds a $text condition
func (qb *QueryBuilder) WhereText(search string) *QueryBuilder {
	qb.filter["$text"] = bson.M{"$search": search}
	return qb
}

// WhereAnd adds an $and condition
func (qb *QueryBuilder) WhereAnd(conditions ...bson.M) *QueryBuilder {
	qb.filter["$and"] = conditions
	return qb
}

// WhereOr adds an $or condition
func (qb *QueryBuilder) WhereOr(conditions ...bson.M) *QueryBuilder {
	qb.filter["$or"] = conditions
	return qb
}

// WhereNor adds a $nor condition
func (qb *QueryBuilder) WhereNor(conditions ...bson.M) *QueryBuilder {
	qb.filter["$nor"] = conditions
	return qb
}

// Build returns the final filter
func (qb *QueryBuilder) Build() bson.M {
	return qb.filter
}

// PaginationBuilder provides a fluent interface for building pagination options
type PaginationBuilder struct {
	pagination Pagination
}

// NewPaginationBuilder creates a new pagination builder
func NewPaginationBuilder() *PaginationBuilder {
	return &PaginationBuilder{
		pagination: Pagination{
			Limit:     10,
			SortField: "_id",
			SortOrder: -1,
		},
	}
}

// Limit sets the limit
func (pb *PaginationBuilder) Limit(limit int) *PaginationBuilder {
	pb.pagination.Limit = limit
	return pb
}

// Skip sets the skip value for offset-based pagination
func (pb *PaginationBuilder) Skip(skip int) *PaginationBuilder {
	pb.pagination.Skip = skip
	return pb
}

// Cursor sets the cursor for cursor-based pagination
func (pb *PaginationBuilder) Cursor(cursor string) *PaginationBuilder {
	pb.pagination.Cursor = cursor
	return pb
}

// SortBy sets the sort field and order
func (pb *PaginationBuilder) SortBy(field string, order string) *PaginationBuilder {
	pb.pagination.SortField = field
	if order == "asc" {
		pb.pagination.SortOrder = 1
	} else {
		pb.pagination.SortOrder = -1
	}
	return pb
}

// SortAsc sorts in ascending order
func (pb *PaginationBuilder) SortAsc(field string) *PaginationBuilder {
	return pb.SortBy(field, "asc")
}

// SortDesc sorts in descending order
func (pb *PaginationBuilder) SortDesc(field string) *PaginationBuilder {
	return pb.SortBy(field, "desc")
}

// Build returns the final pagination options
func (pb *PaginationBuilder) Build() Pagination {
	return pb.pagination
}
