package org.lingniu.idp.service.core.login;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.lingniu.idp.model.dto.LoginUser;
import org.lingniu.idp.model.vo.AccessTokenInfo;
import org.lingniu.idp.model.vo.RefreshTokenInfo;
import org.lingniu.idp.model.vo.TokenInfo;
import org.lingniu.idp.service.ISysUserService;
import org.lingniu.idp.utils.DateUtils;
import org.lingniu.idp.utils.DeviceFingerprintUtil;
import org.lingniu.idp.utils.ip.IpUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.UUID;

@Component
@Slf4j
public class IdpTokenService {
    // 令牌有效期（默认30分钟）单位分钟
    @Value("${token.accessToken.expireTime:30}")
    private int accessTokenExpireTime;
    // 刷新令牌有效期(默认24小时) 单位小时
    @Value("${token.refreshToken.expireTime:24}")
    private int refreshTokenExpireTime;
    @Value("${token.header:Idp}")
    private String tokenHeader;
    private final RedisRefreshTokenService refreshTokenService;

    private final RedisAccessTokenService accessTokenService;
    private final ISysUserService userService;

    public IdpTokenService(RedisRefreshTokenService refreshTokenService, RedisAccessTokenService accessTokenService, ISysUserService userService) {
        this.refreshTokenService = refreshTokenService;
        this.accessTokenService = accessTokenService;
        this.userService = userService;
    }

    public TokenInfo createToken(HttpServletRequest request, LoginUser loginUser){
        String accessToken = UUID.randomUUID().toString().replace("-", "");
        String refreshToken = UUID.randomUUID().toString().replace("-", "");
        String ip = IpUtils.getIpAddr(request);
        String deviceId = DeviceFingerprintUtil.generateFingerprint(request);
        String userAgent = request.getHeader("User-Agent");
        Instant issuedAt = Instant.now();
        Instant accessExpiresAt = issuedAt.plus(accessTokenExpireTime,ChronoUnit.SECONDS);
        Instant refreshExpiresAt = issuedAt.plus(refreshTokenExpireTime, ChronoUnit.HOURS);
        AccessTokenInfo accessTokenInfo = AccessTokenInfo.builder()
                .tokenValue(accessToken)
                .userAgent(userAgent)
                .userId(loginUser.getUser().getUserId().toString())
                .username(loginUser.getUsername())
                .issuedAt(issuedAt)
                .expiresAt(accessExpiresAt)
                .deviceId(deviceId)
                .ipAddress(ip)
                .refreshTokenId(refreshToken)
                .build();
        RefreshTokenInfo refreshTokenInfo = RefreshTokenInfo.builder()
                .tokenValue(refreshToken)
                .userId(loginUser.getUser().getUserId().toString())
                .accessToken(accessToken)
                .username(loginUser.getUsername())
                .deviceId(deviceId)
                .createdAt(issuedAt)
                .lastUsedAt(refreshExpiresAt)
                .accessTokenCount(1)
                .usageCount(0)
                .ipAddress(ip)
                .build();
        return new TokenInfo(accessTokenInfo,refreshTokenInfo);
    }
    public void storeTokenInfo(TokenInfo tokenInfo){
        accessTokenService.storeAccessToken(tokenInfo.getAccessTokenInfo());

        refreshTokenService.storeRefreshToken(tokenInfo.getRefreshTokenInfo());
    }
    public void setAccessTokenHeader(HttpServletResponse response,String accessToken){
        response.addHeader(tokenHeader,accessToken);

    }

    public void setRefreshTokenCookie(HttpServletResponse response, TokenInfo tokenInfo) {
        String refreshToken = tokenInfo.getRefreshTokenInfo().getTokenValue();
//        Cookie cookie = new Cookie("refresh_token", refreshToken);
//        cookie.setHttpOnly(true);
//        cookie.setSecure(true); // 生产环境设为true
//        cookie.setPath("/");
//        cookie.setMaxAge(refreshTokenExpireTime * 60 * 60);
//        cookie.setDomain(".lingniu.com"); // 设置域名

        // 添加SameSite属性
        response.addHeader("Set-Cookie",
                String.format("idp_refresh_token=%s; HttpOnly; Secure; Path=/; Max-Age=%d; SameSite=Strict",
                        refreshToken, refreshTokenExpireTime * 60 * 60));
    }
    public String getCookieRefreshToken(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        return Arrays.stream(cookies)
                .filter(cookie -> "idp_refresh_token".equals(cookie.getName()))
                .map(Cookie::getValue)
                .findFirst()
                .orElse(null);
    }
    public boolean validateAccessToken(HttpServletRequest request){
        String accessToken = request.getHeader(tokenHeader);
        return accessTokenService.validateAccessToken(accessToken);
    }

    public AccessTokenInfo getAccessTokenInfo(HttpServletRequest request){
        String accessToken = request.getHeader(tokenHeader);
        return accessTokenService.getAccessTokenInfo(accessToken);
    }
    public RefreshTokenInfo getRefreshTokenInfo(HttpServletRequest request){
        String accessToken = getCookieRefreshToken(request);
        return refreshTokenService.getRefreshTokenInfo(accessToken);
    }

    public AccessTokenInfo refreshToken(HttpServletRequest request,HttpServletResponse response) throws IOException {
        String accessToken = request.getHeader(tokenHeader);
        String cookieRefreshToken = getCookieRefreshToken(request);
        RefreshTokenInfo refreshTokenInfo = refreshTokenService.getRefreshTokenInfo(cookieRefreshToken);
        if(refreshTokenInfo == null || !refreshTokenInfo.isValid()){
            log.info("refresh token is expire");
            return null;
        }

        if(refreshTokenInfo.getAccessToken()!=null && !refreshTokenInfo.getAccessToken().equals(accessToken)){
            log.info("token 已刷新");
        }
        String accessTokenNew = UUID.randomUUID().toString().replace("-", "");
        String ip = IpUtils.getIpAddr(request);
        String deviceId = DeviceFingerprintUtil.generateFingerprint(request);
        String userAgent = request.getHeader("User-Agent");
        Instant issuedAt = Instant.now();
        Instant accessExpiresAt = issuedAt.plusSeconds(accessTokenExpireTime * 60L);
        AccessTokenInfo accessTokenInfo = AccessTokenInfo.builder()
                .tokenValue(accessTokenNew)
                .userAgent(userAgent)
                .userId(refreshTokenInfo.getUserId())
                .username(refreshTokenInfo.getUsername())
                .issuedAt(issuedAt)
                .expiresAt(accessExpiresAt)
                .deviceId(deviceId)
                .ipAddress(ip)
                .refreshTokenId(refreshTokenInfo.getTokenValue())
                .build();
        accessTokenService.storeAccessToken(accessTokenInfo);

        refreshTokenInfo.incrementUsage();
        refreshTokenInfo.incrementAccessTokenUsage(accessTokenNew);
        refreshTokenService.updateRefreshToken(refreshTokenInfo);
        setAccessTokenHeader(response,accessTokenNew);

        return accessTokenInfo;
    }
    public void revokeToken(HttpServletRequest request,HttpServletResponse response){
        String accessToken = request.getHeader(tokenHeader);
        String cookieRefreshToken = getCookieRefreshToken(request);
        AccessTokenInfo accessTokenInfo = accessTokenService.getAccessTokenInfo(accessToken);
        RefreshTokenInfo refreshTokenInfo = refreshTokenService.getRefreshTokenInfo(cookieRefreshToken);
        Instant now = Instant.now();
        if(accessTokenInfo!=null){
            accessTokenInfo.setRevokedAt(now);
            accessTokenInfo.setRevoked(true);
            accessTokenService.revokeAccessToken(accessTokenInfo);
        }
        if(refreshTokenInfo!=null){
            refreshTokenInfo.setRevokedAt(now);
            refreshTokenInfo.setRevoked(true);
            refreshTokenService.revokeRefreshToken(refreshTokenInfo);
        }
        clearRefreshTokenCookie(response);
    }

    public void clearRefreshTokenCookie(HttpServletResponse response) {
        Cookie cookie = new Cookie("idp_refresh_token", null);
        cookie.setHttpOnly(true);
        cookie.setSecure(true);
        cookie.setPath("/");
        cookie.setMaxAge(0);

        response.addCookie(cookie);
    }
    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
    }
}
