package com.psg.dlsysinfo.dl_sysinfo_server.controller;

import com.psg.dlsysinfo.dl_sysinfo_server.dto.AuthRequest;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.ChangePasswordRequest;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.LoginResponse;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.UserAuth;
import com.psg.dlsysinfo.dl_sysinfo_server.security.CurrentUser;
import com.psg.dlsysinfo.dl_sysinfo_server.security.EncryptionService;
import com.psg.dlsysinfo.dl_sysinfo_server.security.JwtUtil;
import com.psg.dlsysinfo.dl_sysinfo_server.security.TokenResolver;
import com.psg.dlsysinfo.dl_sysinfo_server.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

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

    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;
    private final UserService userAuthService;
    private final EncryptionService encryptionService;
    private final TokenResolver tokenResolver;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public AuthController(AuthenticationManager authenticationManager,
                          JwtUtil jwtUtil,
                          UserService userAuthService,
                          EncryptionService encryptionService,
                          TokenResolver tokenResolver,
                          Environment environment) {
        this.authenticationManager = authenticationManager;
        this.jwtUtil = jwtUtil;
        this.userAuthService = userAuthService;
        this.encryptionService = encryptionService;
        this.tokenResolver = tokenResolver;
    }

    private static String stripBOM(String s) {
        if (s.startsWith("\uFEFF")) {
            return s.substring(1);
        }
        return s;
    }

    private String resolveCookieDomain(HttpServletRequest request) {
        String host = request.getHeader("Host");
        System.out.println("🔍 Host: " + host);

        if (host != null && host.contains("localhost")) {
            return "localhost";
        }

        return ".psg.net.au";
    }




    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody AuthRequest encryptedRequest,
                                              HttpServletRequest request,
                                              HttpServletResponse response) {
        try {
            String username = encryptionService.decryptData(encryptedRequest.getUsername()).trim();
            String password = encryptionService.decryptData(encryptedRequest.getPassword()).trim();
            password = stripBOM(password).replaceAll("[^\\x20-\\x7E]", "").trim();

            UserAuth user = userAuthService.findByUsername(username);
            if (user == null) {
                return ResponseEntity.status(401).body(Map.of("error", "User not found"));
            }

            boolean passwordMatches = passwordEncoder.matches(password, user.getPasswordHash());
            if (!passwordMatches) {
                return ResponseEntity.status(401).body(Map.of("error", "Invalid password"));
            }

            if (user.getClient() == null) {
                return ResponseEntity.status(401).body(Map.of("error", "User is not linked to any client"));
            }

            String decryptedDisplayName = user.getDisplayNameHash() != null
                    ? encryptionService.decryptData(user.getDisplayNameHash())
                    : "";

            List<String> roles = List.of(user.getRole());
            System.out.printf("✅ Assigning roles %s to user %s%n", roles, username);

            String token = jwtUtil.generateToken(
                    username,
                    decryptedDisplayName,
                    user.getClient().getClientIdentifier(),
                    user.getId(),
                    roles
            );

            // Resolve domain and use SameSite=None always now that we’re on HTTPS real origins
            String cookieDomain = resolveCookieDomain(request);

            ResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie.from("authToken", token)
                    .httpOnly(true)
                    .secure(true)
                    .path("/")
                    .sameSite("None") // ✅ Always set to None for frontend-to-backend fetches
                    .maxAge(60 * 60); // 1 hour

            if (!"localhost".equals(cookieDomain)) {
                cookieBuilder.domain(cookieDomain);
            }

            ResponseCookie cookie = cookieBuilder.build();

            System.out.println("🧁 Set-Cookie: " + cookie.toString());
            response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

            return ResponseEntity.ok(new LoginResponse(token, user.getUsername(), user.getId()));
        } catch (Exception e) {
            return ResponseEntity.status(401).body(Map.of("error", "Invalid credentials or decryption error"));
        }
    }


    @PostMapping("/logout")
    public ResponseEntity<Map<String, String>> logout(HttpServletRequest request,
                                                      HttpServletResponse response) {
        SecurityContextHolder.clearContext();

        ResponseCookie expiredCookie = ResponseCookie.from("authToken", "")
                .httpOnly(true)
                .secure(true)
                .path("/")
                .sameSite("None")
                .maxAge(0)
                .domain(resolveCookieDomain(request))
                .build();

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

        return ResponseEntity.ok(Map.of("message", "Logged out successfully"));
    }

    @PutMapping("/change-password")
    public ResponseEntity<?> changePassword(@RequestBody ChangePasswordRequest request,
                                            @AuthenticationPrincipal CurrentUser user) {
        boolean success = userAuthService.changePassword(user.getUsername(), request);
        if (success) {
            return ResponseEntity.ok("Password changed successfully");
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Incorrect current password");
        }
    }
    @PostMapping("/refresh")
    public ResponseEntity<?> refreshToken(HttpServletRequest request, HttpServletResponse response) {
        String token = tokenResolver.resolveToken(request);

        if (token != null && jwtUtil.validateToken(token)) {
            String username = jwtUtil.extractUsername(token);
            String displayName = jwtUtil.extractDisplayName(token);
            String clientIdentifier = jwtUtil.extractClientIdentifier(token);
            Long userId = jwtUtil.extractUserId(token);
            List<String> roles = jwtUtil.extractRoles(token);

            // Generate new token with extended expiry
            String newToken = jwtUtil.generateToken(username, displayName, clientIdentifier, userId, roles);

            // Set new cookie
            ResponseCookie cookie = ResponseCookie.from("authToken", newToken)
                .httpOnly(true)
                .secure(true)
                .path("/")
                .sameSite("None")
                .maxAge(60 * 60)
                .domain(resolveCookieDomain(request))
                .build();

            response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
            return ResponseEntity.ok(Map.of("message", "Token refreshed"));
        }

        return ResponseEntity.status(401).body(Map.of("error", "Invalid token"));
    }


}
