package contact

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

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

// ContactRepository defines the interface for contact data operations
type ContactRepository interface {
	// Create creates a new contact message
	Create(ctx context.Context, contact *Contact) error

	// GetByID retrieves a contact by ID
	GetByID(ctx context.Context, id string) (*Contact, error)

	// Search searches contacts with filters and pagination
	Search(ctx context.Context, filters SearchContactsForm, pagination *response.Pagination) (*orm.PaginatedResult[Contact], error)

	// UpdateStatus updates the status of a contact
	UpdateStatus(ctx context.Context, id string, status ContactStatus, adminNotes *string, resolvedBy *string) error

	// GetStats returns contact statistics
	GetStats(ctx context.Context) (*ContactStats, error)

	// Delete deletes a contact by ID
	Delete(ctx context.Context, id string) error
}

// ContactStats represents contact statistics
type ContactStats struct {
	Total      int64 `json:"total"`
	Pending    int64 `json:"pending"`
	InProgress int64 `json:"in_progress"`
	Resolved   int64 `json:"resolved"`
	Closed     int64 `json:"closed"`
}

// contactRepository implements ContactRepository interface
type contactRepository struct {
	repo   orm.Repository[Contact]
	logger logger.Logger
}

// NewContactRepository creates a new contact repository
func NewContactRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) ContactRepository {
	// Create indexes for contact collection
	contactIndexes := []orm.Index{
		*orm.NewIndex("email_idx", bson.D{{Key: "email", Value: 1}}),
		*orm.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
		*orm.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
		*orm.CreateTextIndex("contact_text_idx", "full_name", "email", "message"),
	}

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

	collection := mongoManager.GetCollection("contacts")
	return &contactRepository{
		repo:   orm.NewRepository[Contact](collection, logger),
		logger: logger,
	}
}

// Create creates a new contact message
func (r *contactRepository) Create(ctx context.Context, contact *Contact) error {
	// Set default status if not provided
	if contact.Status == "" {
		contact.Status = ContactStatusPending
	}

	return r.repo.Create(ctx, contact)
}

// GetByID retrieves a contact by ID
func (r *contactRepository) GetByID(ctx context.Context, id string) (*Contact, error) {
	return r.repo.FindByID(ctx, id)
}

// Search searches contacts with filters and pagination
func (r *contactRepository) Search(ctx context.Context, filters SearchContactsForm, pagination *response.Pagination) (*orm.PaginatedResult[Contact], error) {
	filter := bson.M{}

	if filters.Search != nil && *filters.Search != "" {
		filter["$text"] = bson.M{"$search": *filters.Search}
	}

	if filters.Status != nil && *filters.Status != "" {
		filter["status"] = *filters.Status
	}

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

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

	return r.repo.FindAll(ctx, filter, mongoPagination)
}

// UpdateStatus updates the status of a contact
func (r *contactRepository) UpdateStatus(ctx context.Context, id string, status ContactStatus, adminNotes *string, resolvedBy *string) error {
	// Get the existing contact
	contact, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Update the contact fields
	contact.Status = status
	contact.UpdatedAt = time.Now().Unix()

	if adminNotes != nil {
		contact.AdminNotes = adminNotes
	}

	// Set resolved fields if status is resolved or closed
	if status == ContactStatusResolved || status == ContactStatusClosed {
		now := time.Now().Unix()
		contact.ResolvedAt = &now
		if resolvedBy != nil {
			contact.ResolvedBy = resolvedBy
		}
	} else {
		// Unset resolved fields if status is changed back to pending or in_progress
		contact.ResolvedAt = nil
		contact.ResolvedBy = nil
	}

	err = r.repo.Update(ctx, contact)
	if err != nil {
		r.logger.Error("Failed to update contact status", map[string]interface{}{
			"id":     id,
			"status": status,
			"error":  err.Error(),
		})
		return fmt.Errorf("failed to update contact status: %w", err)
	}

	r.logger.Info("Contact status updated successfully", map[string]interface{}{
		"id":     id,
		"status": status,
	})

	return nil
}

// GetStats returns contact statistics
func (r *contactRepository) GetStats(ctx context.Context) (*ContactStats, error) {
	pipeline := mongo.Pipeline{
		{
			{Key: "$group", Value: bson.D{
				{Key: "_id", Value: "$status"},
				{Key: "count", Value: bson.D{{Key: "$sum", Value: 1}}},
			}},
		},
	}

	results, err := r.repo.Aggregate(ctx, pipeline)
	if err != nil {
		r.logger.Error("Failed to get contact stats", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, fmt.Errorf("failed to get contact stats: %w", err)
	}

	stats := &ContactStats{}
	for _, result := range results {
		status, ok := result["_id"].(string)
		if !ok {
			continue
		}

		count, ok := result["count"].(int32)
		if !ok {
			if count64, ok := result["count"].(int64); ok {
				count = int32(count64)
			} else {
				continue
			}
		}

		switch ContactStatus(status) {
		case ContactStatusPending:
			stats.Pending = int64(count)
		case ContactStatusInProgress:
			stats.InProgress = int64(count)
		case ContactStatusResolved:
			stats.Resolved = int64(count)
		case ContactStatusClosed:
			stats.Closed = int64(count)
		}

		stats.Total += int64(count)
	}

	return stats, nil
}

// Delete deletes a contact by ID
func (r *contactRepository) Delete(ctx context.Context, id string) error {
	return r.repo.Delete(ctx, id)
}
