package io.gmss.fiscad.controllers.user;

import io.gmss.fiscad.entities.user.Role;
import io.gmss.fiscad.entities.user.User;
import io.gmss.fiscad.enums.UserRole;
import io.gmss.fiscad.exceptions.*;
import io.gmss.fiscad.interfaces.infocad.parametre.StructureService;
import io.gmss.fiscad.interfaces.user.RoleService;
import io.gmss.fiscad.interfaces.user.UserService;
import io.gmss.fiscad.paylaods.ApiResponse;
import io.gmss.fiscad.paylaods.JwtAuthenticationResponse;
import io.gmss.fiscad.paylaods.Login;
import io.gmss.fiscad.paylaods.UserRequest;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;

import java.util.HashSet;
import java.util.Set;

@RestController
@RequestMapping(value = "api/auth", produces = MediaType.APPLICATION_JSON_VALUE)
@SecurityRequirement(name = "bearer")
@Tag(name = "Authentification")
@CrossOrigin(origins = "*")
public class AuthController {

    private final UserService userService;
    private final RoleService roleService;
    private final StructureService structureService;
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    public AuthController(UserService userService, RoleService roleService, StructureService structureService) {
        this.userService = userService;
        this.roleService = roleService;
        this.structureService = structureService;
    }

    @PostMapping(value = "/login")
    public ResponseEntity<?> login(@RequestBody @Validated @Valid Login login) {
        try {
            JwtAuthenticationResponse jwtAuthenticationResponse = userService.loginUser(login);

            if (!jwtAuthenticationResponse.getToken().isEmpty()) {
                User user = userService.getUserByUsername(login.getUsername());
                if (user.isResetPassword()) {
                    return new ResponseEntity<>(
                            new ApiResponse<>(false, jwtAuthenticationResponse, "Vous devez impérativement changer son mot de passe avant de pouvoir continuer toute action dans le logiciel infocad."),
                            HttpStatus.OK
                    );
                } else {
                    return new ResponseEntity<>(
                            new ApiResponse<>(true, jwtAuthenticationResponse, "Authentification réussie avec succès."),
                            HttpStatus.OK
                    );
                }
            } else {
                return new ResponseEntity<>(
                        new ApiResponse<>(false, "Authentification échouée."),
                        HttpStatus.OK
                );
            }
        } catch (HttpClientErrorException.MethodNotAllowed e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "Method POST/GET is required."), HttpStatus.OK);
        } catch (NotFoundException | BadRequestException | MyFileNotFoundException | ResourceNotFoundException |
                 FileStorageException e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, e.getMessage()), HttpStatus.OK);
        } catch (NullPointerException e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "Null value has been detected {" + e.getMessage() + "}."), HttpStatus.OK);
        }
        catch (BadCredentialsException ex) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(new ApiResponse<>(false, null, "Identifiants invalides. Veuillez vérifier votre nom d'utilisateur et votre mot de passe."));
        }catch (Exception e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "An error has been occur and the content is {" + e.getMessage() + "}."), HttpStatus.OK);
        }
    }

    @PostMapping("/signup")
    public ResponseEntity<?> createUser(@RequestBody @Valid @Validated UserRequest userRequest) {
        try {
            User user = getUser(userRequest);
            user.setUsername(userRequest.getEmail());
            user = userService.createUser(user, true);
            return new ResponseEntity<>(
                    new ApiResponse<>(true, user, "Inscription effectué avec succès."),
                    HttpStatus.OK
            );
        } catch (HttpClientErrorException.MethodNotAllowed e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "Method POST/GET is required."), HttpStatus.OK);
        } catch (NotFoundException | BadRequestException | MyFileNotFoundException | ResourceNotFoundException |
                 FileStorageException e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, e.getMessage()), HttpStatus.OK);
        } catch (NullPointerException e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "Null value has been detected {" + e.getMessage() + "}."), HttpStatus.OK);
        } catch (Exception e) {
            logger.error(e.getLocalizedMessage());
            return new ResponseEntity<>(new ApiResponse(false, null, "An error has been occur and the content is {" + e.getMessage() + "}."), HttpStatus.OK);
        }
    }

    private User getUser(UserRequest userRequest) {
        User user = new User();
        user.setNom(userRequest.getNom());
        user.setPrenom(userRequest.getPrenom());
        user.setTel(userRequest.getTelephone());
        user.setEmail(userRequest.getEmail());
        user.setUsername(userRequest.getEmail());
        user.setPassword(userRequest.getPassword());
        user.setActive(false);
        Set<Role> roleSet = new HashSet<>();
        roleSet.add(roleService.getRoleByRoleName(UserRole.ROLE_ANONYMOUS).get());
        user.setRoles(roleSet);
        user.setStructure(structureService.getStructureById(userRequest.getStructureId()).get());
        return user;
    }
}
