package service

import (
	"context"
	"errors"
	"api_es/internal/dto"
	"api_es/internal/models"
	"api_es/internal/repository"
	"api_es/internal/utils"
	"api_es/pkg/logger"

	"golang.org/x/crypto/bcrypt"
)

var (
	ErrUserNotFound       = errors.New("user not found")
	ErrInvalidCredentials = errors.New("invalid credentials")
	ErrUserAlreadyExists  = errors.New("user already exists")
	ErrInvalidPassword    = errors.New("invalid password")
)

type UserService interface {
	Register(ctx context.Context, req dto.RegisterRequest) (*dto.AuthResponse, error)
	Login(ctx context.Context, req dto.LoginRequest) (*dto.AuthResponse, error)
	GetUser(ctx context.Context, id uint) (*dto.UserResponse, error)
	UpdateUser(ctx context.Context, id uint, req dto.UpdateUserRequest) (*dto.UserResponse, error)
	DeleteUser(ctx context.Context, id uint) error
	ListUsers(ctx context.Context, limit, offset int) ([]*dto.UserResponse, error)
	GetUserProfile(ctx context.Context, id uint) (*dto.UserResponse, error)
}

type userService struct {
	userRepo repository.UserRepository
	jwtUtil  *utils.JWTUtil
}

func NewUserService(userRepo repository.UserRepository, jwtUtil *utils.JWTUtil) UserService {
	return &userService{
		userRepo: userRepo,
		jwtUtil:  jwtUtil,
	}
}

func (s *userService) Register(ctx context.Context, req dto.RegisterRequest) (*dto.AuthResponse, error) {
	zapLogger := logger.Get()
	zapLogger.Debug("Start register")
	// Проверяем существование пользователя
	existingUser, _ := s.userRepo.GetByEmail(ctx, req.Email)
	if existingUser != nil {
		return nil, ErrUserAlreadyExists
	}

	// Хешируем пароль
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		return nil, err
	}

	// Создаем пользователя
	user := &models.User{
		Email:        req.Email,
		PasswordHash: string(hashedPassword),
		FullName:     req.FullName,
		FirstName:    req.FirstName,
		LastName:     req.LastName,
		Phone:        req.Phone,
		City:         req.City,
		IsActive:     true,
		IsVerified:   false,
		Role:         "user",
	}

	if err := s.userRepo.Create(ctx, user); err != nil {
		return nil, err
	}

	// Генерируем токен
	token, err := s.jwtUtil.GenerateToken(user.ID, user.Email, user.Role)
	if err != nil {
		return nil, err
	}

	userResponse := dto.ToUserResponse(user)
	zapLogger.Debug("End register")
	return &dto.AuthResponse{
		Token: token,
		User:  userResponse,
	}, nil
}

func (s *userService) Login(ctx context.Context, req dto.LoginRequest) (*dto.AuthResponse, error) {
	zapLogger := logger.Get()
	zapLogger.Debug("Start login")
	// Находим пользователя по email
	user, err := s.userRepo.GetByEmail(ctx, req.Email)
	if err != nil {
		return nil, ErrInvalidCredentials
	}

	// Проверяем пароль
	if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
		return nil, ErrInvalidCredentials
	}

	// Проверяем активность пользователя
	if !user.IsActive {
		return nil, errors.New("account is deactivated")
	}

	// Генерируем токен
	token, err := s.jwtUtil.GenerateToken(user.ID, user.Email, user.Role)
	if err != nil {
		return nil, err
	}

	userResponse := dto.ToUserResponse(user)
	zapLogger.Debug("End login")
	return &dto.AuthResponse{
		Token: token,
		User:  userResponse,
	}, nil
}

func (s *userService) GetUser(ctx context.Context, id uint) (*dto.UserResponse, error) {
	user, err := s.userRepo.GetByID(ctx, id)
	if err != nil {
		return nil, ErrUserNotFound
	}

	response := dto.ToUserResponse(user)
	return &response, nil
}

func (s *userService) UpdateUser(ctx context.Context, id uint, req dto.UpdateUserRequest) (*dto.UserResponse, error) {
	user, err := s.userRepo.GetByID(ctx, id)
	if err != nil {
		return nil, ErrUserNotFound
	}

	// Обновляем поля
	if req.FullName != "" {
		user.FullName = req.FullName
	}
	if req.FirstName != "" {
		user.FirstName = req.FirstName
	}
	if req.LastName != "" {
		user.LastName = req.LastName
	}
	if req.Phone != "" {
		user.Phone = req.Phone
	}
	if req.City != "" {
		user.City = req.City
	}
	if req.OrganizationForm != "" {
		user.OrganizationForm = req.OrganizationForm
	}
	if req.OrganizationName != "" {
		user.OrganizationName = req.OrganizationName
	}
	if req.OrganizationShort != "" {
		user.OrganizationShort = req.OrganizationShort
	}
	if req.INN != "" {
		user.INN = req.INN
	}
	if req.PersonalINN != "" {
		user.PersonalINN = req.PersonalINN
	}

	if err := s.userRepo.Update(ctx, user); err != nil {
		return nil, err
	}

	response := dto.ToUserResponse(user)
	return &response, nil
}

func (s *userService) DeleteUser(ctx context.Context, id uint) error {
	return s.userRepo.Delete(ctx, id)
}

func (s *userService) ListUsers(ctx context.Context, limit, offset int) ([]*dto.UserResponse, error) {
	users, err := s.userRepo.List(ctx, limit, offset)
	if err != nil {
		return nil, err
	}

	responses := make([]*dto.UserResponse, len(users))
	for i, user := range users {
		response := dto.ToUserResponse(user)
		responses[i] = &response
	}

	return responses, nil
}

func (s *userService) GetUserProfile(ctx context.Context, id uint) (*dto.UserResponse, error) {
	return s.GetUser(ctx, id)
}
