package cz.muni.fi.pa165.core.user;

import cz.muni.fi.pa165.core.common.DomainFacade;
import cz.muni.fi.pa165.core.helpers.exceptions.EntityConflictException;
import cz.muni.fi.pa165.core.helpers.exceptions.EntityDeletionException;
import cz.muni.fi.pa165.core.house.House;
import cz.muni.fi.pa165.core.house.HouseFacade;
import cz.muni.fi.pa165.core.house.HouseMapper;
import cz.muni.fi.pa165.core.user.roles.HouseRole;
import cz.muni.fi.pa165.core.user.roles.RoleService;
import cz.muni.fi.pa165.model.dto.house.HouseDto;
import cz.muni.fi.pa165.model.dto.role.enums.HouseRoleEnum;
import cz.muni.fi.pa165.model.dto.role.enums.RoleTypeEnum;
import cz.muni.fi.pa165.model.dto.user.ChangePasswordDto;
import cz.muni.fi.pa165.model.dto.user.LoginInfoDto;
import cz.muni.fi.pa165.model.dto.user.UserCreateDto;
import cz.muni.fi.pa165.model.dto.user.UserDto;
import cz.muni.fi.pa165.model.dto.user.UserUpdateDto;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cz.muni.fi.pa165.core.user.utils.PasswordUtil.isPasswordValid;


@Component
public class UserFacade extends DomainFacade<User, UserDto, UserCreateDto, UserUpdateDto> {

	private final UserService userService; // For the "UserService" specific methods
	private final UserMapper userMapper;   // For the "UserMapper" specific methods

	private final HouseMapper houseMapper;
	private final RoleService roleService;
	private final HouseFacade houseFacade;

	@Autowired
	private EntityManager entityManager;

	@Autowired
	public UserFacade(UserService userService, UserMapper userMapper, HouseMapper houseMapper, RoleService roleService, HouseFacade houseFacade) {
		super(userService, userMapper);
		this.userService = userService;
		this.userMapper = userMapper;
		this.houseMapper = houseMapper;
		this.roleService = roleService;
		this.houseFacade = houseFacade;
	}

	/**
	 * Returns the user with the given username.
	 *
	 * @param username the username of the user to retrieve.
	 * @return the user with the given username.
	 */
	public UserDto findByUsername(String username) {
		return mapper.toDto(userService.findByUsername(username));
	}

	@Override
	@Transactional
	public UserDto create(UserCreateDto createDto) {
		if (findByUsername(createDto.getUsername()) != null) {
			throw new EntityConflictException("User with given username already exists");
		}
		UserDto dto = super.create(createDto);
		User user = userService.findById(dto.getId());
		// create default user role
		HouseRole role  = new HouseRole();
		role.setRoleType(RoleTypeEnum.HouseRole);
		role.setUser(user);
		role.setHouseRole(HouseRoleEnum.Owner);
		roleService.create(role);

		return dto;
	}

	@Override
	public UserDto updateById(UserUpdateDto updateDto, String id) {
		if (findByUsername(updateDto.getUsername()) != null) {
			throw new EntityConflictException("User with given username already exists");
		}
		return super.updateById(updateDto, id);
	}

	// TODO: make logout method without ResponseEntity
	public ResponseEntity<?> logout() {
		// validate JWT token...
		return ResponseEntity.ok("Logged out successfully.");
	}

	// TODO: make login method without ResponseEntity
	public ResponseEntity<?> login(LoginInfoDto request) {
		String username = request.getUsername();
		String password = request.getPassword();
		if (username.isBlank()) {
			// TODO: potential validation
			return ResponseEntity.status(401).body("Username is empty.");
		}
		if (!isPasswordValid(password)) {
			return ResponseEntity.status(401).body("Password is not valid.");
		}
		UserDto userDto = findByUsername(username);

		if (Objects.isNull(userDto) || !userService.loginCheck(userDto.getId(), password)) {
			return ResponseEntity.status(401).body("Invalid username or password.");
		}

		return ResponseEntity.ok("Login successful.");
	}

	// TODO: make changePassword method without ResponseEntity
	public ResponseEntity<?> changePassword(ChangePasswordDto request, String userId){
		String oldPassword = request.getOldPassword();
		String newPassword = request.getNewPassword();
		String confirmPassword = request.getNewPasswordConfirmation();

		// Check if new password is valid

		if (!isPasswordValid(newPassword)) {
			return ResponseEntity.badRequest().body("New password is invalid.");
		}

		// Check if new password matches confirmation password
		if (!newPassword.equals(confirmPassword)) {
			return ResponseEntity.badRequest().body("New password and confirmation password do not match.");
		}

		// TODO: Check if old password matches user's current password

		// TODO: Update user's password in the database
		return ResponseEntity.ok("Password changed successfully.");
	}

	@Transactional
	public UserDto deleteHouseUserById(String id) throws EntityDeletionException {
		User user = userService.findById(id);
		user.getRolesList().stream().forEach(role -> {
			if (role.getRoleType() == RoleTypeEnum.HouseRole){
				try {
					House house  = ((HouseRole) role).getHouse();
					roleService.deleteById(role.getId());
					houseFacade.deleteHouseWithAllItsEntities(house);
				} catch (EntityDeletionException e) {
					throw new RuntimeException(e);
				}
			}
		});

		return mapper.toDto(userService.deleteById(user.getId()));
	}

	public List<UserDto> findAllUsersByRole(RoleTypeEnum roleTypeEnum){
		List<User> users = userService.findAll().stream()
				.filter(user -> user.getRolesList().stream()
						.anyMatch(role -> role.getRoleType() == roleTypeEnum))
				.collect(Collectors.toList());
		return userMapper.toDtoList(users);
	}

	public List<HouseDto> findHousesByUserId(String userId){
		User user = userService.findById(userId);
		List<House> houses = user.getRolesList().stream()
				.filter(role -> role instanceof HouseRole)
				.map(role -> ((HouseRole) role).getHouse())
				.toList();
		return houseMapper.toDtoList(houses);
	}



}
