package com.starry.admin.common.security.filter;

import com.starry.admin.common.domain.LoginUser;
import com.starry.admin.common.security.config.ApiTestSecurityProperties;
import com.starry.admin.modules.system.module.entity.SysUserEntity;
import com.starry.common.constant.SecurityConstants;
import com.starry.common.context.CustomSecurityContextHolder;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

public class ApiTestAuthenticationFilter extends OncePerRequestFilter {

    private final ApiTestSecurityProperties properties;

    public ApiTestAuthenticationFilter(ApiTestSecurityProperties properties) {
        this.properties = properties;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String requestedUser = request.getHeader(properties.getUserHeader());
        String requestedTenant = request.getHeader(properties.getTenantHeader());

        String userId = StringUtils.hasText(requestedUser) ? requestedUser : properties.getDefaults().getUserId();
        String tenantId = StringUtils.hasText(requestedTenant) ? requestedTenant : properties.getDefaults().getTenantId();

        if (!StringUtils.hasText(userId) || !StringUtils.hasText(tenantId)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json");
            response.getWriter().write("{\"code\":401,\"message\":\"Missing test user or tenant header\"}");
            response.getWriter().flush();
            return;
        }

        try {
            LoginUser loginUser = buildLoginUser(userId, tenantId);

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null,
                    Collections.emptyList());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
            CustomSecurityContextHolder.set(SecurityConstants.DETAILS_USER_ID, userId);
            CustomSecurityContextHolder.set(SecurityConstants.DETAILS_USERNAME, userId);
            CustomSecurityContextHolder.setTenantId(tenantId);
            CustomSecurityContextHolder.setPermission(String.join(",", loginUser.getPermissions()));

            filterChain.doFilter(request, response);
        } finally {
            CustomSecurityContextHolder.remove();
            SecurityContextHolder.clearContext();
        }
    }

    private LoginUser buildLoginUser(String userId, String tenantId) {
        SysUserEntity sysUser = new SysUserEntity();
        sysUser.setUserId(userId);
        sysUser.setUserCode(userId);
        sysUser.setRealName(userId);
        sysUser.setTenantId(tenantId);
        sysUser.setSuperAdmin(Boolean.FALSE);
        sysUser.setStatus(0);

        LoginUser loginUser = new LoginUser();
        loginUser.setUser(sysUser);
        loginUser.setUserId(userId);
        loginUser.setUserName(userId);
        loginUser.setToken("apitest-" + userId + "-" + tenantId);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(System.currentTimeMillis() + 3600_000);
        loginUser.setTenantEndDate(new Date(System.currentTimeMillis() + 3600_000));
        loginUser.setTenantStatus(0);

        Set<String> roles = new HashSet<>(properties.getDefaults().getRoles());
        Set<String> permissions = new HashSet<>(properties.getDefaults().getPermissions());
        loginUser.setRoles(roles);
        loginUser.setPermissions(permissions);
        loginUser.setCurrentRole(roles.stream().findFirst().orElse(null));

        return loginUser;
    }
}
