package processor

import (
	"apskel-pos-be/internal/mappers"
	"apskel-pos-be/internal/models"
	"apskel-pos-be/internal/repository"
	"context"
	"errors"
	"fmt"

	"github.com/google/uuid"
)

type CustomerProcessor struct {
	customerRepo *repository.CustomerRepository
}

func NewCustomerProcessor(customerRepo *repository.CustomerRepository) *CustomerProcessor {
	return &CustomerProcessor{
		customerRepo: customerRepo,
	}
}

// CreateCustomer creates a new customer
func (p *CustomerProcessor) CreateCustomer(ctx context.Context, req *models.CreateCustomerRequest, organizationID uuid.UUID) (*models.CustomerResponse, error) {
	if req.Email != nil {
		existingCustomer, err := p.customerRepo.GetByEmail(ctx, *req.Email, organizationID)
		if err == nil && existingCustomer != nil {
			return nil, errors.New("email already exists for this organization")
		}
	}

	// Convert request to entity
	customer := mappers.ToCustomerEntity(req, organizationID)

	// Create customer
	err := p.customerRepo.Create(ctx, customer)
	if err != nil {
		return nil, fmt.Errorf("failed to create customer: %w", err)
	}

	return mappers.ToCustomerResponse(customer), nil
}

// GetCustomer retrieves a customer by ID
func (p *CustomerProcessor) GetCustomer(ctx context.Context, customerID, organizationID uuid.UUID) (*models.CustomerResponse, error) {
	customer, err := p.customerRepo.GetByIDAndOrganization(ctx, customerID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("customer not found: %w", err)
	}

	return mappers.ToCustomerResponse(customer), nil
}

// ListCustomers retrieves customers with pagination and filtering
func (p *CustomerProcessor) ListCustomers(ctx context.Context, query *models.ListCustomersQuery, organizationID uuid.UUID) (*models.PaginatedResponse[models.CustomerResponse], error) {
	// Set default values
	if query.Page <= 0 {
		query.Page = 1
	}
	if query.Limit <= 0 {
		query.Limit = 10
	}
	if query.Limit > 100 {
		query.Limit = 100
	}

	offset := (query.Page - 1) * query.Limit

	// Get customers from repository
	customers, total, err := p.customerRepo.List(
		ctx,
		organizationID,
		offset,
		query.Limit,
		query.Search,
		query.IsActive,
		query.IsDefault,
		query.SortBy,
		query.SortOrder,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to list customers: %w", err)
	}

	// Convert to responses
	responses := mappers.ToCustomerResponses(customers)

	// Calculate pagination info
	totalPages := int((total + int64(query.Limit) - 1) / int64(query.Limit))

	return &models.PaginatedResponse[models.CustomerResponse]{
		Data: responses,
		Pagination: models.Pagination{
			Page:       query.Page,
			Limit:      query.Limit,
			Total:      total,
			TotalPages: totalPages,
		},
	}, nil
}

// UpdateCustomer updates an existing customer
func (p *CustomerProcessor) UpdateCustomer(ctx context.Context, customerID, organizationID uuid.UUID, req *models.UpdateCustomerRequest) (*models.CustomerResponse, error) {
	// Get existing customer
	customer, err := p.customerRepo.GetByIDAndOrganization(ctx, customerID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("customer not found: %w", err)
	}

	// Check if email is already taken by another customer within the organization
	if req.Email != nil && *req.Email != *customer.Email {
		existingCustomer, err := p.customerRepo.GetByEmail(ctx, *req.Email, organizationID)
		if err == nil && existingCustomer != nil && existingCustomer.ID != customerID {
			return nil, errors.New("email already exists for this organization")
		}
	}

	// Update customer fields
	mappers.UpdateCustomerEntity(customer, req)

	// Save updated customer
	err = p.customerRepo.Update(ctx, customer)
	if err != nil {
		return nil, fmt.Errorf("failed to update customer: %w", err)
	}

	return mappers.ToCustomerResponse(customer), nil
}

// DeleteCustomer deletes a customer
func (p *CustomerProcessor) DeleteCustomer(ctx context.Context, customerID, organizationID uuid.UUID) error {
	// Get existing customer
	customer, err := p.customerRepo.GetByIDAndOrganization(ctx, customerID, organizationID)
	if err != nil {
		return fmt.Errorf("customer not found: %w", err)
	}

	// Prevent deletion of default customer
	if customer.IsDefault {
		return errors.New("cannot delete default customer")
	}

	// Delete customer
	err = p.customerRepo.Delete(ctx, customerID)
	if err != nil {
		return fmt.Errorf("failed to delete customer: %w", err)
	}

	return nil
}

func (p *CustomerProcessor) SetDefaultCustomer(ctx context.Context, customerID, organizationID uuid.UUID) (*models.CustomerResponse, error) {
	_, err := p.customerRepo.GetByIDAndOrganization(ctx, customerID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("customer not found: %w", err)
	}

	// Set as default
	err = p.customerRepo.SetAsDefault(ctx, customerID, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to set default customer: %w", err)
	}

	// Get updated customer
	updatedCustomer, err := p.customerRepo.GetByID(ctx, customerID)
	if err != nil {
		return nil, fmt.Errorf("failed to get updated customer: %w", err)
	}

	return mappers.ToCustomerResponse(updatedCustomer), nil
}

func (p *CustomerProcessor) GetDefaultCustomer(ctx context.Context, organizationID uuid.UUID) (*models.CustomerResponse, error) {
	customer, err := p.customerRepo.GetDefaultCustomer(ctx, organizationID)
	if err != nil {
		return nil, fmt.Errorf("default customer not found: %w", err)
	}

	return mappers.ToCustomerResponse(customer), nil
}

func (p *CustomerProcessor) EnsureDefaultCustomer(ctx context.Context, organizationID uuid.UUID) (*models.CustomerResponse, error) {
	customer, err := p.customerRepo.GetDefaultCustomer(ctx, organizationID)
	if err == nil {
		return mappers.ToCustomerResponse(customer), nil
	}

	defaultCustomer, err := p.customerRepo.CreateDefaultCustomer(ctx, organizationID)
	if err != nil {
		return nil, fmt.Errorf("failed to create default customer: %w", err)
	}

	return mappers.ToCustomerResponse(defaultCustomer), nil
}
