package ru.soune.nocopy.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.register.ChangePasswordRequest;
import ru.soune.nocopy.dto.register.RegAnswer;
import ru.soune.nocopy.dto.register.RegRequest;
import ru.soune.nocopy.dto.tarriff.TariffInfoDTO;
import ru.soune.nocopy.dto.user.UserDTO;
import ru.soune.nocopy.dto.user.UserRequest;
import ru.soune.nocopy.entity.tarif.Tariff;
import ru.soune.nocopy.entity.tarif.TariffInfo;
import ru.soune.nocopy.entity.user.AuthToken;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.InvalidUserEmail;
import ru.soune.nocopy.exception.NotFoundAuthToken;
import ru.soune.nocopy.exception.NotValidFieldException;
import ru.soune.nocopy.mapper.UserMapper;
import ru.soune.nocopy.repository.AuthTokenRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.file.FileStatsService;
import ru.soune.nocopy.service.register.AuthService;
import ru.soune.nocopy.service.tariff.TariffService;
import ru.soune.nocopy.service.user.UserService;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@RestController
@RequestMapping("v1/api/user")
@RequiredArgsConstructor
public class UserController {

    private final UserRepository userRepository;

    private final UserService userService;

    private final AuthTokenRepository authTokenRepository;

    private final UserMapper userMapper;

    private final PasswordEncoder passwordEncoder;

    private final AuthService authService;

    private final TariffService tariffService;

    private final FileStatsService fileStatsService;

    @GetMapping("/all")
    public ResponseEntity<List<UserDTO>> getAllUsers() {
        List<UserDTO> allUsers = userRepository.findAll().stream()
                .map(u -> new UserDTO(u.getFullName(), u.getCompanyName(), u.getEmail(), u.isActive(),
                        u.getPhone(), u.getGenderType(),
                        u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType(), tariffService.getAllTariffs(),
                        null, null, u.getVerificationStatus()))
                .toList();

        return ResponseEntity.ok(allUsers);
    }

    @GetMapping
    public ResponseEntity<UserDTO> getUser(@RequestParam("email") String email,
                                           @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        if (tokenOptional.isPresent()) {
            User user = userRepository.findByEmail(email);

            if (user != null) {
                UserDTO userDTO = userMapper.toDTO(user);
                userDTO.setEmail(email);
                userDTO.setFullName(user.getFullName());
                userDTO.setVerifiedStatus(user.getVerificationStatus());

                if (user.getCompany() != null) {
                    userDTO.setCompany(user.getCompany().getCompanyName());
                }

                if (user.canManageCompanySettings() || user.canLogin() && user.getCompany() == null) {
                    userDTO.setTariffs(tariffService.getTariffByAccountType(user));

                    Long fileOnDisk = user.canManageCompanySettings() ?
                            fileStatsService.calculateCompanyFileSize(user.getId()):
                            fileStatsService.calculateUserFileSize(user.getId());

                    Integer filesCount =  user.canManageCompanySettings() ?
                            fileStatsService.filesCompanyCount(user.getId()):
                            fileStatsService.filesUserCount(user.getId());

                    TariffInfo personalTariffInfo;

                    if (user.getCompany() == null) {
                        personalTariffInfo = user.getPersonalTariffInfo();
                    } else {
                        personalTariffInfo = user.getCompany().getTariffInfo();
                    }

                    Tariff tariff = personalTariffInfo.getTariff();

                    TariffInfoDTO infoDTO = TariffInfoDTO.builder().tokens(personalTariffInfo.getTokens())
                            .id(personalTariffInfo.getId())
                            .tariffName(tariff.getName())
                            .status(personalTariffInfo.getStatus().name())
                            .tariffId(tariff.getId())
                            .currentFileCounts(filesCount)
                            .maxFileCounts(tariff.getMaxFilesCount())
                            .currentFileOnDisk(fileOnDisk)
                            .maxFileOnDisk(tariff.getDiskSize())
                            .startTariff(personalTariffInfo.getStartTariff())
                            .endTariff(personalTariffInfo.getEndTariff())
                            .tokens(personalTariffInfo.getTokens() + personalTariffInfo.getBoughtTokens())
                            .build();

                    userDTO.setTariffInfo(infoDTO);

                }

                userDTO.setPhone(user.getPhone());
                userDTO.setGenderType(user.getGenderType());
                userDTO.setBirthday(user.getBirthday());
                userDTO.setCreatedAt(user.getCreatedAt());
                userDTO.setSubscriptionType(user.getSubscriptionType());
                userDTO.setActive(user.isActive());
                userDTO.setPermission(user.getUserPermissions());

                return ResponseEntity.ok(userDTO);
            }
        } else {
            return ResponseEntity.notFound().build();
        }

        return ResponseEntity.notFound().build();
    }

    //TODO fix mapper,doesnot exist all fields
    @PostMapping("/change-password")
    public ResponseEntity<UserDTO> updateUser(@RequestBody ChangePasswordRequest changePasswordRequest,
                                              @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        if (!changePasswordRequest.getEmail().equals(user.getEmail())) {
            throw new InvalidUserEmail("Email is not valid: " + changePasswordRequest.getEmail() + "not found");
        }

        User updateUser = userService.changePassword(user, changePasswordRequest);

        return ResponseEntity.ok(userMapper.toDTO(updateUser));
    }

    @PatchMapping("/user-update")
    public ResponseEntity<UserDTO> updateUser(@RequestBody UserRequest userRequest,
                                              @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        return ResponseEntity.ok(userService.updateUser(userRequest, user));
    }

    @PostMapping("/create-user")
    public ResponseEntity<User> addUser(@RequestBody RegRequest registerRequest) {

        if (userRepository.existsByEmail(registerRequest.getEmail()) ||
                userRepository.existsByPhone(registerRequest.getPhone())) {
            RegAnswer regAnswer = new RegAnswer();
            regAnswer.setFieldErrors(Arrays.asList(Map.of("email", registerRequest.getEmail())));
            regAnswer.setFieldErrors(Arrays.asList(Map.of("phone", registerRequest.getPhone())));

            throw new NotValidFieldException("User already exists with email:" + registerRequest.getEmail() + " or phone: " +
                    registerRequest.getPhone(), new BaseResponse(2,
                    MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getCode(),
                    MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer));
        }

        User user = new User();
        user.setFullName(registerRequest.getFullName());
        user.setEmail(registerRequest.getEmail());
        user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
        user.setActive(true);
        user.setEmailVerified(true);

        if (registerRequest.getPhone() != null) {
            user.setPhone(registerRequest.getPhone());
        }

        User savedUser = userRepository.save(user);

        AuthToken authToken = authService.generateAuthToken(savedUser);

        authTokenRepository.save(authToken);

        return ResponseEntity.ok().body(savedUser);
    }

    @DeleteMapping("/delete-user/{userId}")
    public ResponseEntity<?> addUser(@PathVariable Long userId) {
        userRepository.deleteById(userId);
        return ResponseEntity.ok().body(Map.of("userId", userId, "deleted", "true"));
    }

}
