package com.ttstd.signaling.controller;

import com.ttstd.signaling.controller.dto.AuthDtos;
import com.ttstd.signaling.model.AuthPrincipal;
import com.ttstd.signaling.model.UserAccount;
import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.security.RateLimiter;
import com.ttstd.signaling.service.AccountService;
import com.ttstd.signaling.service.TokenPair;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 主控端账号认证接口。
 */
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

    /** 登录限流：单 IP 5 分钟内最多 10 次 */
    private static final int LOGIN_LIMIT = 10;
    private static final long LOGIN_WINDOW_SECONDS = 300;

    private final AccountService accountService;
    private final RateLimiter rateLimiter;

    public AuthController(AccountService accountService, RateLimiter rateLimiter) {
        this.accountService = accountService;
        this.rateLimiter = rateLimiter;
    }

    @PostMapping("/register")
    public ResponseEntity<Map<String, Object>> register(@Valid @RequestBody AuthDtos.RegisterRequest request,
                                                        HttpServletRequest httpRequest) {
        String ip = clientIp(httpRequest);
        if (!rateLimiter.tryAcquire("register:" + ip, 5, 3600)) {
            throw AuthException.tooManyRequests("注册过于频繁，请稍后再试");
        }
        UserAccount account = accountService.register(request.username(), request.password());
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("userId", account.getUserId());
        body.put("username", account.getUsername());
        return ResponseEntity.ok(body);
    }

    @PostMapping("/login")
    public ResponseEntity<AuthDtos.TokenResponse> login(@Valid @RequestBody AuthDtos.LoginRequest request,
                                                        HttpServletRequest httpRequest) {
        String ip = clientIp(httpRequest);
        if (!rateLimiter.tryAcquire("login:" + ip, LOGIN_LIMIT, LOGIN_WINDOW_SECONDS)) {
            throw AuthException.tooManyRequests("登录尝试过于频繁，请稍后再试");
        }
        TokenPair pair = accountService.login(
                request.username(), request.password(), request.totpCode(),
                ip, userAgent(httpRequest));
        rateLimiter.reset("login:" + ip);
        return ResponseEntity.ok(toResponse(pair));
    }

    @PostMapping("/refresh")
    public ResponseEntity<AuthDtos.TokenResponse> refresh(@Valid @RequestBody AuthDtos.RefreshRequest request,
                                                          HttpServletRequest httpRequest) {
        TokenPair pair = accountService.refresh(
                request.refreshToken(), clientIp(httpRequest), userAgent(httpRequest));
        return ResponseEntity.ok(toResponse(pair));
    }

    @PostMapping("/logout")
    public ResponseEntity<Map<String, Object>> logout(HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        accountService.logout(principal.sessionId());
        return ResponseEntity.ok(Map.of("success", true));
    }

    @PostMapping("/logout-all")
    public ResponseEntity<Map<String, Object>> logoutAll(HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        accountService.revokeAllSessions(principal.principalId(), "用户主动退出全部设备");
        return ResponseEntity.ok(Map.of("success", true));
    }

    @PostMapping("/change-password")
    public ResponseEntity<Map<String, Object>> changePassword(
            @Valid @RequestBody AuthDtos.ChangePasswordRequest request,
            HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        accountService.changePassword(principal.principalId(),
                request.oldPassword(), request.newPassword());
        return ResponseEntity.ok(Map.of("success", true, "message", "密码已修改，请重新登录"));
    }

    // ==================== TOTP 双因子 ====================

    /**
     * 生成 TOTP 密钥。返回 otpauth URI 供客户端渲染二维码；
     * 此时尚未生效，需再调用 /totp/enable 完成绑定。
     */
    @PostMapping("/totp/setup")
    public ResponseEntity<Map<String, Object>> totpSetup(HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        Map<String, String> result = accountService.setupTotp(principal.principalId());
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("secret", result.get("secret"));
        body.put("otpauthUri", result.get("otpauthUri"));
        body.put("notice", "请用认证器扫码后调用 /api/auth/totp/enable 提交动态码完成绑定");
        return ResponseEntity.ok(body);
    }

    /** 提交一次动态码，正式启用双因子。 */
    @PostMapping("/totp/enable")
    public ResponseEntity<Map<String, Object>> totpEnable(
            @Valid @RequestBody AuthDtos.TotpEnableRequest request,
            HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        accountService.enableTotp(principal.principalId(), request.code());
        return ResponseEntity.ok(Map.of("success", true, "message", "双因子认证已启用"));
    }

    /** 关闭双因子，需同时校验密码与动态码。 */
    @PostMapping("/totp/disable")
    public ResponseEntity<Map<String, Object>> totpDisable(
            @Valid @RequestBody AuthDtos.TotpDisableRequest request,
            HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        accountService.disableTotp(principal.principalId(), request.password(), request.code());
        return ResponseEntity.ok(Map.of("success", true, "message", "双因子认证已关闭"));
    }

    /** 查询当前登录身份。 */
    @GetMapping("/me")
    public ResponseEntity<Map<String, Object>> me(HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("principalId", principal.principalId());
        body.put("principalType", principal.principalType().name());
        body.put("displayName", principal.displayName());
        body.put("sessionId", principal.sessionId());
        body.put("signalDeviceId", principal.deviceId());
        // 便于客户端展示"是否已开启双因子"，仅对用户身份有效
        if (principal.isUser()) {
            body.put("totpEnabled",
                    accountService.requireUser(principal.principalId()).isTotpEnabled());
        }
        return ResponseEntity.ok(body);
    }

    /** 查询当前账号的活跃会话列表。 */
    @GetMapping("/sessions")
    public ResponseEntity<List<Map<String, Object>>> sessions(HttpServletRequest httpRequest) {
        AuthPrincipal principal = requirePrincipal(httpRequest);
        List<Map<String, Object>> body = accountService.listSessions(principal.principalId())
                .stream()
                .map(s -> {
                    Map<String, Object> item = new LinkedHashMap<>();
                    item.put("sessionId", s.getSessionId());
                    item.put("ip", s.getIp());
                    item.put("userAgent", s.getUserAgent());
                    item.put("createdAt", s.getCreatedAt().toEpochMilli());
                    item.put("lastSeenAt", s.getLastSeenAt().toEpochMilli());
                    item.put("current", s.getSessionId().equals(principal.sessionId()));
                    return item;
                })
                .toList();
        return ResponseEntity.ok(body);
    }

    private AuthDtos.TokenResponse toResponse(TokenPair pair) {
        return new AuthDtos.TokenResponse(
                pair.accessToken(), pair.refreshToken(), pair.expiresInSeconds(),
                pair.sessionId(), pair.principalId(), pair.displayName());
    }

    private AuthPrincipal requirePrincipal(HttpServletRequest request) {
        Object principal = request.getAttribute(
                com.ttstd.signaling.config.BearerAuthFilter.ATTR_PRINCIPAL);
        if (principal instanceof AuthPrincipal auth) {
            return auth;
        }
        throw AuthException.unauthorized("缺少有效的访问令牌");
    }

    static String clientIp(HttpServletRequest request) {
        String forwarded = request.getHeader("X-Forwarded-For");
        if (forwarded != null && !forwarded.isBlank()) {
            int comma = forwarded.indexOf(',');
            return (comma > 0 ? forwarded.substring(0, comma) : forwarded).trim();
        }
        return request.getRemoteAddr();
    }

    static String userAgent(HttpServletRequest request) {
        String ua = request.getHeader("User-Agent");
        if (ua == null) {
            return "unknown";
        }
        return ua.length() > 256 ? ua.substring(0, 256) : ua;
    }
}
