package com.lesingle.edu.common.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lesingle.edu.common.enums.GenericStatus;
import com.lesingle.edu.common.enums.TenantStatus;
import com.lesingle.edu.common.enums.UserRole;
import com.lesingle.edu.entity.AdminUser;
import com.lesingle.edu.entity.Parent;
import com.lesingle.edu.entity.Tenant;
import com.lesingle.edu.entity.Teacher;
import com.lesingle.edu.mapper.AdminUserMapper;
import com.lesingle.edu.mapper.ParentMapper;
import com.lesingle.edu.mapper.TenantMapper;
import com.lesingle.edu.mapper.TeacherMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT Authentication Filter
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final JwtTokenRedisService jwtTokenRedisService;
    private final AdminUserMapper adminUserMapper;
    private final TenantMapper tenantMapper;
    private final TeacherMapper teacherMapper;
    private final ParentMapper parentMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        log.debug("JwtAuthenticationFilter doFilterInternal called for: {}", request.getRequestURI());
        try {
            String token = resolveToken(request);
            log.debug("Token extracted: {}", token != null ? "present" : "null");
            if (StringUtils.hasText(token)) {
                log.debug("Token validation starting...");
                // 验证 token 并获取错误原因
                String tokenErrorReason = jwtTokenProvider.validateTokenWithReason(token);
                if (tokenErrorReason != null) {
                    // token 无效，返回 401 错误
                    log.debug("Token validation failed: {}", tokenErrorReason);
                    sendError(response, HttpStatus.UNAUTHORIZED, getErrorMessage(tokenErrorReason));
                    return;
                }

                JwtPayload payload = jwtTokenProvider.getPayloadFromToken(token);

                // 使用 Redis 验证 token 是否有效（仅检查黑名单）
                if (!jwtTokenRedisService.validateToken(payload.getUsername(), token)) {
                    log.debug("Token validation failed in Redis for user: {}", payload.getUsername());
                    sendError(response, HttpStatus.UNAUTHORIZED, "Token 已失效，请重新登录");
                    return;
                }

                // 检查账户状态是否为 active
                if (!isAccountActive(payload)) {
                    log.debug("Account is not active for user: {}", payload.getUsername());
                    sendError(response, HttpStatus.UNAUTHORIZED, "账户已被禁用，请联系管理员");
                    return;
                }

                // 使用 UserRole 枚举确保角色有效性
                UserRole.fromCode(payload.getRole());
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(
                                payload,
                                null,
                                Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + payload.getRole().toUpperCase()))
                        );

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception e) {
            log.error("Cannot set user authentication: {}", e.getMessage());
            sendError(response, HttpStatus.UNAUTHORIZED, "Token 验证失败，请重新登录");
        }

        filterChain.doFilter(request, response);
    }

    /**
     * 根据错误原因返回错误消息
     */
    private String getErrorMessage(String errorReason) {
        switch (errorReason) {
            case "TOKEN_EXPIRED":
                return "Token 已过期，请重新登录";
            case "TOKEN_INVALID":
                return "Token 无效，请重新登录";
            case "TOKEN_UNSUPPORTED":
                return "不支持的 Token 格式";
            case "TOKEN_EMPTY":
                return "Token 为空";
            default:
                return "Token 验证失败，请重新登录";
        }
    }

    /**
     * 发送错误响应
     */
    private void sendError(HttpServletResponse response, HttpStatus status, String message) throws IOException {
        response.setStatus(status.value());
        response.setContentType("application/json;charset=UTF-8");
        Map<String, Object> body = new HashMap<>();
        body.put("code", status.value());
        body.put("message", message);
        String json = new ObjectMapper().writeValueAsString(body);
        response.getWriter().write(json);
    }

    private String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }

    /**
     * 检查账户是否处于激活状态
     * 根据用户角色查询对应的表，检查 status 字段是否为 "active"
     *
     * @param payload JWT payload
     * @return true 如果账户激活
     */
    private boolean isAccountActive(JwtPayload payload) {
        String role = payload.getRole();
        Long userId = payload.getUserId();

        return switch (role) {
            case "admin" -> {
                AdminUser adminUser = adminUserMapper.selectById(userId);
                yield adminUser != null && GenericStatus.isActive(adminUser.getStatus());
            }
            case "school" -> {
                Tenant tenant = tenantMapper.selectById(userId);
                // 租户状态需要是 ACTIVE 才算激活
                yield tenant != null && TenantStatus.ACTIVE.getCode().equalsIgnoreCase(tenant.getStatus());
            }
            case "teacher" -> {
                Teacher teacher = teacherMapper.selectById(userId);
                yield teacher != null && GenericStatus.isActive(teacher.getStatus());
            }
            case "parent" -> {
                Parent parent = parentMapper.selectById(userId);
                yield parent != null && GenericStatus.isActive(parent.getStatus());
            }
            default -> false;
        };
    }

}
