package com.storycove.controller;

import com.storycove.entity.RefreshToken;
import com.storycove.service.LibraryService;
import com.storycove.service.PasswordAuthenticationService;
import com.storycove.service.RefreshTokenService;
import com.storycove.util.JwtUtil;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    private final PasswordAuthenticationService passwordService;
    private final LibraryService libraryService;
    private final JwtUtil jwtUtil;
    private final RefreshTokenService refreshTokenService;

    public AuthController(PasswordAuthenticationService passwordService, LibraryService libraryService, JwtUtil jwtUtil, RefreshTokenService refreshTokenService) {
        this.passwordService = passwordService;
        this.libraryService = libraryService;
        this.jwtUtil = jwtUtil;
        this.refreshTokenService = refreshTokenService;
    }
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request, HttpServletRequest httpRequest, HttpServletResponse response) {
        // Use new library-aware authentication
        String token = passwordService.authenticateAndSwitchLibrary(request.getPassword());

        if (token != null) {
            // Get library ID from JWT token
            String libraryId = jwtUtil.getLibraryIdFromToken(token);

            // Get user agent and IP address for refresh token
            String userAgent = httpRequest.getHeader("User-Agent");
            String ipAddress = getClientIpAddress(httpRequest);

            // Create refresh token
            RefreshToken refreshToken = refreshTokenService.createRefreshToken(libraryId, userAgent, ipAddress);

            // Set access token cookie (24 hours)
            ResponseCookie accessCookie = ResponseCookie.from("token", token)
                    .httpOnly(true)
                    .secure(false) // Set to true in production with HTTPS
                    .path("/")
                    .maxAge(Duration.ofDays(1))
                    .build();

            // Set refresh token cookie (14 days)
            ResponseCookie refreshCookie = ResponseCookie.from("refreshToken", refreshToken.getToken())
                    .httpOnly(true)
                    .secure(false) // Set to true in production with HTTPS
                    .path("/")
                    .maxAge(Duration.ofDays(14))
                    .build();

            response.addHeader(HttpHeaders.SET_COOKIE, accessCookie.toString());
            response.addHeader(HttpHeaders.SET_COOKIE, refreshCookie.toString());

            String libraryInfo = passwordService.getCurrentLibraryInfo();
            return ResponseEntity.ok(new LoginResponse("Authentication successful - " + libraryInfo, token));
        } else {
            return ResponseEntity.status(401).body(new ErrorResponse("Invalid password"));
        }
    }

    @PostMapping("/refresh")
    public ResponseEntity<?> refresh(HttpServletRequest request, HttpServletResponse response) {
        // Get refresh token from cookie
        String refreshTokenString = getRefreshTokenFromCookies(request);

        if (refreshTokenString == null) {
            return ResponseEntity.status(401).body(new ErrorResponse("Refresh token not found"));
        }

        // Verify refresh token
        Optional<RefreshToken> refreshTokenOpt = refreshTokenService.verifyRefreshToken(refreshTokenString);

        if (refreshTokenOpt.isEmpty()) {
            return ResponseEntity.status(401).body(new ErrorResponse("Invalid or expired refresh token"));
        }

        RefreshToken refreshToken = refreshTokenOpt.get();
        String tokenLibraryId = refreshToken.getLibraryId();

        // Check if we need to switch libraries based on refresh token's library ID
        try {
            String currentLibraryId = libraryService.getCurrentLibraryId();

            // Switch library if refresh token's library differs from current library
            // This handles cross-device library switching on token refresh
            if (tokenLibraryId != null && !tokenLibraryId.equals(currentLibraryId)) {
                logger.info("Refresh token library '{}' differs from current library '{}', switching libraries",
                        tokenLibraryId, currentLibraryId);
                libraryService.switchToLibraryAfterAuthentication(tokenLibraryId);
            } else if (currentLibraryId == null && tokenLibraryId != null) {
                // Handle case after backend restart where no library is active
                logger.info("No active library on refresh, switching to refresh token's library: {}", tokenLibraryId);
                libraryService.switchToLibraryAfterAuthentication(tokenLibraryId);
            }
        } catch (Exception e) {
            logger.error("Failed to switch library during token refresh: {}", e.getMessage());
            return ResponseEntity.status(500).body(new ErrorResponse("Failed to switch library: " + e.getMessage()));
        }

        // Generate new access token
        String newAccessToken = jwtUtil.generateToken("user", tokenLibraryId);

        // Set new access token cookie
        ResponseCookie cookie = ResponseCookie.from("token", newAccessToken)
                .httpOnly(true)
                .secure(false) // Set to true in production with HTTPS
                .path("/")
                .maxAge(Duration.ofDays(1))
                .build();

        response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

        return ResponseEntity.ok(new LoginResponse("Token refreshed successfully", newAccessToken));
    }
    
    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request, HttpServletResponse response) {
        // Clear authentication state
        libraryService.clearAuthentication();

        // Revoke refresh token if present
        String refreshTokenString = getRefreshTokenFromCookies(request);
        if (refreshTokenString != null) {
            refreshTokenService.findByToken(refreshTokenString).ifPresent(refreshTokenService::revokeToken);
        }

        // Clear the access token cookie
        ResponseCookie accessCookie = ResponseCookie.from("token", "")
                .httpOnly(true)
                .secure(false)
                .path("/")
                .maxAge(Duration.ZERO)
                .build();

        // Clear the refresh token cookie
        ResponseCookie refreshCookie = ResponseCookie.from("refreshToken", "")
                .httpOnly(true)
                .secure(false)
                .path("/")
                .maxAge(Duration.ZERO)
                .build();

        response.addHeader(HttpHeaders.SET_COOKIE, accessCookie.toString());
        response.addHeader(HttpHeaders.SET_COOKIE, refreshCookie.toString());

        return ResponseEntity.ok(new MessageResponse("Logged out successfully"));
    }
    
    @GetMapping("/verify")
    public ResponseEntity<?> verify(Authentication authentication) {
        if (authentication != null && authentication.isAuthenticated()) {
            return ResponseEntity.ok(new MessageResponse("Token is valid"));
        } else {
            return ResponseEntity.status(401).body(new ErrorResponse("Token is invalid or expired"));
        }
    }

    // Helper methods
    private String getRefreshTokenFromCookies(HttpServletRequest request) {
        if (request.getCookies() == null) {
            return null;
        }

        return Arrays.stream(request.getCookies())
                .filter(cookie -> "refreshToken".equals(cookie.getName()))
                .map(Cookie::getValue)
                .findFirst()
                .orElse(null);
    }

    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }

        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }

        return request.getRemoteAddr();
    }

    // DTOs
    public static class LoginRequest {
        @NotBlank(message = "Password is required")
        private String password;
        
        public String getPassword() {
            return password;
        }
        
        public void setPassword(String password) {
            this.password = password;
        }
    }
    
    public static class LoginResponse {
        private String message;
        private String token;
        
        public LoginResponse(String message, String token) {
            this.message = message;
            this.token = token;
        }
        
        public String getMessage() {
            return message;
        }
        
        public String getToken() {
            return token;
        }
    }
    
    public static class MessageResponse {
        private String message;
        
        public MessageResponse(String message) {
            this.message = message;
        }
        
        public String getMessage() {
            return message;
        }
    }
    
    public static class ErrorResponse {
        private String error;
        
        public ErrorResponse(String error) {
            this.error = error;
        }
        
        public String getError() {
            return error;
        }
    }
}