package feedback

import (
	"context"
	"time"
	"tm/pkg/logger"
	"tm/pkg/mongo"

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

// FeedbackRepository interface defines feedback data access methods
type Repository interface {
	// Basic CRUD operations
	Create(ctx context.Context, feedback *Feedback) error
	Update(ctx context.Context, feedback *Feedback) error
	GetByID(ctx context.Context, id string) (*Feedback, error)
	GetByCompanyID(ctx context.Context, companyID string, tender string) (*Feedback, error)
	Delete(ctx context.Context, id string) error
	List(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) ([]*Feedback, int64, error)
	Search(ctx context.Context, criteria FeedbackSearchCriteria) ([]*Feedback, error)
	GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error)
	GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error)
	GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error)
	GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error)
}

// feedbackRepository implements FeedbackRepository interface using MongoDB ORM
type feedbackRepository struct {
	repo   mongo.Repository[Feedback]
	logger logger.Logger
}

// NewFeedbackRepository creates a new feedback repository
func NewFeedbackRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository {
	// Create indexes for feedback collection
	feedbackIndexes := []mongo.Index{
		*mongo.NewIndex("tender_id_idx", bson.D{{Key: "tender_id", Value: 1}}),
		*mongo.NewIndex("customer_id_idx", bson.D{{Key: "customer_id", Value: 1}}),
		*mongo.NewIndex("company_id_idx", bson.D{{Key: "company_id", Value: 1}}),
		*mongo.NewIndex("feedback_type_idx", bson.D{{Key: "feedback_type", Value: 1}}),
		// Compound indexes for common queries
		*mongo.NewIndex("tender_customer_idx", bson.D{
			{Key: "tender_id", Value: 1},
			{Key: "customer_id", Value: 1},
		}),
		*mongo.NewIndex("tender_type_idx", bson.D{
			{Key: "tender_id", Value: 1},
			{Key: "feedback_type", Value: 1},
		}),
	}

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

	// Create ORM repository
	repo := mongo.NewRepository[Feedback](mongoManager.GetCollection("feedback"), logger)

	return &feedbackRepository{
		repo:   repo,
		logger: logger,
	}
}

// Create creates a new feedback
func (r *feedbackRepository) Create(ctx context.Context, feedback *Feedback) error {
	// Set timestamps
	now := time.Now().Unix()
	feedback.CreatedAt = now
	feedback.UpdatedAt = now

	err := r.repo.Create(ctx, feedback)
	if err != nil {
		r.logger.Error("Failed to create feedback", map[string]interface{}{
			"error":     err.Error(),
			"tender_id": feedback.TenderID,
		})
		return err
	}

	r.logger.Info("Feedback created successfully", map[string]interface{}{
		"feedback_id": feedback.GetID(),
		"tender_id":   feedback.TenderID,
		"type":        feedback.FeedbackType,
	})

	return nil
}

// Update updates an existing feedback
func (r *feedbackRepository) Update(ctx context.Context, feedback *Feedback) error {
	feedback.UpdatedAt = time.Now().Unix()

	err := r.repo.Update(ctx, feedback)
	if err != nil {
		r.logger.Error("Failed to update feedback", map[string]interface{}{
			"feedback_id": feedback.GetID(),
			"error":       err.Error(),
		})
		return err
	}

	r.logger.Info("Feedback updated successfully", map[string]interface{}{
		"feedback_id": feedback.GetID(),
		"tender_id":   feedback.TenderID,
	})

	return nil
}

// GetByID retrieves feedback by ID
func (r *feedbackRepository) GetByID(ctx context.Context, id string) (*Feedback, error) {
	feedback, err := r.repo.FindByID(ctx, id)
	if err != nil {
		r.logger.Error("Failed to get feedback by ID", map[string]interface{}{
			"feedback_id": id,
			"error":       err.Error(),
		})
		return nil, err
	}

	return feedback, nil
}

// GetByCompanyID retrieves feedback by company ID and tender ID
func (r *feedbackRepository) GetByCompanyID(ctx context.Context, companyID string, tenderID string) (*Feedback, error) {
	filter := bson.M{"company_id": companyID, "tender_id": tenderID}

	result, err := r.repo.FindOne(ctx, filter)
	if err != nil {
		r.logger.Error("Failed to get feedback by company ID and tender ID", map[string]interface{}{
			"company_id": companyID,
			"tender_id":  tenderID,
			"error":      err.Error(),
		})
		return nil, err
	}

	return result, nil
}

// Delete permanently deletes feedback by ID
func (r *feedbackRepository) Delete(ctx context.Context, id string) error {
	err := r.repo.Delete(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete feedback", map[string]interface{}{
			"feedback_id": id,
			"error":       err.Error(),
		})
		return err
	}

	r.logger.Info("Feedback deleted successfully", map[string]interface{}{
		"feedback_id": id,
	})

	return nil
}

// List retrieves feedback with pagination and filtering
func (r *feedbackRepository) List(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) ([]*Feedback, int64, error) {
	filter := r.buildSearchFilter(criteria)

	pagination := mongo.Pagination{
		Limit:     limit,
		Skip:      offset,
		SortField: "created_at",
		SortOrder: -1,
	}

	result, err := r.repo.FindAll(ctx, filter, pagination)
	if err != nil {
		r.logger.Error("Failed to list feedback", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, 0, err
	}

	// Convert []TenderFeedback to []*TenderFeedback
	feedback := make([]*Feedback, len(result.Items))
	for i := range result.Items {
		feedback[i] = &result.Items[i]
	}

	return feedback, result.TotalCount, nil
}

// GetByFeedbackType retrieves feedback by type
func (r *feedbackRepository) GetByFeedbackType(ctx context.Context, feedbackType FeedbackType, limit, offset int) ([]*Feedback, int64, error) {
	filter := bson.M{"feedback_type": feedbackType}

	pagination := mongo.Pagination{
		Limit:     limit,
		Skip:      offset,
		SortField: "created_at",
		SortOrder: -1,
	}

	result, err := r.repo.FindAll(ctx, filter, pagination)
	if err != nil {
		r.logger.Error("Failed to get feedback by type", map[string]interface{}{
			"feedback_type": feedbackType,
			"error":         err.Error(),
		})
		return nil, 0, err
	}

	// Convert []TenderFeedback to []*TenderFeedback
	feedback := make([]*Feedback, len(result.Items))
	for i := range result.Items {
		feedback[i] = &result.Items[i]
	}

	return feedback, result.TotalCount, nil
}

// Search searches feedback based on criteria
func (r *feedbackRepository) Search(ctx context.Context, criteria FeedbackSearchCriteria) ([]*Feedback, error) {
	filter := r.buildSearchFilter(criteria)

	pagination := mongo.Pagination{
		Limit:     100,
		SortField: "created_at",
		SortOrder: -1,
	}

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

	// Convert []TenderFeedback to []*TenderFeedback
	feedback := make([]*Feedback, len(result.Items))
	for i := range result.Items {
		feedback[i] = &result.Items[i]
	}

	return feedback, nil
}

// GetFeedbackSummary calculates feedback summary for a tender
func (r *feedbackRepository) GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error) {
	pipeline := driver.Pipeline{
		{
			{Key: "$match", Value: bson.M{
				"tender_id": tenderID,
			}},
		},
		{
			{Key: "$group", Value: bson.M{
				"_id": nil,
				"total_likes": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeLike)}},
							1,
							0,
						},
					},
				},
				"total_dislikes": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeDislike)}},
							1,
							0,
						},
					},
				},
				"total_comments": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$ne": []interface{}{"$comment", nil}},
							1,
							0,
						},
					},
				},
				"total_ratings": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$gt": []interface{}{"$rating", 0}},
							1,
							0,
						},
					},
				},
				"rating_sum": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$gt": []interface{}{"$rating", 0}},
							"$rating",
							0,
						},
					},
				},
				"last_updated": bson.M{"$max": "$updated_at"},
			}},
		},
	}

	results, err := r.repo.Aggregate(ctx, pipeline)
	if err != nil {
		r.logger.Error("Failed to get feedback summary", map[string]interface{}{
			"tender_id": tenderID,
			"error":     err.Error(),
		})
		return nil, err
	}

	summary := &FeedbackSummary{
		TenderID:      tenderID,
		TotalLikes:    0,
		TotalDislikes: 0,
		LastUpdated:   time.Now().Unix(),
	}

	if len(results) > 0 {
		result := results[0]
		if totalLikes, ok := result["total_likes"].(int32); ok {
			summary.TotalLikes = int64(totalLikes)
		}
		if totalDislikes, ok := result["total_dislikes"].(int32); ok {
			summary.TotalDislikes = int64(totalDislikes)
		}
		if lastUpdated, ok := result["last_updated"].(int64); ok {
			summary.LastUpdated = lastUpdated
		}
	}

	return summary, nil
}

// GetFeedbackCountByType gets feedback count grouped by type
func (r *feedbackRepository) GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error) {
	pipeline := driver.Pipeline{
		{
			{Key: "$group", Value: bson.M{
				"_id":   "$feedback_type",
				"count": bson.M{"$sum": 1},
			}},
		},
	}

	results, err := r.repo.Aggregate(ctx, pipeline)
	if err != nil {
		return nil, err
	}

	counts := make(map[FeedbackType]int64)
	for _, result := range results {
		if feedbackType, ok := result["_id"].(string); ok {
			if count, ok := result["count"].(int32); ok {
				counts[FeedbackType(feedbackType)] = int64(count)
			}
		}
	}

	return counts, nil
}

// GetCustomerFeedbackHistory gets the feedback history for a specific customer
func (r *feedbackRepository) GetCustomerFeedbackHistory(ctx context.Context, customerID string, limit, offset int) ([]*Feedback, int64, error) {
	filter := bson.M{
		"customer_id": customerID,
	}

	pagination := mongo.Pagination{
		Limit:     limit,
		Skip:      offset,
		SortField: "created_at",
		SortOrder: -1,
	}

	result, err := r.repo.FindAll(ctx, filter, pagination)
	if err != nil {
		r.logger.Error("Failed to get customer feedback history", map[string]interface{}{
			"customer_id": customerID,
			"error":       err.Error(),
		})
		return nil, 0, err
	}

	// Convert []TenderFeedback to []*TenderFeedback
	feedback := make([]*Feedback, len(result.Items))
	for i := range result.Items {
		feedback[i] = &result.Items[i]
	}

	return feedback, result.TotalCount, nil
}

func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error) {
	filter := bson.M{"tender_id": tenderID, "company_id": companyID}

	result, err := r.repo.FindOne(ctx, filter)
	if err != nil {
		r.logger.Error("Failed to get feedback by tender ID", map[string]interface{}{
			"tender_id": tenderID,
			"error":     err.Error(),
		})
		return nil, err
	}

	return result, nil
}

// GetCompanyFeedbackStats calculates feedback statistics for a company
func (r *feedbackRepository) GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) {
	pipeline := driver.Pipeline{
		{
			{Key: "$match", Value: bson.M{
				"company_id": companyID,
			}},
		},
		{
			{Key: "$group", Value: bson.M{
				"_id": nil,
				"total_feedback": bson.M{
					"$sum": 1,
				},
				"total_likes": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeLike)}},
							1,
							0,
						},
					},
				},
				"total_dislikes": bson.M{
					"$sum": bson.M{
						"$cond": []interface{}{
							bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeDislike)}},
							1,
							0,
						},
					},
				},
				"last_updated": bson.M{"$max": "$updated_at"},
			}},
		},
	}

	results, err := r.repo.Aggregate(ctx, pipeline)
	if err != nil {
		r.logger.Error("Failed to get company feedback stats", map[string]interface{}{
			"company_id": companyID,
			"error":      err.Error(),
		})
		return nil, err
	}

	stats := &CompanyFeedbackStatsResponse{
		CompanyID:     companyID,
		TotalFeedback: 0,
		TotalLikes:    0,
		TotalDislikes: 0,
		LastUpdated:   time.Now().Unix(),
	}

	if len(results) > 0 {
		result := results[0]
		if totalFeedback, ok := result["total_feedback"].(int32); ok {
			stats.TotalFeedback = int64(totalFeedback)
		}
		if totalLikes, ok := result["total_likes"].(int32); ok {
			stats.TotalLikes = int64(totalLikes)
		}
		if totalDislikes, ok := result["total_dislikes"].(int32); ok {
			stats.TotalDislikes = int64(totalDislikes)
		}
		if lastUpdated, ok := result["last_updated"].(int64); ok {
			stats.LastUpdated = lastUpdated
		}
	}

	return stats, nil
}

// buildSearchFilter builds MongoDB filter from search criteria
func (r *feedbackRepository) buildSearchFilter(criteria FeedbackSearchCriteria) bson.M {
	filter := bson.M{}

	// Always exclude deleted feedback unless specifically requested
	if criteria.TenderID != nil {
		filter["tender_id"] = *criteria.TenderID
	}

	if criteria.CustomerID != nil {
		filter["customer_id"] = *criteria.CustomerID
	}

	if criteria.CompanyID != nil {
		filter["company_id"] = *criteria.CompanyID
	}

	if criteria.FeedbackType != nil {
		filter["feedback_type"] = *criteria.FeedbackType
	}

	if criteria.DateFrom != nil || criteria.DateTo != nil {
		dateFilter := bson.M{}
		if criteria.DateFrom != nil {
			dateFilter["$gte"] = *criteria.DateFrom
		}
		if criteria.DateTo != nil {
			dateFilter["$lte"] = *criteria.DateTo
		}
		filter["created_at"] = dateFilter
	}

	return filter
}
