package services

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"

	"github.com/RyanCopley/skybridge/user/internal/domain"
	"github.com/RyanCopley/skybridge/user/internal/repository/interfaces"
)

// UserService defines the interface for user business logic
type UserService interface {
	// Create creates a new user
	Create(ctx context.Context, req *domain.CreateUserRequest, actorID string) (*domain.User, error)
	
	// GetByID retrieves a user by ID
	GetByID(ctx context.Context, id uuid.UUID) (*domain.User, error)
	
	// GetByEmail retrieves a user by email
	GetByEmail(ctx context.Context, email string) (*domain.User, error)
	
	// Update updates an existing user
	Update(ctx context.Context, id uuid.UUID, req *domain.UpdateUserRequest, actorID string) (*domain.User, error)
	
	// Delete deletes a user by ID
	Delete(ctx context.Context, id uuid.UUID, actorID string) error
	
	// List retrieves users with filtering and pagination
	List(ctx context.Context, req *domain.ListUsersRequest) (*domain.ListUsersResponse, error)
	
	// UpdateLastLogin updates the last login timestamp
	UpdateLastLogin(ctx context.Context, id uuid.UUID) error
	
	// ExistsByEmail checks if a user exists with the given email
	ExistsByEmail(ctx context.Context, email string) (bool, error)
}

type userService struct {
	userRepo        interfaces.UserRepository
	profileRepo     interfaces.UserProfileRepository
	auditRepo       interfaces.AuditRepository
	logger          *zap.Logger
}

// NewUserService creates a new user service
func NewUserService(
	userRepo interfaces.UserRepository,
	profileRepo interfaces.UserProfileRepository,
	auditRepo interfaces.AuditRepository,
	logger *zap.Logger,
) UserService {
	return &userService{
		userRepo:    userRepo,
		profileRepo: profileRepo,
		auditRepo:   auditRepo,
		logger:      logger,
	}
}

func (s *userService) Create(ctx context.Context, req *domain.CreateUserRequest, actorID string) (*domain.User, error) {
	// Validate email uniqueness
	exists, err := s.userRepo.ExistsByEmail(ctx, req.Email)
	if err != nil {
		s.logger.Error("Failed to check email uniqueness", zap.String("email", req.Email), zap.Error(err))
		return nil, fmt.Errorf("failed to validate email uniqueness: %w", err)
	}
	if exists {
		return nil, fmt.Errorf("user with email %s already exists", req.Email)
	}

	// Create user domain object
	user := &domain.User{
		ID:          uuid.New(),
		Email:       req.Email,
		FirstName:   req.FirstName,
		LastName:    req.LastName,
		DisplayName: req.DisplayName,
		Avatar:      req.Avatar,
		Role:        req.Role,
		Status:      req.Status,
		CreatedBy:   actorID,
		UpdatedBy:   actorID,
	}

	if user.Status == "" {
		user.Status = domain.UserStatusPending
	}

	// Create user in database
	err = s.userRepo.Create(ctx, user)
	if err != nil {
		s.logger.Error("Failed to create user", zap.String("email", req.Email), zap.Error(err))
		return nil, fmt.Errorf("failed to create user: %w", err)
	}

	// Create default user profile
	profile := &domain.UserProfile{
		UserID:      user.ID,
		Bio:         "",
		Location:    "",
		Website:     "",
		Timezone:    "UTC",
		Language:    "en",
		Preferences: make(map[string]interface{}),
	}

	err = s.profileRepo.Create(ctx, profile)
	if err != nil {
		s.logger.Warn("Failed to create user profile", zap.String("user_id", user.ID.String()), zap.Error(err))
		// Don't fail user creation if profile creation fails
	}

	// Log audit event
	if s.auditRepo != nil {
		auditEvent := &interfaces.AuditEvent{
			ID:           uuid.New(),
			Type:         "user.created",
			Severity:     "info",
			Status:       "success",
			Timestamp:    time.Now().Format(time.RFC3339),
			ActorID:      actorID,
			ActorType:    "user",
			ResourceID:   user.ID.String(),
			ResourceType: "user",
			Action:       "create",
			Description:  fmt.Sprintf("User %s created", user.Email),
			Details: map[string]interface{}{
				"user_id": user.ID.String(),
				"email":   user.Email,
				"role":    user.Role,
				"status":  user.Status,
			},
		}
		_ = s.auditRepo.LogEvent(ctx, auditEvent)
	}

	s.logger.Info("User created successfully", 
		zap.String("user_id", user.ID.String()),
		zap.String("email", user.Email),
		zap.String("actor", actorID))

	return user, nil
}

func (s *userService) GetByID(ctx context.Context, id uuid.UUID) (*domain.User, error) {
	user, err := s.userRepo.GetByID(ctx, id)
	if err != nil {
		s.logger.Debug("Failed to get user by ID", zap.String("id", id.String()), zap.Error(err))
		return nil, err
	}

	return user, nil
}

func (s *userService) GetByEmail(ctx context.Context, email string) (*domain.User, error) {
	user, err := s.userRepo.GetByEmail(ctx, email)
	if err != nil {
		s.logger.Debug("Failed to get user by email", zap.String("email", email), zap.Error(err))
		return nil, err
	}

	return user, nil
}

func (s *userService) Update(ctx context.Context, id uuid.UUID, req *domain.UpdateUserRequest, actorID string) (*domain.User, error) {
	// Get existing user
	existingUser, err := s.userRepo.GetByID(ctx, id)
	if err != nil {
		return nil, err
	}

	// Check email uniqueness if email is being updated
	if req.Email != nil && *req.Email != existingUser.Email {
		exists, err := s.userRepo.ExistsByEmail(ctx, *req.Email)
		if err != nil {
			s.logger.Error("Failed to check email uniqueness", zap.String("email", *req.Email), zap.Error(err))
			return nil, fmt.Errorf("failed to validate email uniqueness: %w", err)
		}
		if exists {
			return nil, fmt.Errorf("user with email %s already exists", *req.Email)
		}
	}

	// Update fields
	if req.Email != nil {
		existingUser.Email = *req.Email
	}
	if req.FirstName != nil {
		existingUser.FirstName = *req.FirstName
	}
	if req.LastName != nil {
		existingUser.LastName = *req.LastName
	}
	if req.DisplayName != nil {
		existingUser.DisplayName = *req.DisplayName
	}
	if req.Avatar != nil {
		existingUser.Avatar = *req.Avatar
	}
	if req.Role != nil {
		existingUser.Role = *req.Role
	}
	if req.Status != nil {
		existingUser.Status = *req.Status
	}
	existingUser.UpdatedBy = actorID

	// Update user in database
	err = s.userRepo.Update(ctx, existingUser)
	if err != nil {
		s.logger.Error("Failed to update user", zap.String("id", id.String()), zap.Error(err))
		return nil, fmt.Errorf("failed to update user: %w", err)
	}

	// Log audit event
	if s.auditRepo != nil {
		auditEvent := &interfaces.AuditEvent{
			ID:           uuid.New(),
			Type:         "user.updated",
			Severity:     "info",
			Status:       "success",
			Timestamp:    time.Now().Format(time.RFC3339),
			ActorID:      actorID,
			ActorType:    "user",
			ResourceID:   id.String(),
			ResourceType: "user",
			Action:       "update",
			Description:  fmt.Sprintf("User %s updated", existingUser.Email),
			Details: map[string]interface{}{
				"user_id": id.String(),
				"email":   existingUser.Email,
				"role":    existingUser.Role,
				"status":  existingUser.Status,
			},
		}
		_ = s.auditRepo.LogEvent(ctx, auditEvent)
	}

	s.logger.Info("User updated successfully", 
		zap.String("user_id", id.String()),
		zap.String("email", existingUser.Email),
		zap.String("actor", actorID))

	return existingUser, nil
}

func (s *userService) Delete(ctx context.Context, id uuid.UUID, actorID string) error {
	// Get user for audit logging
	user, err := s.userRepo.GetByID(ctx, id)
	if err != nil {
		return err
	}

	// Delete user profile first
	_ = s.profileRepo.Delete(ctx, id) // Don't fail if profile doesn't exist

	// Delete user
	err = s.userRepo.Delete(ctx, id)
	if err != nil {
		s.logger.Error("Failed to delete user", zap.String("id", id.String()), zap.Error(err))
		return fmt.Errorf("failed to delete user: %w", err)
	}

	// Log audit event
	if s.auditRepo != nil {
		auditEvent := &interfaces.AuditEvent{
			ID:           uuid.New(),
			Type:         "user.deleted",
			Severity:     "warn",
			Status:       "success",
			Timestamp:    time.Now().Format(time.RFC3339),
			ActorID:      actorID,
			ActorType:    "user",
			ResourceID:   id.String(),
			ResourceType: "user",
			Action:       "delete",
			Description:  fmt.Sprintf("User %s deleted", user.Email),
			Details: map[string]interface{}{
				"user_id": id.String(),
				"email":   user.Email,
			},
		}
		_ = s.auditRepo.LogEvent(ctx, auditEvent)
	}

	s.logger.Info("User deleted successfully", 
		zap.String("user_id", id.String()),
		zap.String("email", user.Email),
		zap.String("actor", actorID))

	return nil
}

func (s *userService) List(ctx context.Context, req *domain.ListUsersRequest) (*domain.ListUsersResponse, error) {
	response, err := s.userRepo.List(ctx, req)
	if err != nil {
		s.logger.Error("Failed to list users", zap.Error(err))
		return nil, fmt.Errorf("failed to list users: %w", err)
	}

	return response, nil
}

func (s *userService) UpdateLastLogin(ctx context.Context, id uuid.UUID) error {
	err := s.userRepo.UpdateLastLogin(ctx, id)
	if err != nil {
		s.logger.Error("Failed to update last login", zap.String("id", id.String()), zap.Error(err))
		return fmt.Errorf("failed to update last login: %w", err)
	}

	return nil
}

func (s *userService) ExistsByEmail(ctx context.Context, email string) (bool, error) {
	exists, err := s.userRepo.ExistsByEmail(ctx, email)
	if err != nil {
		s.logger.Error("Failed to check email existence", zap.String("email", email), zap.Error(err))
		return false, fmt.Errorf("failed to check email existence: %w", err)
	}

	return exists, nil
}