package inquiry

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 inquiry data access
type Repository interface {
	Create(ctx context.Context, inquiry *Inquiry) error
	GetByID(ctx context.Context, id string) (*Inquiry, error)
	UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error
	Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) (*orm.PaginatedResult[Inquiry], error)
	Delete(ctx context.Context, id string) error
	CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error)
	CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error)
}

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

// NewInquiryRepository creates a new inquiry repository
func NewRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
	// Create indexes using the ORM's index management
	indexes := []orm.Index{
		*orm.CreateTextIndex("search_idx", "full_name", "company_name", "work_email"),
		*orm.NewIndex("company_name_idx", bson.D{bson.E{Key: "company_name", Value: 1}}),
		*orm.NewIndex("work_email_idx", bson.D{bson.E{Key: "work_email", Value: 1}}),
		*orm.NewIndex("phone_number_idx", bson.D{bson.E{Key: "phone_number", Value: 1}}),
		*orm.NewIndex("status_idx", bson.D{bson.E{Key: "status", Value: 1}}),
		*orm.NewIndex("created_at_idx", bson.D{bson.E{Key: "created_at", Value: -1}}),
		// Compound index for duplicate prevention
		*orm.NewIndex("email_status_idx", bson.D{
			bson.E{Key: "work_email", Value: 1},
			bson.E{Key: "status", Value: 1},
		}),
		*orm.NewIndex("phone_status_idx", bson.D{
			bson.E{Key: "phone_number", Value: 1},
			bson.E{Key: "status", Value: 1},
		}),
	}

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

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

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

// Create creates a new inquiry
func (r *inquiryRepository) Create(ctx context.Context, inquiry *Inquiry) error {
	// Set created timestamp using Unix timestamp
	now := time.Now().Unix()
	inquiry.SetCreatedAt(now)
	inquiry.SetUpdatedAt(now)

	// Set initial status to pending
	inquiry.Status = StatusPending

	// Add initial status change to history
	inquiry.AddStatusChange(StatusPending, "Inquiry submitted", "system", "Initial inquiry submission")

	// Use ORM to create inquiry
	err := r.ormRepo.Create(ctx, inquiry)
	if err != nil {
		r.logger.Error("Failed to create inquiry", map[string]interface{}{
			"error":        err.Error(),
			"work_email":   inquiry.WorkEmail,
			"company_name": inquiry.CompanyName,
		})
		return err
	}

	r.logger.Info("Inquiry created successfully", map[string]interface{}{
		"inquiry_id":   inquiry.ID,
		"work_email":   inquiry.WorkEmail,
		"company_name": inquiry.CompanyName,
		"status":       inquiry.Status,
	})

	return nil
}

// GetByID retrieves an inquiry by ID
func (r *inquiryRepository) GetByID(ctx context.Context, id string) (*Inquiry, error) {
	inquiry, err := r.ormRepo.FindByID(ctx, id)
	if err != nil {
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return nil, errors.New("inquiry not found")
		}
		r.logger.Error("Failed to get inquiry by ID", map[string]interface{}{
			"error":      err.Error(),
			"inquiry_id": id,
		})
		return nil, err
	}

	return inquiry, nil
}

// UpdateStatus updates the status of an inquiry
func (r *inquiryRepository) UpdateStatus(ctx context.Context, id string, status, reason, changedBy, description string) error {
	// Get inquiry first
	inquiry, err := r.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Add status change to history
	inquiry.AddStatusChange(status, reason, changedBy, description)

	// Update in database - use the correct Update method signature
	err = r.ormRepo.Update(ctx, inquiry)
	if err != nil {
		r.logger.Error("Failed to update inquiry status", map[string]interface{}{
			"error":      err.Error(),
			"inquiry_id": id,
			"new_status": status,
		})
		return err
	}

	r.logger.Info("Inquiry status updated successfully", map[string]interface{}{
		"inquiry_id": id,
		"old_status": inquiry.StatusHistory[len(inquiry.StatusHistory)-2].Status, // Previous status
		"new_status": status,
		"changed_by": changedBy,
	})

	return nil
}

// CheckPendingInquiry checks if there's a pending inquiry for the given email
func (r *inquiryRepository) CheckPendingInquiry(ctx context.Context, email string) (*Inquiry, error) {
	filter := bson.M{
		"work_email": email,
		"status":     bson.M{"$in": []string{StatusPending, StatusReviewed}},
	}

	pagination := orm.NewPaginationBuilder().
		Limit(1).
		SortBy("created_at", "desc").
		Build()

	result, err := r.ormRepo.FindAll(ctx, filter, pagination)
	if err != nil {
		r.logger.Error("Failed to check pending inquiry by email", map[string]interface{}{
			"error":      err.Error(),
			"work_email": email,
		})
		return nil, err
	}

	if len(result.Items) > 0 {
		return &result.Items[0], nil
	}

	return nil, nil
}

// CheckPendingInquiryByPhone checks if there's a pending inquiry for the given phone number
func (r *inquiryRepository) CheckPendingInquiryByPhone(ctx context.Context, phone string) (*Inquiry, error) {
	filter := bson.M{
		"phone_number": phone,
		"status":       bson.M{"$in": []string{StatusPending, StatusReviewed}},
	}

	pagination := orm.NewPaginationBuilder().
		Limit(1).
		SortBy("created_at", "desc").
		Build()

	result, err := r.ormRepo.FindAll(ctx, filter, pagination)
	if err != nil {
		r.logger.Error("Failed to check pending inquiry by phone", map[string]interface{}{
			"error":        err.Error(),
			"phone_number": phone,
		})
		return nil, err
	}

	if len(result.Items) > 0 {
		return &result.Items[0], nil
	}

	return nil, nil
}

// Search retrieves inquiries with pagination and filters
func (r *inquiryRepository) Search(ctx context.Context, form *SearchInquiriesForm, pagination *response.Pagination) (*orm.PaginatedResult[Inquiry], error) {
	filter := bson.M{}

	// Add search filter
	if form.Search != nil {
		filter["$or"] = bson.A{
			bson.M{"full_name": bson.M{"$regex": form.Search, "$options": "i"}},
			bson.M{"company_name": bson.M{"$regex": form.Search, "$options": "i"}},
			bson.M{"work_email": bson.M{"$regex": form.Search, "$options": "i"}},
		}
	}

	// Add status filter
	if form.Status != nil {
		filter["status"] = *form.Status
	}

	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 inquiries", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, err
	}

	return result, nil
}

// Delete deletes an inquiry
func (r *inquiryRepository) Delete(ctx context.Context, id string) error {
	// Get inquiry first to ensure it exists
	_, err := r.GetByID(ctx, id)
	if err != nil {
		r.logger.Error("Failed to delete inquiry", map[string]interface{}{
			"error":      err.Error(),
			"inquiry_id": id,
		})
		if errors.Is(err, orm.ErrDocumentNotFound) {
			return errors.New("inquiry not found")
		}
		return err
	}

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

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

	return nil
}
