package cms

import (
	"context"
	"errors"
	"time"
	"tm/pkg/logger"
	orm "tm/pkg/mongo"
	"tm/pkg/response"

	"go.mongodb.org/mongo-driver/v2/bson"
)

// Repository defines methods for CMS data access
type Repository interface {
	Create(ctx context.Context, cms *CMS) error
	Update(ctx context.Context, cms *CMS) error
	Delete(ctx context.Context, id string) error
	GetByID(ctx context.Context, id string) (*CMS, error)
	GetByKey(ctx context.Context, key string) (*CMS, error)
	Search(ctx context.Context, search *SearchCMSForm, pagination *response.Pagination) (*orm.PaginatedResult[CMS], error)
}

// cmsRepository implements the Repository interface using the MongoDB ORM
type cmsRepository struct {
	ormRepo orm.Repository[CMS]
	logger  logger.Logger
}

// NewRepository creates a new CMS repository
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
	// Create indexes using the ORM's index management
	indexes := []orm.Index{
		*orm.CreateTextIndex("cms_search_idx", "key"),
		*orm.NewIndex("key_idx", bson.D{{Key: "key", Value: 1}}),
		*orm.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
	}

	// Create indexes
	err := mongoManager.CreateIndexes("cms", indexes)
	if err != nil {
		logger.Warn("Failed to create CMS indexes", map[string]interface{}{
			"error": err.Error(),
		})
	}

	// Create ORM repository
	ormRepo := orm.NewRepository[CMS](mongoManager.GetCollection("cms"), logger)

	return &cmsRepository{
		ormRepo: ormRepo,
		logger:  logger,
	}
}

// Create creates a new CMS entry
func (r *cmsRepository) Create(ctx context.Context, cms *CMS) error {
	// Set created timestamp using Unix timestamp
	now := time.Now().Unix()
	cms.SetCreatedAt(now)

	// Use ORM to create CMS
	err := r.ormRepo.Create(ctx, cms)
	if err != nil {
		r.logger.Error("Failed to create CMS", map[string]interface{}{
			"error": err.Error(),
			"key":   cms.Key,
		})
		return err
	}

	r.logger.Info("CMS created successfully", map[string]interface{}{
		"id":  cms.GetID(),
		"key": cms.Key,
	})

	return nil
}

// Update updates a CMS entry
func (r *cmsRepository) Update(ctx context.Context, cms *CMS) error {
	// Set updated timestamp using Unix timestamp
	cms.SetUpdatedAt(time.Now().Unix())

	// Use ORM to update CMS
	err := r.ormRepo.Update(ctx, cms)
	if err != nil {
		r.logger.Error("Failed to update CMS", map[string]interface{}{
			"error": err.Error(),
			"id":    cms.GetID(),
			"key":   cms.Key,
		})
		return err
	}

	r.logger.Info("CMS updated successfully", map[string]interface{}{
		"id":  cms.GetID(),
		"key": cms.Key,
	})

	return nil
}

// GetByID retrieves a CMS entry by ID
func (r *cmsRepository) GetByID(ctx context.Context, id string) (*CMS, error) {
	cms, err := r.ormRepo.FindByID(ctx, id)
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, ErrCMSNotFound
		}
		r.logger.Error("Failed to get CMS by ID", map[string]interface{}{
			"error": err.Error(),
			"id":    id,
		})
		return nil, err
	}

	return cms, nil
}

// GetByKey retrieves a CMS entry by key
func (r *cmsRepository) GetByKey(ctx context.Context, key string) (*CMS, error) {
	cms, err := r.ormRepo.FindOne(ctx, bson.M{"key": key})
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, ErrCMSNotFound
		}
		r.logger.Error("Failed to get CMS by key", map[string]interface{}{
			"error": err.Error(),
			"key":   key,
		})
		return nil, err
	}

	return cms, nil
}

// Delete deletes a CMS entry
func (r *cmsRepository) Delete(ctx context.Context, id string) error {
	// Get CMS first to log the key
	cms, err := r.GetByID(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete CMS", map[string]interface{}{
			"error": err.Error(),
			"id":    id,
		})
		if errors.Is(err, ErrCMSNotFound) {
			return ErrCMSNotFound
		}
		return err
	}

	// Delete from database
	err = r.ormRepo.Delete(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete CMS", map[string]interface{}{
			"error": err.Error(),
			"id":    id,
			"key":   cms.Key,
		})
		return err
	}

	r.logger.Info("CMS deleted successfully", map[string]interface{}{
		"id":  id,
		"key": cms.Key,
	})

	return nil
}

// Search searches CMS entries with filters and pagination
func (r *cmsRepository) Search(ctx context.Context, search *SearchCMSForm, pagination *response.Pagination) (*orm.PaginatedResult[CMS], error) {
	filter := bson.M{}

	if search.Search != nil && *search.Search != "" {
		filter["$or"] = bson.A{
			bson.M{"key": bson.M{"$regex": search.Search, "$options": "i"}},
		}
	}

	if search.Key != nil && *search.Key != "" {
		filter["key"] = bson.M{"$regex": search.Key, "$options": "i"}
	}

	mongoPagination, err := orm.BuildListPagination(
		pagination.Limit,
		pagination.Offset,
		pagination.Cursor,
		pagination.SortBy,
		pagination.SortOrder,
		filter,
		orm.ListPaginationOptions{},
	)
	if err != nil {
		return nil, err
	}

	result, err := r.ormRepo.FindAll(ctx, filter, mongoPagination)
	if err != nil {
		r.logger.Error("Failed to search CMS entries", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, err
	}

	return result, nil
}
