package customer

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"strings"
	"time"

	"tm/internal/company"
	"tm/pkg/audit"
	"tm/pkg/logger"
	"tm/pkg/notification"
	"tm/pkg/redis"
	"tm/pkg/response"

	"tm/pkg/authorization"

	"go.mongodb.org/mongo-driver/v2/bson"
	"golang.org/x/crypto/bcrypt"
)

// Service defines business logic for customer operations
type Service interface {
	// Core customer operations
	Register(ctx context.Context, form *CreateCustomerForm) (*CustomerResponse, error)
	GetByID(ctx context.Context, id string) (*CustomerResponse, error)
	Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*CustomerListResponse, error)
	SearchNotification(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) ([]*CustomerNotificationResponse, error)
	GetCustomersByIDs(ctx context.Context, ids []string) ([]*CustomerNotificationResponse, error)
	GetCustomersByRole(ctx context.Context, role string) ([]*CustomerNotificationResponse, error)
	GetCustomersByCompanies(ctx context.Context, companies []string) ([]*CustomerNotificationResponse, error)
	Update(ctx context.Context, id string, form *UpdateCustomerForm) (*CustomerResponse, error)
	Delete(ctx context.Context, id string) error
	UpdateStatus(ctx context.Context, id string, form *UpdateStatusForm) error
	AdminResetPassword(ctx context.Context, actorID, targetID string) (*AdminResetPasswordResponse, error)

	// Authentication operations
	Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
	RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
	GetProfileWithCompanies(ctx context.Context, id string) (*CustomerResponse, error)
	UpdateProfileKeywords(ctx context.Context, customerID string, keywords []string) (*CustomerResponse, error)
	Logout(ctx context.Context, id string, accessToken string) error

	// Forgot password operations
	RequestResetPassword(ctx context.Context, form *RequestResetPasswordForm) (*RequestResetPasswordResponse, error)
	VerifyOTP(ctx context.Context, form *VerifyOTPForm) (*VerifyOTPResponse, error)
	ResetPassword(ctx context.Context, form *ResetPasswordForm) (*ResetPasswordResponse, error)

	// Role assignment operations
	AssignRole(ctx context.Context, customerID string, form *AssignRoleForm) (*AssignRoleResponse, error)
	AssignCompanies(ctx context.Context, customerID string, Companies []string) error
}

// customerService implements the Service interface
type customerService struct {
	repository     Repository
	logger         logger.Logger
	authService    authorization.AuthorizationService
	companyService company.Service
	redisClient    redis.Client
	notification   notification.SDK
	validator      ValidationService
	auditLogger    audit.Logger
}

// New creates a new customer service
func NewService(repository Repository, logger logger.Logger, authService authorization.AuthorizationService, companyService company.Service, redisClient redis.Client, notificationSDK notification.SDK, validator ValidationService, auditLogger audit.Logger) Service {
	return &customerService{
		repository:     repository,
		logger:         logger,
		authService:    authService,
		companyService: companyService,
		redisClient:    redisClient,
		notification:   notificationSDK,
		validator:      validator,
		auditLogger:    auditLogger,
	}
}

// Register creates a new customer
func (s *customerService) Register(ctx context.Context, form *CreateCustomerForm) (*CustomerResponse, error) {
	// Check if email already exists
	existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
	if existingCustomer != nil {
		return nil, errors.New("customer with this email already exists")
	}

	// Check if username already exists
	existingCustomer, _ = s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
	if existingCustomer != nil {
		return nil, errors.New("customer with this username already exists")
	}

	// Check validator
	if !s.validator.IsValidUsername(form.Username) {
		return nil, errors.New("invalid username format")
	}

	var Companies []string
	if len(form.Companies) > 0 {
		validated, err := s.validateCompanyIDs(ctx, form.Companies)
		if err != nil {
			return nil, err
		}
		Companies = validated
	}

	// Hash password
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
	if err != nil {
		s.logger.Error("Failed to hash password", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("failed to process password")
	}

	// Create customer
	customer := &Customer{
		Type:      CustomerType(form.Type),
		Role:      CustomerRole(form.Role),
		Status:    CustomerStatusActive,
		Companies: Companies,
		FullName:  form.FullName,
		Username:  strings.ToLower(form.Username),
		Email:     form.Email,
		Password:  string(hashedPassword),
		Phone:     form.Phone,
	}

	// Keyword auto-generation is handled by the AI service when integrated; backend leaves keywords empty on register.
	customer.KeywordsStatus = KeywordsStatusInProgress
	keywords, err := s.generateKeywords(ctx, customer)
	if err != nil {
		s.logger.Warn("Failed to generate keywords, continuing without keywords", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
		keywords = []string{}
		customer.KeywordsStatus = KeywordsStatusFailed
	} else {
		customer.Keywords = keywords
		customer.KeywordsStatus = KeywordsStatusReady
	}

	// Save to database
	err = s.repository.Register(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to create customer", map[string]interface{}{
			"error":    err.Error(),
			"email":    form.Email,
			"username": form.Username,
		})
		return nil, err
	}
	companies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies for customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
	}

	s.logger.Info("Customer created successfully", map[string]interface{}{
		"customer_id": customer.ID,
		"email":       customer.Email,
		"type":        customer.Type,
		"company_ids": Companies,
	})

	go s.notification.SendEmail(context.Background(), notification.Model{
		Recipient: customer.Email,
		Title:     "Welcome to Opplens",
		Message:   fmt.Sprintf("Welcome to Opplens\nUsername: %s\nPassword: %s", strings.ToLower(customer.Username), form.Password),
		Type:      "info",
		Priority:  "important",
		UserID:    customer.ID.Hex(),
	})

	return customer.ToResponse(companies), nil
}

// GetByID retrieves a customer by ID
func (s *customerService) GetByID(ctx context.Context, id string) (*CustomerResponse, error) {
	customer, err := s.repository.GetByID(ctx, id)
	if err != nil {
		if err.Error() == "customer not found" {
			return nil, errors.New("customer not found")
		}
		s.logger.Error("Failed to get customer by ID", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return nil, err
	}

	var companies []*CompanySummary
	if len(customer.Companies) > 0 {
		companies, err = s.loadCompaniesForCustomer(ctx, customer)
		if err != nil {
			s.logger.Error("Failed to load companies for customer", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customer.ID,
			})
		}
	}

	s.logger.Info("Customer retrieved successfully", map[string]interface{}{
		"customer_id": customer.ID,
		"email":       customer.Email,
	})

	return customer.ToResponse(companies), nil
}

// Search searches for customers
func (s *customerService) Search(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) (*CustomerListResponse, error) {
	page, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to search customers", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, err
	}

	var customerResponses []*CustomerResponse
	for _, customer := range page.Items {
		companies, err := s.loadCompaniesForCustomer(ctx, &customer)
		if err != nil {
			s.logger.Error("Failed to load companies for customer", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customer.ID,
			})
		}

		customerResponses = append(customerResponses, customer.ToResponse(companies))
	}

	return &CustomerListResponse{
		Customers: customerResponses,
		Meta:      pagination.ListMeta(page.TotalCount, page.NextCursor, page.HasMore, page.PageOffset),
	}, nil
}

func (s *customerService) SearchNotification(ctx context.Context, form *SearchCustomersForm, pagination *response.Pagination) ([]*CustomerNotificationResponse, error) {
	page, err := s.repository.Search(ctx, form, pagination)
	if err != nil {
		s.logger.Error("Failed to search customers", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("failed to search customers")
	}

	var customerResponses []*CustomerNotificationResponse
	for _, customer := range page.Items {
		customerResponses = append(customerResponses, customer.ToNotificationResponse())
	}

	return customerResponses, nil
}

// GetCustomersByIDs retrieves customers by IDs
func (s *customerService) GetCustomersByIDs(ctx context.Context, ids []string) ([]*CustomerNotificationResponse, error) {
	customers, err := s.repository.GetByIDs(ctx, ids)
	if err != nil {
		return nil, err
	}

	var customerResponses []*CustomerNotificationResponse
	for _, customer := range customers {
		customerResponses = append(customerResponses, customer.ToNotificationResponse())
	}

	return customerResponses, nil
}

// GetCustomersByRole retrieves customers by role
func (s *customerService) GetCustomersByRole(ctx context.Context, role string) ([]*CustomerNotificationResponse, error) {
	customers, err := s.repository.GetByRole(ctx, CustomerRole(role))
	if err != nil {
		s.logger.Error("Failed to get customers by role", map[string]interface{}{
			"error": err.Error(),
			"role":  role,
		})
		return nil, err
	}

	var customerResponses []*CustomerNotificationResponse
	for _, customer := range customers {
		customerResponses = append(customerResponses, customer.ToNotificationResponse())
	}

	return customerResponses, nil
}

// GetCustomersByCompanies retrieves customers by companies
func (s *customerService) GetCustomersByCompanies(ctx context.Context, companies []string) ([]*CustomerNotificationResponse, error) {
	customers, err := s.repository.GetByCompanies(ctx, companies)
	if err != nil {
		s.logger.Error("Failed to get customers by companies", map[string]interface{}{
			"error":     err.Error(),
			"companies": companies,
		})
		return nil, err
	}

	var customerResponses []*CustomerNotificationResponse
	for _, customer := range customers {
		customerResponses = append(customerResponses, customer.ToNotificationResponse())
	}

	return customerResponses, nil
}

// Update updates a customer
func (s *customerService) Update(ctx context.Context, id string, form *UpdateCustomerForm) (*CustomerResponse, error) {
	// Get existing customer
	customer, err := s.repository.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to get customer for update", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return nil, errors.New("customer not found")
	}

	// Check if email already exists (if changing email)
	if form.Email != "" && form.Email != customer.Email {
		existingCustomer, _ := s.repository.GetByEmail(ctx, form.Email)
		if existingCustomer != nil {
			return nil, errors.New("customer with this email already exists")
		}
		customer.Email = form.Email
	}

	// Check validator
	if !s.validator.IsValidUsername(form.Username) {
		return nil, errors.New("invalid username format")
	}

	changeUsername := false
	// Check if username already exists (if changing username)
	if form.Username != "" && strings.ToLower(form.Username) != customer.Username {
		existingCustomer, _ := s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
		if existingCustomer != nil {
			return nil, errors.New("customer with this username already exists")
		}
		customer.Username = strings.ToLower(form.Username)
		changeUsername = true
	}

	companiesChanged := false
	if form.Companies != nil {
		companyIDs, validateErr := s.validateCompanyIDs(ctx, *form.Companies)
		if validateErr != nil {
			return nil, validateErr
		}
		companiesChanged = !companyIDsEqual(customer.Companies, companyIDs)
		customer.Companies = companyIDs
	}

	infoChanged := false
	if form.Type != "" && customer.Type != CustomerType(form.Type) {
		customer.Type = CustomerType(form.Type)
		infoChanged = true
	}
	if form.FullName != nil && (customer.FullName == nil || *form.FullName != *customer.FullName) {
		customer.FullName = form.FullName
		infoChanged = true
	}

	if form.Phone != nil && (customer.Phone == nil || *form.Phone != *customer.Phone) {
		customer.Phone = form.Phone
		infoChanged = true
	}

	// Update role if provided
	if form.Role != "" && customer.Role != CustomerRole(form.Role) {
		customer.Role = CustomerRole(form.Role)
		infoChanged = true
	}

	// Handle keywords
	if form.Keywords != nil {
		// Keywords explicitly provided by customer, set status to in_progress
		// Customer's manual keywords are saved, but we'll enhance them with AI
		customer.Keywords = form.Keywords
		customer.KeywordsStatus = KeywordsStatusInProgress

		// Save customer with in_progress status first, then process asynchronously
		s.processKeywordsAsync(customer.ID.Hex())
	} else if infoChanged || companiesChanged {
		// Customer info changed, regenerate keywords
		customer.KeywordsStatus = KeywordsStatusInProgress
		keywords, err := s.generateKeywords(ctx, customer)
		if err != nil {
			s.logger.Warn("Failed to regenerate keywords, keeping existing keywords", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customer.ID,
			})
			customer.KeywordsStatus = KeywordsStatusFailed
		} else {
			customer.Keywords = keywords
			customer.KeywordsStatus = KeywordsStatusReady
		}
	}

	// Update in database
	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return nil, errors.New("failed to update customer")
	}

	if changeUsername {
		go s.notification.SendEmail(context.Background(), notification.Model{
			Recipient: customer.Email,
			Title:     "Username Updated",
			Message:   fmt.Sprintf("Your username has been updated to %s", customer.Username),
			Type:      "info",
			Priority:  "important",
			UserID:    customer.ID.Hex(),
		})
	}
	s.logger.Info("Customer updated successfully", map[string]interface{}{
		"customer_id": customer.ID,
		"email":       customer.Email,
	})

	responseCompanies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies for customer response", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
		responseCompanies = []*CompanySummary{}
	}

	return customer.ToResponse(responseCompanies), nil
}

// Delete deletes a customer (soft delete)
func (s *customerService) Delete(ctx context.Context, id string) error {
	// Get customer to check if exists
	_, err := s.repository.GetByID(ctx, id)
	if err != nil {
		s.logger.Error("Failed to get customer for delete", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return errors.New("customer not found")
	}

	// Delete customer (soft delete)
	err = s.repository.Delete(ctx, id)
	if err != nil {
		s.logger.Error("Failed to delete customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return errors.New("failed to delete customer")
	}

	s.logger.Info("Customer deleted successfully", map[string]interface{}{
		"customer_id": id,
	})

	return nil
}

// UpdateStatus updates customer status
func (s *customerService) UpdateStatus(ctx context.Context, id string, form *UpdateStatusForm) error {
	// Get customer to check if exists
	customer, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return errors.New("customer not found")
	}

	// Update status
	status := CustomerStatus(form.Status)
	err = s.repository.UpdateStatus(ctx, id, status)
	if err != nil {
		s.logger.Error("Failed to update customer status", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
			"status":      form.Status,
		})
		return errors.New("failed to update customer status")
	}

	s.logger.Info("Customer status updated successfully", map[string]interface{}{
		"customer_id": id,
		"status":      form.Status,
	})

	go s.notification.SendEmail(context.Background(), notification.Model{
		Recipient: customer.Email,
		Title:     "Account Status Updated",
		Message:   fmt.Sprintf("Your account has been %s", strings.ToUpper(form.Status)),
		Type:      "warning",
		Priority:  "important",
		UserID:    customer.ID.Hex(),
	})

	return nil
}

// AssignCompanies assigns companies to a customer
func (s *customerService) AssignCompanies(ctx context.Context, customerID string, Companies []string) error {
	// Get customer to check if exists
	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		return errors.New("customer not found")
	}

	// Verify companies exist and update customer's company IDs
	var validCompanies []string
	for _, companyID := range Companies {
		_, err = s.companyService.GetByID(ctx, companyID)
		if err != nil {
			s.logger.Error("Company not found during assignment", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customerID,
				"company_id":  companyID,
			})
			continue // Skip invalid company IDs
		}
		validCompanies = append(validCompanies, companyID)
	}

	// Update customer's company IDs
	customer.Companies = make([]string, 0)
	customer.Companies = append(customer.Companies, validCompanies...)
	customer.UpdatedAt = time.Now().Unix()

	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer with company assignments", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return errors.New("failed to assign companies to customer")
	}

	s.logger.Info("Companies assigned to customer successfully", map[string]interface{}{
		"customer_id": customerID,
		"company_ids": validCompanies,
	})

	return nil
}

// RemoveCompanies removes companies from a customer
func (s *customerService) RemoveCompanies(ctx context.Context, customerID string, Companies []string) error {
	// Get customer to check if exists
	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		return errors.New("customer not found")
	}

	// Remove company IDs from customer's list
	var updatedCompanies []string
	for _, existingID := range customer.Companies {
		shouldRemove := false
		for _, removeID := range Companies {
			if existingID == removeID {
				shouldRemove = true
				break
			}
		}
		if !shouldRemove {
			updatedCompanies = append(updatedCompanies, existingID)
		}
	}

	// Update customer's company IDs
	customer.Companies = updatedCompanies
	customer.UpdatedAt = time.Now().Unix()

	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer after removing companies", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return errors.New("failed to remove companies from customer")
	}

	s.logger.Info("Companies removed from customer successfully", map[string]interface{}{
		"customer_id": customerID,
		"company_ids": Companies,
	})

	return nil
}

// Suspend suspends a customer
func (s *customerService) Suspend(ctx context.Context, id string, reason string) error {
	// Get customer to check if exists
	_, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return errors.New("customer not found")
	}

	// Update status to suspended
	err = s.repository.UpdateStatus(ctx, id, CustomerStatusSuspended)
	if err != nil {
		s.logger.Error("Failed to suspend customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return errors.New("failed to suspend customer")
	}

	s.logger.Info("Customer suspended successfully", map[string]interface{}{
		"customer_id": id,
		"reason":      reason,
	})

	return nil
}

// Activate activates a customer
func (s *customerService) Activate(ctx context.Context, id string) error {
	// Get customer to check if exists
	_, err := s.repository.GetByID(ctx, id)
	if err != nil {
		return errors.New("customer not found")
	}

	// Update status to active
	err = s.repository.UpdateStatus(ctx, id, CustomerStatusActive)
	if err != nil {
		s.logger.Error("Failed to activate customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": id,
		})
		return errors.New("failed to activate customer")
	}

	s.logger.Info("Customer activated successfully", map[string]interface{}{
		"customer_id": id,
	})

	return nil
}

// Login handles customer authentication
func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResponse, error) {
	s.logger.Info("Customer login attempt", map[string]interface{}{
		"username": strings.ToLower(form.Username),
	})

	// Get customer by username
	customer, err := s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
	if err != nil {
		s.logger.Warn("Customer login failed - username not found", map[string]interface{}{
			"username": strings.ToLower(form.Username),
		})
		return nil, errors.New("invalid credentials")
	}

	// Check if customer is active
	if customer.Status != CustomerStatusActive {
		s.logger.Warn("Customer login failed - account not active", map[string]interface{}{
			"username":    strings.ToLower(form.Username),
			"customer_id": customer.ID,
			"status":      string(customer.Status),
		})
		return nil, errors.New("account is not active")
	}

	// Verify password
	err = bcrypt.CompareHashAndPassword([]byte(customer.Password), []byte(form.Password))
	if err != nil {
		s.logger.Warn("Customer login failed - wrong password", map[string]interface{}{
			"customer_id": customer.ID,
			"email":       customer.Email,
		})
		return nil, errors.New("invalid credentials")
	}

	// Generate JWT tokens using authorization service
	companyID := ""
	if len(customer.Companies) > 0 {
		companyID = customer.Companies[0] // Use first company ID for JWT token
	}

	tokenPair, err := s.authService.GenerateTokenPair(
		customer.ID.Hex(),
		customer.Email,
		string(customer.Role), // Use customer's actual role
		companyID,
	)
	if err != nil {
		s.logger.Error("Failed to generate JWT tokens", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
		return nil, errors.New("failed to generate authentication tokens")
	}

	err = s.repository.Login(ctx, customer.ID.Hex(), form.DeviceToken)
	if err != nil {
		s.logger.Error("Failed to update customer last login", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
	}

	companies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies for customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
	}

	s.logger.Info("Customer login successful", map[string]interface{}{
		"username":      form.Username,
		"customer_id":   customer.ID,
		"customer_type": string(customer.Type),
	})

	return &AuthResponse{
		Customer:     customer.ToResponse(companies),
		AccessToken:  tokenPair.AccessToken,
		RefreshToken: tokenPair.RefreshToken,
		ExpiresAt:    time.Now().Add(15 * time.Minute).Unix(), // 15 minutes from now
	}, nil
}

// RefreshToken handles token refresh for customers
func (s *customerService) RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error) {
	s.logger.Info("Customer token refresh attempt", map[string]interface{}{
		"refresh_token": refreshToken[:10] + "...", // Log only first 10 chars for security
	})

	// Validate refresh token and generate new access token
	newAccessToken, err := s.authService.RefreshAccessToken(refreshToken)
	if err != nil {
		s.logger.Warn("Failed to refresh access token", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("invalid or expired refresh token")
	}

	// Parse the new access token to get customer information
	validationResult, err := s.authService.ValidateAccessToken(newAccessToken)
	if err != nil {
		s.logger.Error("Failed to validate new access token", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("failed to generate valid access token")
	}

	if !validationResult.Valid {
		s.logger.Error("New access token validation failed", map[string]interface{}{
			"error": validationResult.Error,
		})
		return nil, errors.New("failed to generate valid access token")
	}

	// Get customer information
	customerID, err := bson.ObjectIDFromHex(validationResult.Claims.UserID)
	if err != nil {
		s.logger.Error("Failed to parse customer ID from token", map[string]interface{}{
			"error": err.Error(),
		})
		return nil, errors.New("invalid token format")
	}

	customer, err := s.repository.GetByID(ctx, customerID.Hex())
	if err != nil {
		s.logger.Error("Failed to get customer for token refresh", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID.Hex(),
		})
		return nil, errors.New("customer not found")
	}

	// Check if customer is still active
	if customer.Status != CustomerStatusActive {
		return nil, errors.New("customer account is inactive")
	}

	companies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies for customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
	}

	s.logger.Info("Access token refreshed successfully", map[string]interface{}{
		"customer_id":   customer.ID,
		"customer_type": string(customer.Type),
	})

	return &AuthResponse{
		Customer:     customer.ToResponse(companies),
		AccessToken:  newAccessToken,
		RefreshToken: refreshToken,                            // Return the same refresh token
		ExpiresAt:    time.Now().Add(15 * time.Minute).Unix(), // 15 minutes from now
	}, nil
}

// GetProfileWithCompanies retrieves customer profile information including companies
func (s *customerService) GetProfileWithCompanies(ctx context.Context, customerID string) (*CustomerResponse, error) {
	s.logger.Info("Getting customer profile with companies", map[string]interface{}{
		"customer_id": customerID,
	})

	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		s.logger.Error("Failed to get customer profile with companies", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return nil, errors.New("customer not found")
	}

	// Load companies for the customer
	companies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies for customer profile", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
		})
		// Continue without companies if loading fails
		companies = []*CompanySummary{}
	}

	customerResponse := customer.ToResponse(companies)

	s.logger.Info("Customer profile retrieved with companies successfully", map[string]interface{}{
		"customer_id":   customerID,
		"customer_type": string(customer.Type),
	})

	return customerResponse, nil
}

// UpdateProfileKeywords updates customer profile keywords and triggers AI enhancement asynchronously.
func (s *customerService) UpdateProfileKeywords(ctx context.Context, customerID string, keywords []string) (*CustomerResponse, error) {
	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		s.logger.Error("Failed to get customer for profile keyword update", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return nil, errors.New("customer not found")
	}

	customer.Keywords = keywords
	customer.KeywordsStatus = KeywordsStatusInProgress

	if err := s.repository.Update(ctx, customer); err != nil {
		s.logger.Error("Failed to save profile keywords", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return nil, errors.New("failed to update profile keywords")
	}

	s.processKeywordsAsync(customerID)

	companies, err := s.loadCompaniesForCustomer(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to load companies after profile keyword update", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		companies = []*CompanySummary{}
	}

	return customer.ToResponse(companies), nil
}

// Logout handles customer logout
func (s *customerService) Logout(ctx context.Context, customerID string, accessToken string) error {
	s.logger.Info("Customer logout", map[string]interface{}{
		"customer_id": customerID,
	})

	// get customer by id
	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		s.logger.Error("Failed to get customer for logout", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
	}

	// Invalidate the access token
	err = s.authService.InvalidateToken(accessToken)
	if err != nil {
		s.logger.Error("Failed to invalidate access token", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		// Don't return error here as the customer should still be logged out
		return errors.New("failed to invalidate access token")
	}

	// delete device token from customer
	customer.DeviceToken = make([]string, 0)
	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer device token", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
	}

	s.logger.Info("Customer logout successful", map[string]interface{}{
		"customer_id": customerID,
	})

	return nil
}

// RequestResetPassword handles password reset request
func (s *customerService) RequestResetPassword(ctx context.Context, form *RequestResetPasswordForm) (*RequestResetPasswordResponse, error) {
	s.logger.Info("Password reset request", map[string]interface{}{
		"email": form.Email,
	})

	// Check if customer exists
	customer, err := s.repository.GetByEmail(ctx, form.Email)
	if err != nil {
		s.logger.Warn("Password reset request for non-existent email", map[string]interface{}{
			"email": form.Email,
		})
		// Return success even if email doesn't exist for security
		return &RequestResetPasswordResponse{
			Message: "If the email exists, a password reset code has been sent",
			Success: true,
		}, nil
	}

	// Check if customer is active
	if customer.Status != CustomerStatusActive {
		s.logger.Warn("Password reset request for inactive customer", map[string]interface{}{
			"email":       form.Email,
			"customer_id": customer.ID,
			"status":      string(customer.Status),
		})
		return &RequestResetPasswordResponse{
			Message: "If the email exists, a password reset code has been sent",
			Success: true,
		}, nil
	}

	// Generate 6-digit OTP
	otpCode, err := s.generateOTP()
	if err != nil {
		s.logger.Error("Failed to generate OTP", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to generate verification code")
	}

	// Store OTP in Redis with 10 minutes expiry
	otpKey := fmt.Sprintf("otp:%s", form.Email)
	otpData := OTP{
		Code:      otpCode,
		Email:     form.Email,
		ExpiresAt: time.Now().Add(10 * time.Minute).Unix(),
		CreatedAt: time.Now().Unix(),
	}

	otpDataJSON, err := json.Marshal(otpData)
	if err != nil {
		s.logger.Error("Failed to marshal OTP data", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to process reset request")
	}
	err = s.redisClient.Set(ctx, otpKey, string(otpDataJSON), 10*time.Minute)
	if err != nil {
		s.logger.Error("Failed to store OTP in Redis", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to process reset request")
	}

	// Send OTP via email
	go s.notification.SendEmail(context.Background(), notification.Model{
		Recipient: form.Email,
		Title:     "Password Reset",
		Message:   fmt.Sprintf("Your password reset code is: %s", otpCode),
		Type:      "info",
		Priority:  "important",
		UserID:    customer.ID.Hex(),
	})

	s.logger.Info("Password reset OTP sent successfully", map[string]interface{}{
		"email":       form.Email,
		"customer_id": customer.ID,
	})

	return &RequestResetPasswordResponse{
		Message: "Password reset code sent to your email",
		Success: true,
	}, nil
}

// VerifyOTP handles OTP verification
func (s *customerService) VerifyOTP(ctx context.Context, form *VerifyOTPForm) (*VerifyOTPResponse, error) {
	s.logger.Info("OTP verification attempt", map[string]interface{}{
		"email": form.Email,
	})

	// Get OTP from Redis
	otpKey := fmt.Sprintf("otp:%s", form.Email)
	otpDataStr, err := s.redisClient.Get(ctx, otpKey)
	if err != nil {
		s.logger.Warn("OTP verification failed - OTP not found or expired", map[string]interface{}{
			"email": form.Email,
		})
		return nil, errors.New("invalid or expired verification code")
	}

	var otpData OTP
	err = json.Unmarshal([]byte(otpDataStr), &otpData)
	if err != nil {
		s.logger.Error("Failed to unmarshal OTP data", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("invalid verification code format")
	}

	if otpData.Code != form.Code {
		s.logger.Warn("OTP verification failed - wrong code", map[string]interface{}{
			"email": form.Email,
		})
		return nil, errors.New("invalid verification code")
	}

	// Generate reset token
	resetToken, err := s.generateResetToken()
	if err != nil {
		s.logger.Error("Failed to generate reset token", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to generate reset token")
	}

	// Store reset token in Redis with 30 minutes expiry
	tokenKey := fmt.Sprintf("reset_token:%s", resetToken)
	tokenData := ResetToken{
		Token:     resetToken,
		Email:     form.Email,
		ExpiresAt: time.Now().Add(30 * time.Minute).Unix(),
		CreatedAt: time.Now().Unix(),
	}

	tokenDataJSON, err := json.Marshal(tokenData)
	if err != nil {
		s.logger.Error("Failed to marshal reset token data", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to process verification")
	}
	err = s.redisClient.Set(ctx, tokenKey, string(tokenDataJSON), 30*time.Minute)
	if err != nil {
		s.logger.Error("Failed to store reset token in Redis", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
		return nil, errors.New("failed to process verification")
	}

	// Delete OTP from Redis after successful verification
	err = s.redisClient.Del(ctx, otpKey)
	if err != nil {
		s.logger.Warn("Failed to delete OTP after verification", map[string]interface{}{
			"error": err.Error(),
			"email": form.Email,
		})
	}

	s.logger.Info("OTP verified successfully", map[string]interface{}{
		"email": form.Email,
	})

	return &VerifyOTPResponse{
		Message: "OTP verified successfully",
		Token:   resetToken,
		Success: true,
	}, nil
}

// ResetPassword handles password reset
func (s *customerService) ResetPassword(ctx context.Context, form *ResetPasswordForm) (*ResetPasswordResponse, error) {
	s.logger.Info("Password reset attempt", map[string]interface{}{
		"token": form.Token,
	})

	// Validate password strength
	if !s.isValidPassword(form.NewPassword) {
		return nil, errors.New("password must be at least 5 characters with uppercase, lowercase, and special character")
	}

	// Get reset token from Redis
	tokenKey := fmt.Sprintf("reset_token:%s", form.Token)
	tokenDataStr, err := s.redisClient.Get(ctx, tokenKey)
	if err != nil {
		s.logger.Warn("Password reset failed - invalid or expired token", map[string]interface{}{
			"token": form.Token,
		})
		return nil, errors.New("invalid or expired reset token")
	}

	var tokenData ResetToken
	err = json.Unmarshal([]byte(tokenDataStr), &tokenData)
	if err != nil {
		s.logger.Error("Failed to unmarshal reset token data", map[string]interface{}{
			"error": err.Error(),
			"token": form.Token,
		})
		return nil, errors.New("invalid reset token format")
	}

	if tokenData.Token != form.Token {
		s.logger.Warn("Password reset failed - wrong token", map[string]interface{}{
			"token": form.Token,
		})
		return nil, errors.New("invalid reset token")
	}

	// Get customer
	customer, err := s.repository.GetByEmail(ctx, tokenData.Email)
	if err != nil {
		s.logger.Error("Password reset failed - customer not found", map[string]interface{}{
			"error": err.Error(),
			"email": tokenData.Email,
		})
		return nil, errors.New("customer not found")
	}

	// Hash new password
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(form.NewPassword), bcrypt.DefaultCost)
	if err != nil {
		s.logger.Error("Failed to hash new password", map[string]interface{}{
			"error": err.Error(),
			"email": tokenData.Email,
		})
		return nil, errors.New("failed to process new password")
	}

	// Update customer password
	customer.Password = string(hashedPassword)
	customer.UpdatedAt = time.Now().Unix()

	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer password", map[string]interface{}{
			"error":       err.Error(),
			"email":       tokenData.Email,
			"customer_id": customer.ID,
		})
		return nil, errors.New("failed to reset password")
	}

	// Delete reset token from Redis
	err = s.redisClient.Del(ctx, tokenKey)
	if err != nil {
		s.logger.Warn("Failed to delete reset token after password reset", map[string]interface{}{
			"error": err.Error(),
			"email": tokenData.Email,
		})
	}

	s.logger.Info("Password reset successfully", map[string]interface{}{
		"email":       tokenData.Email,
		"customer_id": customer.ID,
	})

	go s.notification.SendEmail(context.Background(), notification.Model{
		Recipient: tokenData.Email,
		Title:     "Password Reset Success",
		Message:   "Your password has been reset successfully",
		Type:      "info",
		Priority:  "important",
		UserID:    customer.ID.Hex(),
	})

	return &ResetPasswordResponse{
		Message: "Password reset successfully",
		Success: true,
	}, nil
}

// generateOTP generates a 6-digit OTP
func (s *customerService) generateOTP() (string, error) {
	// Generate 6 random digits
	otp := ""
	for i := 0; i < 6; i++ {
		num, err := rand.Int(rand.Reader, big.NewInt(10))
		if err != nil {
			return "", err
		}
		otp += num.String()
	}
	return otp, nil
}

// generateResetToken generates a random reset token
func (s *customerService) generateResetToken() (string, error) {
	// Generate 32 random bytes
	tokenBytes := make([]byte, 32)
	_, err := rand.Read(tokenBytes)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", tokenBytes), nil
}

// isValidPassword validates password strength
func (s *customerService) isValidPassword(password string) bool {
	if len(password) < 5 {
		return false
	}

	hasUpper := false
	hasLower := false
	hasSpecial := false

	for _, char := range password {
		if char >= 'A' && char <= 'Z' {
			hasUpper = true
		} else if char >= 'a' && char <= 'z' {
			hasLower = true
		} else if char == '!' || char == '@' || char == '#' || char == '$' || char == '%' || char == '^' || char == '&' || char == '*' {
			hasSpecial = true
		}
	}

	return hasUpper && hasLower && hasSpecial
}

func (s *customerService) validateCompanyIDs(ctx context.Context, companyIDs []string) ([]string, error) {
	if len(companyIDs) == 0 {
		return []string{}, nil
	}

	validated := make([]string, 0, len(companyIDs))
	seen := make(map[string]struct{}, len(companyIDs))
	for _, companyID := range companyIDs {
		if companyID == "" {
			continue
		}
		if _, exists := seen[companyID]; exists {
			continue
		}
		if _, err := bson.ObjectIDFromHex(companyID); err != nil {
			return nil, errors.New("invalid company ID format: " + companyID)
		}
		if _, err := s.companyService.GetByID(ctx, companyID); err != nil {
			s.logger.Error("Invalid company ID provided", map[string]interface{}{
				"error":      err.Error(),
				"company_id": companyID,
			})
			return nil, errors.New("invalid company ID: " + companyID)
		}
		seen[companyID] = struct{}{}
		validated = append(validated, companyID)
	}

	return validated, nil
}

func companyIDsEqual(existing, updated []string) bool {
	if len(existing) != len(updated) {
		return false
	}

	counts := make(map[string]int, len(existing))
	for _, id := range existing {
		counts[id]++
	}
	for _, id := range updated {
		counts[id]--
		if counts[id] < 0 {
			return false
		}
	}
	for _, count := range counts {
		if count != 0 {
			return false
		}
	}

	return true
}

// loadCompaniesForCustomer loads company summaries for a customer
func (s *customerService) loadCompaniesForCustomer(ctx context.Context, customer *Customer) ([]*CompanySummary, error) {
	if len(customer.Companies) == 0 {
		return []*CompanySummary{}, nil
	}

	// Load companies in batch using the new GetCompaniesByIDs method
	companies, err := s.companyService.GetByIDs(ctx, customer.Companies)
	if err != nil {
		s.logger.Error("Failed to load companies for customer", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customer.ID,
			"company_ids": customer.Companies,
		})
		return []*CompanySummary{}, err
	}

	// Convert to CompanySummary format
	var companySummaries []*CompanySummary
	for _, company := range companies {
		companySummaries = append(companySummaries, &CompanySummary{
			ID:   company.ID,
			Name: company.Name,
		})
	}

	s.logger.Info("Companies loaded for customer", map[string]interface{}{
		"customer_id":     customer.ID,
		"requested_count": len(customer.Companies),
		"loaded_count":    len(companySummaries),
	})

	return companySummaries, nil
}

// AssignRole assigns a role to a customer
func (s *customerService) AssignRole(ctx context.Context, customerID string, form *AssignRoleForm) (*AssignRoleResponse, error) {
	s.logger.Info("Assigning role to customer", map[string]interface{}{
		"customer_id": customerID,
		"role":        form.Role,
	})

	// Get customer to check if exists
	customer, err := s.repository.GetByID(ctx, customerID)
	if err != nil {
		s.logger.Error("Failed to get customer for role assignment", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
		})
		return nil, errors.New("customer not found")
	}

	// Validate role
	role := CustomerRole(form.Role)
	if role != CustomerRoleAdmin && role != CustomerRoleAnalyst {
		s.logger.Error("Invalid role provided", map[string]interface{}{
			"customer_id": customerID,
			"role":        form.Role,
		})
		return nil, errors.New("invalid role. Must be 'admin' or 'analyst'")
	}

	// Update customer role
	customer.Role = role
	customer.UpdatedAt = time.Now().Unix()

	err = s.repository.Update(ctx, customer)
	if err != nil {
		s.logger.Error("Failed to update customer role", map[string]interface{}{
			"error":       err.Error(),
			"customer_id": customerID,
			"role":        form.Role,
		})
		return nil, errors.New("failed to assign role to customer")
	}

	s.logger.Info("Role assigned to customer successfully", map[string]interface{}{
		"customer_id": customerID,
		"role":        form.Role,
	})

	return &AssignRoleResponse{
		Message: "Role assigned successfully",
		Success: true,
	}, nil
}

// generateKeywords is reserved for future integration with the AI service; the backend does not generate keywords.
func (s *customerService) generateKeywords(ctx context.Context, customer *Customer) ([]string, error) {
	_ = ctx
	_ = customer
	return []string{}, nil
}

func (s *customerService) processKeywordsAsync(customerID string) {
	go func() {
		ctx := context.Background()
		cust, err := s.repository.GetByID(ctx, customerID)
		if err != nil {
			s.logger.Error("Failed to get customer for keyword processing", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customerID,
			})
			return
		}

		keywords, err := s.generateKeywords(ctx, cust)
		if err != nil {
			s.logger.Warn("Failed to process keywords after manual update", map[string]interface{}{
				"error":       err.Error(),
				"customer_id": customerID,
			})
			cust.KeywordsStatus = KeywordsStatusFailed
		} else if len(keywords) > 0 {
			cust.Keywords = keywords
			cust.KeywordsStatus = KeywordsStatusReady
		} else {
			s.logger.Debug("No auto-generated keywords from backend, keeping existing keywords", map[string]interface{}{
				"customer_id": customerID,
			})
			cust.KeywordsStatus = KeywordsStatusReady
		}

		if updateErr := s.repository.Update(ctx, cust); updateErr != nil {
			s.logger.Error("Failed to update customer keywords status", map[string]interface{}{
				"error":       updateErr.Error(),
				"customer_id": customerID,
			})
		} else {
			s.logger.Info("Keywords processed successfully", map[string]interface{}{
				"customer_id": customerID,
				"status":      cust.KeywordsStatus,
			})
		}
	}()
}
