package com.wishurgift.entry.controller;

import com.wishurgift.entry.security.Connected;
import com.wishurgift.entry.security.Secured;
import com.wishurgift.entry.validator.LoginValidator;
import com.wishurgift.entry.validator.RegisterValidator;
import com.wishurgift.entry.validator.UpdateUserValidator;
import com.wishurgift.service.UserService;
import com.wishurgift.service.dto.ErrorDTO;
import com.wishurgift.service.dto.LoginDTO;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.WishlistHeaderDTO;
import com.wishurgift.service.dto.request.LoginRequestDTO;
import com.wishurgift.service.dto.request.RegisterRequestDTO;
import com.wishurgift.service.dto.request.UpdateUserRequestDTO;
import com.wishurgift.utils.Constant;
import com.wishurgift.utils.JwtUtils;
import com.wishurgift.utils.ResponseUtils;
import com.wishurgift.utils.StringUtils;
import com.wishurgift.wrapper.Page;
import com.wishurgift.wrapper.Result;
import lombok.extern.slf4j.Slf4j;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

@Slf4j
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserController {

    @Inject
    private UserService service;

    @Inject
    private RegisterValidator registerValidator;

    @Inject
    private LoginValidator loginValidator;

    @Inject
    private UpdateUserValidator updateUserValidator;

    @GET
    @Connected
    @Path("/{id}")
    public Response getUser(@PathParam("id") long id, @Context SecurityContext securityContext) {
        log.info("Begin UserController getUser, id={}", id);
        // Recuperation de l'utilisateur connecté
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        // Récupération de l'utilisateur
        Result result = service.getUser(principal, id);
        if (!result.isSuccess()) {
            ErrorDTO err = (ErrorDTO) result.getData();
            log.info("End UserController getUser, error: " + err.getMessage());
            return ResponseUtils.make(result);
        }
        log.info("End UserController getUser");
        return Result.success().data(result.getData()).response();
    }

    @GET
    @Path("/search/{request}")
    public Response search(@PathParam("request") String request) {
        return search(request, 0);
    }

    @GET
    @Path("/search/{request}/{limit}")
    public Response search(@PathParam("request") String request, @PathParam("limit") int limit) {
        log.info("Begin UserController search, request={} limt={}", request, limit);
        // Verification parametre
        if (StringUtils.isBlank(request)) {
            log.info("End UserController search, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.SEARCH_REQUEST_EMPTY).response();
        }
        if (limit < 0) {
            log.info("End UserController search, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.LIMIT_INVALID).response();
        }
        // Recherche
        Page<UserDTO> result = service.search(request, limit);
        log.info("End UserController search, result: " + result.getTotal());
        return Result.success().data(result).response();
    }

    @GET
    @Connected
    @Path("/{id}/wishlists")
    public Response wishlist(@PathParam("id") long id, @Context SecurityContext securityContext) {
        log.info("Begin UserController wishlist, id={}", id);
        if (id < 1) {
            log.info("End UserController wishlist, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.INVALID_ID).response();
        }
        // Recup utilisateur principale
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        // Recup les demandes effectuée par l'utilisateur connecté
        Page<WishlistHeaderDTO> page = service.getWishlist(principal, id);
        if (page == null) {
            log.info("End UserController wishlist, unable to get data");
            return Result.error().data(Result.Error.UNABLE_TO_GET_DATA).trace(Constant.USER_NOT_FOUND).response();
        }
        log.info("End UserController wishlist");
        return Result.success().data(page).response();
    }

    @GET
    @Connected
    @Path("/{id}/wishlists/page/{num}/{nb}")
    public Response wishlistPage(@PathParam("id") long id, @PathParam("num") int numPage, @PathParam("nb") int nbEltPage, @Context SecurityContext securityContext) {
        log.info("Begin UserController wishlistPage, id={} numPage={} nbElt={}", id, numPage, nbEltPage);
        if (id < 1) {
            log.info("End UserController wishlistPage, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.INVALID_ID).response();
        }
        // Verif que le numero de page est valide
        if (numPage < 1) {
            log.info("End UserController wishlistPage, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.PAGE_NUMBER_INVALID).response();
        }
        // Recup utilisateur principale
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        // Recup les demandes effectuée par l'utilisateur connecté
        Page<WishlistHeaderDTO> page = service.getWishlist(numPage, nbEltPage, principal, id);
        if (page == null) {
            log.info("End UserController wishlistPage, unable to get data");
            return Result.error().data(Result.Error.UNABLE_TO_GET_DATA).trace(Constant.USER_NOT_FOUND).response();
        }
        log.info("End UserController wishlistPage");
        return Result.success().data(page).response();
    }

    @GET
    @Secured
    @Path("/wishlists/shared")
    public Response shared(@Context SecurityContext securityContext) {
        log.info("Begin UserController shared");
        // Recup utilisateur principale
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        if (principal == null) {
            log.info("End UserController shared, unable to connect");
            return Result.error().data(Result.Error.UNABLE_TO_CONNECT).trace(Constant.USER_NOT_FOUND).response();
        }
        // Recup les listes
        Page<WishlistHeaderDTO> page = service.getShared(principal);
        if (page == null) {
            log.info("End UserController shared, unable to get data");
            return Result.error().data(Result.Error.UNABLE_TO_GET_DATA).response();
        }
        log.info("End UserController shared");
        return Result.success().data(page).response();
    }

    @GET
    @Secured
    @Path("/wishlists/shared/page/{num}/{nb}")
    public Response sharedPage(@PathParam("num") int numPage, @PathParam("nb") int nbEltPage, @Context SecurityContext securityContext) {
        log.info("Begin UserController sharedPage, numPage={} nbElt={}", numPage, nbEltPage);
        // Verif que le numero de page est valide
        if (numPage < 1) {
            log.info("End UserController sharedPage, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(Constant.PAGE_NUMBER_INVALID).response();
        }
        // Recup utilisateur principale
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        if (principal == null) {
            log.info("End UserController sharedPage, unable to connect");
            return Result.error().data(Result.Error.UNABLE_TO_CONNECT).trace(Constant.USER_NOT_FOUND).response();
        }
        // Recup les demandes effectuée par l'utilisateur connecté
        Page<WishlistHeaderDTO> page = service.getShared(numPage, nbEltPage, principal);
        if (page == null) {
            log.info("End UserController sharedPage, unable to get data");
            return Result.error().data(Result.Error.UNABLE_TO_GET_DATA).response();
        }
        log.info("End UserController sharedPage");
        return Result.success().data(page).response();
    }

    @POST
    @Path("/login")
    public Response login(LoginRequestDTO data) {
        log.info("Begin UserController login, login={}", data.getLogin());
        //Vérification des données
        if (loginValidator.isNotValid(data)) {
            log.info("End UserController login, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(loginValidator.getAllErrors()).response();
        }
        //Connexion de l'utilisateur
        LoginDTO login = service.login(data);
        if (login == null) {
            //compte ou password incorrect
            log.info("End UserController login, invalid login or password");
            return Result.error().data(Result.Error.UNABLE_TO_CONNECT).trace(Constant.LOGIN_FAIL).response();
        }
        log.info("End UserController login, connected");
        return Result.success().data(login).response();
    }

    @POST
    public Response register(RegisterRequestDTO data) {
        log.info("Begin UserController register, name={} email={}", data.getName(), data.getEmail());
        // Verification des données
        if (registerValidator.isNotValid(data)) {
            log.info("End UserController register, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(registerValidator.getAllErrors()).response();
        }
        // Inscription de l'utilisateur
        UserDTO user = service.register(data);
        // Si null est retourné c'est que l'utilisateur existe deja
        if (user == null) {
            log.info("End UserController register, user already exist");
            return Result.error().data(Result.Error.USER_ALREADY_EXIST).response();
        }
        log.info("End UserController register, user added");
        return Result.success().response();
    }

    @PUT
    @Secured
    public Response update(UpdateUserRequestDTO request, @Context SecurityContext securityContext) {
        log.info("Begin UserController update");
        // Recuperation utilsateur connecté
        UserDTO principal = JwtUtils.getUser(securityContext.getUserPrincipal());
        if (principal == null) {
            log.info("End UserController update, unable to connect");
            return Result.error().data(Result.Error.UNABLE_TO_CONNECT).trace(Constant.USER_NOT_FOUND).response();
        }
        // Verification des données
        if (updateUserValidator.isNotValid(request)) {
            log.info("End UserController update, invalid request");
            return Result.error().data(Result.Error.INVALID_REQUEST).trace(updateUserValidator.getAllErrors()).response();
        }
        // Mise à jour
        UserDTO user = service.update(principal, request);
        if (user == null) {
            log.info("End UserController update, unauthorized");
            return Result.error().data(Result.Error.UNAUTHORIZED).trace(Constant.EMAIL_ALREADY_USED).response();
        }
        log.info("End UserController update, user updated");
        return Result.success().data(user).response();
    }

    @PUT
    @Secured
    @Path("/refresh")
    public Response refresh(@Context SecurityContext securityContext) {
        log.info("Begin UserController refresh");
        String token = service.refresh(securityContext.getUserPrincipal());
        if (token == null) {
            log.info("End UserController refresh, can not refresh token");
            return Result.error().data(Result.Error.UNAUTHORIZED).response();
        }
        log.info("End UserController refresh, token refresh");
        return Result.success().data(new LoginDTO(token)).response();
    }

}
