package com.example.demo.service.Impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.example.demo.dto.ProfileDto;
import com.example.demo.dto.RecipeDto;
import com.example.demo.dto.UpdateProfileDto;
import com.example.demo.dto.UserDto;
import com.example.demo.entity.Recipe;
import com.example.demo.entity.User;
import com.example.demo.exception.NotFoundException;
import com.example.demo.repository.RecipeRepo;
import com.example.demo.repository.UserRepo;
import com.example.demo.service.RecipeService;
import com.example.demo.service.UserService;

import jakarta.transaction.Transactional;

@Service
public class UserServiceImpl implements UserService {

	private UserRepo userRepository;
	private RecipeRepo recipeRepository;
	private PasswordEncoder passwordEncoder;
	private RecipeService recipeService;

	public UserServiceImpl(UserRepo userRepository, RecipeRepo recipeRepository,
			PasswordEncoder passwordEncoder, RecipeService recipeService) {
		super();
		this.userRepository = userRepository;
		this.recipeRepository = recipeRepository;
		this.passwordEncoder = passwordEncoder;
		this.recipeService = recipeService;
	}

	@Override
	public UserDto convertToDto(User user) {
		return new UserDto(
				user.getId(),
				user.getUsername(),
				user.getEmail(),
				user.getDisplayName(),
				user.getBio()
		);
	}

	@Override
	public User saveUser(User user) {
		if (user.getRole() == null || user.getRole().isBlank()) {
			user.setRole("ROLE_USER");
		}
		user.setBanned(false);
		user.setHashedpassword(passwordEncoder.encode(user.getPassword()));
		return userRepository.save(user);
	}

	@Override
	public List<UserDto> getAllUsers() {
		List<UserDto> list = new ArrayList<>();
		for (User user : userRepository.findAll()) {
			UserDto userDto = convertToDto(user);
			list.add(userDto);
		}
		return list;
	}

	@Override
	public UserDto getUserById(Integer id) {
		return convertToDto(userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id)));
	}

	@Override
	@Transactional
	public UserDto saveFavorite(Integer userId, Integer recipeId) {
		User existingUser = userRepository.findById(userId)
				.orElseThrow(() -> new NotFoundException("User", "id", userId));

		Recipe existingRecipe = recipeRepository.findById(recipeId)
				.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));

		existingUser.getFavRecipes().add(existingRecipe);
		userRepository.save(existingUser);

		return convertToDto(existingUser);
	}

	@Override
	public UserDto updateUser(User user, Integer id) {
		User existingUser = userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));

		existingUser.setUsername(user.getUsername());
		existingUser.setEmail(user.getEmail());

		userRepository.save(existingUser);

		return convertToDto(existingUser);
	}

	@Override
	public void deleteUser(Integer id) {
		userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));
		userRepository.deleteById(id);
	}

	@Override
	@Transactional
	public void deleteFavorite(Integer userId, Integer recipeId) {
		User existingUser = userRepository.findById(userId)
				.orElseThrow(() -> new NotFoundException("User", "id", userId));

		Recipe existingRecipe = recipeRepository.findById(recipeId)
				.orElseThrow(() -> new NotFoundException("Recipe", "id", recipeId));

		existingUser.getFavRecipes().remove(existingRecipe);
		userRepository.save(existingUser);
	}

	@Override
	public List<UserDto> getUsersByName(String name) {
		List<User> users = userRepository.findByUsernameContainingIgnoreCase(name);

		if (users.isEmpty()) {
			throw new NotFoundException("User", "username containing", name);
		}

		List<UserDto> userList = new ArrayList<>();

		for (User user : users) {
			UserDto dto = convertToDto(user);
			userList.add(dto);
		}
		return userList;
	}

	@Override
	public UserDto banUser(Integer id) {
		User user = userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));
		user.setBanned(true);
		userRepository.save(user);
		return convertToDto(user);
	}

	@Override
	public UserDto unbanUser(Integer id) {
		User user = userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));
		user.setBanned(false);
		userRepository.save(user);
		return convertToDto(user);
	}

	@Override
	public UserDto makeAdmin(Integer id) {
		User user = userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));
		user.setRole("ROLE_ADMIN");
		userRepository.save(user);
		return convertToDto(user);
	}

	@Override
	public UserDto makeUser(Integer id) {
		User user = userRepository.findById(id)
				.orElseThrow(() -> new NotFoundException("User", "id", id));
		user.setRole("ROLE_USER");
		userRepository.save(user);
		return convertToDto(user);
	}

	@Override
	@Transactional
	public ProfileDto getProfileByUserId(Integer userId) {
		User user = userRepository.findById(userId)
				.orElseThrow(() -> new NotFoundException("User", "id", userId));

		List<Recipe> recipes = recipeRepository.findByUserId(userId);
		List<RecipeDto> recipeDtos = new ArrayList<>();

		for (Recipe recipe : recipes) {
			recipeDtos.add(recipeService.convertToDto(recipe));
		}

		ProfileDto profile = new ProfileDto();
		profile.setId(user.getId());
		profile.setUsername(user.getUsername());
		profile.setDisplayName(user.getDisplayName());
		profile.setBio(user.getBio());
		profile.setRecipes(recipeDtos);

		return profile;
	}

	@Override
	@Transactional
	public ProfileDto getCurrentUserProfile(String username) {
		User user = userRepository.findByUsername(username)
				.orElseThrow(() -> new NotFoundException("User", "username", username));

		return getProfileByUserId(user.getId());
	}

	@Override
	@Transactional
	public ProfileDto updateProfile(String username, UpdateProfileDto dto) {
		User user = userRepository.findByUsername(username)
				.orElseThrow(() -> new NotFoundException("User", "username", username));

		user.setDisplayName(dto.getDisplayName());
		user.setBio(dto.getBio());

		userRepository.save(user);

		return getProfileByUserId(user.getId());
	}

	@Override
	@Transactional
	public List<RecipeDto> getFavoriteRecipesByUsername(String username) {
		User user = userRepository.findByUsername(username)
				.orElseThrow(() -> new NotFoundException("User", "username", username));

		List<RecipeDto> favoriteDtos = new ArrayList<>();

		for (Recipe recipe : user.getFavRecipes()) {
			favoriteDtos.add(recipeService.convertToDto(recipe));
		}

		return favoriteDtos;
	}
}