package services

import (
	"58team_blog/internal/application/commands"
	"58team_blog/internal/application/common"
	"58team_blog/internal/application/mapper"
	"58team_blog/internal/application/queries"
	"58team_blog/internal/domain/entities"
	"58team_blog/internal/domain/repository"
	"errors"
	"fmt"
)

type UserService struct {
	repo repository.UsersRepository
}

func NewUserService(repo repository.UsersRepository) UserService {
	return UserService{
		repo: repo,
	}
}

func (s *UserService) Create(cmd commands.CreateUserCommand) (*common.UserResult, error) {
	// Check user with given name exists
	{
		user, err := s.repo.FindByName(cmd.Username)
		if err != nil {
			return nil, err
		}

		if user != nil {
			return nil, fmt.Errorf("User: %s already exists", user.UserName)
		}
	}

	// Create new user
	user, err := entities.CreateUser(cmd.Username, cmd.Password)
	if err != nil {
		return nil, err
	}

	if err := user.Validate(); err != nil {
		return nil, err
	}

	entity, err := s.repo.Create(&user)
	if err != nil {
		return nil, err
	}

	result := mapper.CreateUserResultFromEntity(entity)

	return result, nil
}

func (s *UserService) FindById(query queries.UserFindByIdQuery) (*queries.UserFindByIdResult, error) {
	entity, err := s.repo.FindById(query.Id)
	if err != nil {
		return nil, err
	}

	if err := entity.Validate(); err != nil {
		return nil, err
	}

	result := mapper.CreateUserFindByIdResultFromEntity(entity)

	return result, nil
}

func (s *UserService) FindByName(query queries.UserFindByNameQuery) (*queries.UserFindByNameResult, error) {
	entity, err := s.repo.FindByName(query.Name)
	if err != nil {
		return nil, err
	}

	if err := entity.Validate(); err != nil {
		return nil, err
	}

	result := mapper.CreateUserFindByNameResultFromEntity(entity)
	return result, nil
}

func (s *UserService) GetAll() (*queries.UserGetAllResult, error) {
	entityList, err := s.repo.GetAll()
	if err != nil {
		return nil, err
	}

	for _, e := range entityList {
		if err := e.Validate(); err != nil {
			return nil, err
		}
	}

	result := mapper.CreateUserGetAllResultFromEntity(entityList)
	return result, nil
}

func (s *UserService) Update(cmd commands.UpdateUserCommand) (*common.UserResult, error) {
	entity, err := s.repo.FindById(cmd.Id)
	if err != nil {
		return nil, err
	}

	if err := entity.Validate(); err != nil {
		return nil, err
	}

	if cmd.Username != entity.UserName {
		return nil, errors.New("You cannot change user name")
	}

	entity.Password = cmd.Password

	if err := entity.Validate(); err != nil {
		return nil, err
	}

	if err := s.repo.Update(entity); err != nil {
		return nil, err
	}

	result := mapper.CreateUserResultFromEntity(entity)
	return result, nil
}

func (s *UserService) Delete(cmd commands.DeleteUserCommand) error {
	entity, err := s.repo.FindById(cmd.Id)
	if err != nil {
		return err
	}

	if err := entity.Validate(); err != nil {
		return err
	}

	if err := s.repo.Delete(entity.Id); err != nil {
		return err
	}

	return nil
}
