// Path: src/main/java/com/lyubenblagoev/postfixrest/controller/model/LogoutRequest.java
// public class LogoutRequest {
// 
//     private String login;
// 
//     private String refreshToken;
// 
//     public String getLogin() {
//         return login;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/DeviceInformation.java
// public class DeviceInformation {
// 
//     private String ipAddress;
// 
//     private String browser;
// 
//     private String os;
// 
//     public DeviceInformation(String ipAddress, String browser, String os) {
//         this.ipAddress = ipAddress;
//         this.browser = browser;
//         this.os = os;
//     }
// 
//     public String getIpAddress() {
//         return ipAddress;
//     }
// 
//     public String getBrowser() {
//         return browser;
//     }
// 
//     public String getOs() {
//         return os;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/AuthService.java
// public interface AuthService {
// 
//     AuthResponse createTokens(String email);
// 
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/UserService.java
// public interface UserService {
//     Optional<User> findByEmail(String email);
// 
//     Optional<User> findByEmailAndPassword(String email, String password);
// 
//     Optional<User> update(UserChangeRequest userChangeRequest);
// 
//     void addDeviceTokens(String email, DeviceInformation device, String refreshToken, Date refreshTokenExpirationDate);
// 
//     void updateDeviceTokens(String email, DeviceInformation device, String previousRefreshToken,
//             String newRefreshToken, Date refreshTokenExpirationDate);
// 
//     void removeDeviceToken(String email, String refreshToken);
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/AuthResponse.java
// public class AuthResponse {
// 
//     private String token;
// 
//     private String refreshToken;
// 
//     private Date refreshTokenExpirationDate;
// 
//     public AuthResponse(String token, String refreshToken, Date refreshTokenExpirationDate) {
//         this.token = token;
//         this.refreshToken = refreshToken;
//         this.refreshTokenExpirationDate = refreshTokenExpirationDate;
//     }
// 
//     public String getToken() {
//         return token;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public Date getRefreshTokenExpirationDate() {
//         return refreshTokenExpirationDate;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/LoginRequest.java
// public class LoginRequest {
//     @NotEmpty @Email private String login;
//     @NotEmpty private String password;
// 
//     public String getLogin() {
//         return login;
//     }
// 
//     public void setLogin(String login) {
//         this.login = login;
//     }
// 
//     public String getPassword() {
//         return password;
//     }
// 
//     public void setPassword(String password) {
//         this.password = password;
//     }
// 
//     @Override
//     public String toString() {
//         return "LoginRequest{" +
//                 "user='" + login + '\'' +
//                 ", password='" + password + '\'' +
//                 '}';
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/RefreshTokenRequest.java
// public class RefreshTokenRequest {
// 
//     private String refreshToken;
// 
//     private String login;
// 
//     public RefreshTokenRequest(String refreshToken, String login) {
//         this.refreshToken = refreshToken;
//         this.login = login;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public String getLogin() {
//         return login;
//     }
// }


import com.lyubenblagoev.postfixrest.controller.model.LogoutRequest;
import com.lyubenblagoev.postfixrest.service.model.DeviceInformation;
import com.lyubenblagoev.postfixrest.service.AuthService;
import com.lyubenblagoev.postfixrest.service.UserService;
import com.lyubenblagoev.postfixrest.service.model.AuthResponse;
import com.lyubenblagoev.postfixrest.service.model.LoginRequest;
import com.lyubenblagoev.postfixrest.service.model.RefreshTokenRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

    public AuthController(DaoAuthenticationProvider authenticationProvider,
                          AuthService authService,
                          UserService userService,
                          DeviceInformationConverter deviceInformationConverter) {
        this.authenticationProvider = authenticationProvider;
        this.authService = authService;
        this.userService = userService;
        this.deviceInformationConverter = deviceInformationConverter;
    }

    @PostMapping("/signin")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest, HttpServletRequest request) {
        authenticationProvider.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getLogin(), loginRequest.getPassword()));
        DeviceInformation device = deviceInformationConverter.convert(request);
        logger.debug("New login for user {} from IP address {} using {} on {}", loginRequest.getLogin(),
                device.getIpAddress(), device.getBrowser(), device.getOs());
        AuthResponse newTokens = authService.createTokens(loginRequest.getLogin());
        userService.addDeviceTokens(loginRequest.getLogin(), device,
                newTokens.getRefreshToken(), newTokens.getRefreshTokenExpirationDate());
        return ResponseEntity.ok(newTokens);
    }

    @PostMapping("/signout")
    public ResponseEntity<?> logout(@RequestBody LogoutRequest logoutRequest, Authentication authentication) {
        if (authentication == null) {
            logger.warn("Unauthenticated logout request from " + logoutRequest.getLogin());
            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
        }
// Path: src/main/java/com/lyubenblagoev/postfixrest/controller/model/LogoutRequest.java
// public class LogoutRequest {
// 
//     private String login;
// 
//     private String refreshToken;
// 
//     public String getLogin() {
//         return login;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/DeviceInformation.java
// public class DeviceInformation {
// 
//     private String ipAddress;
// 
//     private String browser;
// 
//     private String os;
// 
//     public DeviceInformation(String ipAddress, String browser, String os) {
//         this.ipAddress = ipAddress;
//         this.browser = browser;
//         this.os = os;
//     }
// 
//     public String getIpAddress() {
//         return ipAddress;
//     }
// 
//     public String getBrowser() {
//         return browser;
//     }
// 
//     public String getOs() {
//         return os;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/AuthService.java
// public interface AuthService {
// 
//     AuthResponse createTokens(String email);
// 
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/UserService.java
// public interface UserService {
//     Optional<User> findByEmail(String email);
// 
//     Optional<User> findByEmailAndPassword(String email, String password);
// 
//     Optional<User> update(UserChangeRequest userChangeRequest);
// 
//     void addDeviceTokens(String email, DeviceInformation device, String refreshToken, Date refreshTokenExpirationDate);
// 
//     void updateDeviceTokens(String email, DeviceInformation device, String previousRefreshToken,
//             String newRefreshToken, Date refreshTokenExpirationDate);
// 
//     void removeDeviceToken(String email, String refreshToken);
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/AuthResponse.java
// public class AuthResponse {
// 
//     private String token;
// 
//     private String refreshToken;
// 
//     private Date refreshTokenExpirationDate;
// 
//     public AuthResponse(String token, String refreshToken, Date refreshTokenExpirationDate) {
//         this.token = token;
//         this.refreshToken = refreshToken;
//         this.refreshTokenExpirationDate = refreshTokenExpirationDate;
//     }
// 
//     public String getToken() {
//         return token;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public Date getRefreshTokenExpirationDate() {
//         return refreshTokenExpirationDate;
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/LoginRequest.java
// public class LoginRequest {
//     @NotEmpty @Email private String login;
//     @NotEmpty private String password;
// 
//     public String getLogin() {
//         return login;
//     }
// 
//     public void setLogin(String login) {
//         this.login = login;
//     }
// 
//     public String getPassword() {
//         return password;
//     }
// 
//     public void setPassword(String password) {
//         this.password = password;
//     }
// 
//     @Override
//     public String toString() {
//         return "LoginRequest{" +
//                 "user='" + login + '\'' +
//                 ", password='" + password + '\'' +
//                 '}';
//     }
// }
// 
// Path: src/main/java/com/lyubenblagoev/postfixrest/service/model/RefreshTokenRequest.java
// public class RefreshTokenRequest {
// 
//     private String refreshToken;
// 
//     private String login;
// 
//     public RefreshTokenRequest(String refreshToken, String login) {
//         this.refreshToken = refreshToken;
//         this.login = login;
//     }
// 
//     public String getRefreshToken() {
//         return refreshToken;
//     }
// 
//     public String getLogin() {
//         return login;
//     }
// }


// Path: src/main/java/com/lyubenblagoev/postfixrest/controller/AuthController.java
import com.lyubenblagoev.postfixrest.controller.model.LogoutRequest;
import com.lyubenblagoev.postfixrest.service.model.DeviceInformation;
import com.lyubenblagoev.postfixrest.service.AuthService;
import com.lyubenblagoev.postfixrest.service.UserService;
import com.lyubenblagoev.postfixrest.service.model.AuthResponse;
import com.lyubenblagoev.postfixrest.service.model.LoginRequest;
import com.lyubenblagoev.postfixrest.service.model.RefreshTokenRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;


    public AuthController(DaoAuthenticationProvider authenticationProvider,
                          AuthService authService,
                          UserService userService,
                          DeviceInformationConverter deviceInformationConverter) {
        this.authenticationProvider = authenticationProvider;
        this.authService = authService;
        this.userService = userService;
        this.deviceInformationConverter = deviceInformationConverter;
    }

    @PostMapping("/signin")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest, HttpServletRequest request) {
        authenticationProvider.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getLogin(), loginRequest.getPassword()));
        DeviceInformation device = deviceInformationConverter.convert(request);
        logger.debug("New login for user {} from IP address {} using {} on {}", loginRequest.getLogin(),
                device.getIpAddress(), device.getBrowser(), device.getOs());
        AuthResponse newTokens = authService.createTokens(loginRequest.getLogin());
        userService.addDeviceTokens(loginRequest.getLogin(), device,
                newTokens.getRefreshToken(), newTokens.getRefreshTokenExpirationDate());
        return ResponseEntity.ok(newTokens);
    }

    @PostMapping("/signout")
    public ResponseEntity<?> logout(@RequestBody LogoutRequest logoutRequest, Authentication authentication) {
        if (authentication == null) {
            logger.warn("Unauthenticated logout request from " + logoutRequest.getLogin());
            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
        }
        userService.removeDeviceToken(logoutRequest.getLogin(), logoutRequest.getRefreshToken());