package de.avatic.lcc.config.filter;


import de.avatic.lcc.config.LccOidcUser;
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.context.annotation.Profile;
import org.springframework.security.core.Authentication;
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;


@Profile("dev | test")
public class DevUserEmulationFilter extends OncePerRequestFilter {

    private final Logger log = LoggerFactory.getLogger(DevUserEmulationFilter.class);

    private static final String DEV_USER_ID_SESSION_KEY = "dev.emulated.user.id";
    private final UserRepository userRepository;

    public DevUserEmulationFilter(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);

        // Add logging to debug
        log.debug("DevUserEmulationFilter - Session ID: " + session.getId());
        log.debug("DevUserEmulationFilter - Emulated User ID: " + emulatedUserId);

        if(emulatedUserId != null) {
            User user = userRepository.getById(emulatedUserId);
            if (user != null) {
                setEmulatedUser(user);
                log.debug("DevUserEmulationFilter - Set user: " + user.getEmail());
            }
        } else {
            log.debug("DevUserEmulationFilter - " + request.getRequestURI() + " - No emulated user set");
        }

        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()
        );

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

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

    public static void setEmulatedUserId(HttpSession session, Integer userId) {
        if (userId == null) {
            session.removeAttribute(DEV_USER_ID_SESSION_KEY);
        } else {
            session.setAttribute(DEV_USER_ID_SESSION_KEY, userId);
        }
    }

    public static Integer getEmulatedUserId(HttpSession session) {
        return (Integer) session.getAttribute(DEV_USER_ID_SESSION_KEY);
    }
}