package de.avatic.lcc.e2e.config;

import de.avatic.lcc.config.LccOidcUser;
import de.avatic.lcc.config.filter.DevUserEmulationFilter;
import de.avatic.lcc.model.db.users.User;
import de.avatic.lcc.repositories.users.UserRepository;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Filter that auto-logins a test user when running E2E tests.
 * This bypasses the need to manually select a user on the /dev page.
 */
public class TestAutoLoginFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(TestAutoLoginFilter.class);
    private static final String TEST_USER_EMAIL = "john.doe@test.com";
    private static final String DEV_USER_ID_SESSION_KEY = "dev.emulated.user.id";

    private final UserRepository userRepository;

    public TestAutoLoginFilter(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain) throws ServletException, IOException {

        HttpSession session = request.getSession(true);
        Integer emulatedUserId = (Integer) session.getAttribute(DEV_USER_ID_SESSION_KEY);

        // If no user is selected, auto-login the test user
        if (emulatedUserId == null) {
            try {
                User testUser = userRepository.getByEmail(TEST_USER_EMAIL);
                if (testUser != null) {
                    log.debug("TestAutoLoginFilter - Auto-logging in test user: {}", TEST_USER_EMAIL);
                    session.setAttribute(DEV_USER_ID_SESSION_KEY, testUser.getId());
                    setEmulatedUser(testUser);
                } else {
                    log.warn("TestAutoLoginFilter - Test user {} not found", TEST_USER_EMAIL);
                }
            } catch (Exception e) {
                log.debug("TestAutoLoginFilter - Could not auto-login: {}", e.getMessage());
            }
        } else {
            // User is already selected, set authentication
            User user = userRepository.getById(emulatedUserId);
            if (user != null) {
                setEmulatedUser(user);
            }
        }

        filterChain.doFilter(request, response);
    }

    private void setEmulatedUser(User user) {
        Set<GrantedAuthority> authorities = new HashSet<>();
        user.getGroups().forEach(group ->
                authorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName().toUpperCase()))
        );

        // Create a mock OIDC user
        Map<String, Object> claims = new HashMap<>();
        claims.put("sub", user.getId().toString());
        claims.put("email", user.getEmail());
        claims.put("preferred_username", user.getEmail());
        claims.put("name", user.getFirstName() + " " + user.getLastName());
        if (user.getWorkdayId() != null) {
            claims.put("workday_id", user.getWorkdayId());
        }

        OidcIdToken idToken = new OidcIdToken(
                "mock-token",
                Instant.now(),
                Instant.now().plusSeconds(3600),
                claims
        );

        OidcUserInfo userInfo = new OidcUserInfo(claims);

        LccOidcUser oidcUser = new LccOidcUser(
                authorities,
                idToken,
                userInfo,
                "preferred_username",
                user.getId()
        );

        var authentication = new PreAuthenticatedAuthenticationToken(
                oidcUser,
                null,
                authorities
        );

        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        SecurityContextHolder.setContext(context);
    }
}
