package com.ttstd.signaling.service;

import com.ttstd.signaling.model.AccountStatus;
import com.ttstd.signaling.model.AdminAccount;
import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.security.JwtService;
import com.ttstd.signaling.security.SecurityProperties;
import com.ttstd.signaling.security.TokenUtils;
import com.ttstd.signaling.store.AdminStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 后台管理员服务：与 {@link AccountService}（主控端用户）完全独立。
 *
 * <ul>
 *   <li>使用单独的 {@link AdminStore} 与数据库表 admin_account；</li>
 *   <li>登录签发独立用途（admin）的 JWT，前缀为 {@code admt_}；</li>
 *   <li>不共享用户的注册/绑定/设备逻辑。</li>
 * </ul>
 */
@Service
public class AdminService {

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

    /** 管理员令牌用途标识，与用户 access/refresh/device 令牌隔离。 */
    public static final String TOKEN_PURPOSE = "admin";
    /** 令牌前缀，便于前端区分与排查。 */
    public static final String TOKEN_PREFIX = "admt_";

    private static final Pattern ADMIN_USERNAME_PATTERN =
            Pattern.compile("^[A-Za-z0-9_.\\-]{3,32}$");

    private final AdminStore adminStore;
    private final PasswordEncoder passwordEncoder;
    private final JwtService jwtService;
    private final SecurityProperties properties;

    @Autowired
    public AdminService(AdminStore adminStore,
                        PasswordEncoder passwordEncoder,
                        JwtService jwtService,
                        SecurityProperties properties) {
        this.adminStore = adminStore;
        this.passwordEncoder = passwordEncoder;
        this.jwtService = jwtService;
        this.properties = properties;
    }

    /**
     * 启动引导：若管理员表为空，则依据 application.yml 的
     * security.admin.bootstrap-username / bootstrap-password 创建初始管理员。
     * 若未配置，则记录告警日志（不会抛异常，避免阻断启动）。
     */
    @jakarta.annotation.PostConstruct
    void bootstrap() {
        String username = properties.getAdmin() != null
                ? properties.getAdmin().getBootstrapUsername() : null;
        String password = properties.getAdmin() != null
                ? properties.getAdmin().getBootstrapPassword() : null;
        if (username == null || username.isBlank() || password == null || password.isBlank()) {
            if (adminStore.count() == 0) {
                logger.warn("后台管理员表为空，且未配置 security.admin.bootstrap-username / "
                        + "bootstrap-password。请通过管理接口或环境变量创建首个管理员，否则无法登录后台。");
            }
            return;
        }
        AdminAccount existing = adminStore.findByUsername(username).orElse(null);
        if (existing != null) {
            // 已存在同名管理员：保证其密码与配置的引导密码一致，
            // 避免早期手动创建的账号与配置不一致导致网页端无法登录。
            if (!passwordEncoder.matches(password, existing.getPasswordHash())) {
                existing.setPasswordHash(passwordEncoder.encode(password));
                existing.setUpdatedAt(java.time.Instant.now());
                adminStore.save(existing);
                logger.info("已将引导管理员 {} 的密码重置为 application.yml 配置值", username);
            }
            return;
        }
        if (adminStore.count() > 0) {
            return;
        }
        createAdminInternal(username, password, username);
        logger.info("已根据 application.yml 创建初始后台管理员: {}", username);
    }

    // ==================== 登录 ====================

    public Map<String, Object> login(String username, String password) {
        if (username == null || password == null) {
            throw AuthException.badRequest("用户名或密码缺失");
        }
        AdminAccount admin = adminStore.findByUsername(username)
                .orElseThrow(() -> AuthException.unauthorized("用户名或密码错误"));
        if (admin.isDisabled()) {
            throw AuthException.forbidden("该管理员已被禁用", "admin disabled");
        }
        if (!passwordEncoder.matches(password, admin.getPasswordHash())) {
            throw AuthException.unauthorized("用户名或密码错误");
        }
        admin.setLastLoginAt(Instant.now());
        adminStore.save(admin);
        return issueToken(admin);
    }

    /** 校验管理员令牌，返回声明。令牌需以 {@code admt_} 前缀。 */
    public Map<String, Object> verifyToken(String rawToken) {
        String token = stripPrefix(rawToken);
        Map<String, Object> claims = jwtService.verify(token, TOKEN_PURPOSE);
        String adminId = JwtService.claimAsString(claims, "sub");
        AdminAccount admin = adminStore.findById(adminId)
                .orElseThrow(() -> AuthException.unauthorized("管理员不存在或已被删除"));
        if (admin.isDisabled()) {
            throw AuthException.forbidden("该管理员已被禁用", "admin disabled");
        }
        return claims;
    }

    public String currentAdminId(String rawToken) {
        Map<String, Object> claims = verifyToken(rawToken);
        return JwtService.claimAsString(claims, "sub");
    }

    private Map<String, Object> issueToken(AdminAccount admin) {
        long ttl = properties.getAdmin() != null
                ? properties.getAdmin().getTokenTtlSeconds() : 3600L;
        if (ttl <= 0) {
            ttl = 3600L;
        }
        String jwt = jwtService.issue(admin.getAdminId(), TOKEN_PURPOSE, ttl, 0, null);
        Map<String, Object> result = new java.util.HashMap<>();
        result.put("token", TOKEN_PREFIX + jwt);
        result.put("tokenType", "Bearer");
        result.put("expiresIn", ttl);
        result.put("adminId", admin.getAdminId());
        result.put("username", admin.getUsername());
        result.put("displayName", admin.getDisplayName());
        return result;
    }

    // ==================== 管理员管理 ====================

    public List<AdminAccount> listAdmins() {
        return adminStore.findAll();
    }

    public AdminAccount createAdmin(String username, String password, String displayName) {
        validateUsername(username);
        validatePassword(password);
        if (adminStore.existsByUsername(username)) {
            throw AuthException.badRequest("管理员用户名已被占用");
        }
        return createAdminInternal(username, password, displayName);
    }

    private AdminAccount createAdminInternal(String username, String password, String displayName) {
        String adminId = "adm_" + TokenUtils.randomBase62(20);
        AdminAccount admin = new AdminAccount(adminId, username,
                passwordEncoder.encode(password));
        admin.setDisplayName(displayName == null || displayName.isBlank() ? username : displayName);
        admin.setStatus(AccountStatus.ACTIVE);
        admin.setCreatedAt(Instant.now());
        adminStore.save(admin);
        logger.info("后台管理员已创建: {} ({})", username, adminId);
        return admin;
    }

    public void disableAdmin(String adminId) {
        guardNotLastAdmin(adminId);
        AdminAccount admin = requireAdmin(adminId);
        admin.setStatus(AccountStatus.DISABLED);
        admin.setUpdatedAt(Instant.now());
        adminStore.save(admin);
        logger.info("后台管理员已禁用: {}", adminId);
    }

    public void enableAdmin(String adminId) {
        AdminAccount admin = requireAdmin(adminId);
        admin.setStatus(AccountStatus.ACTIVE);
        admin.setUpdatedAt(Instant.now());
        adminStore.save(admin);
        logger.info("后台管理员已启用: {}", adminId);
    }

    public void deleteAdmin(String adminId) {
        guardNotLastAdmin(adminId);
        requireAdmin(adminId);
        adminStore.delete(adminId);
        logger.info("后台管理员已删除: {}", adminId);
    }

    public void resetPassword(String adminId, String newPassword) {
        validatePassword(newPassword);
        AdminAccount admin = requireAdmin(adminId);
        admin.setPasswordHash(passwordEncoder.encode(newPassword));
        admin.setUpdatedAt(Instant.now());
        adminStore.save(admin);
        logger.info("后台管理员密码已重置: {}", adminId);
    }

    private AdminAccount requireAdmin(String adminId) {
        return adminStore.findById(adminId)
                .orElseThrow(() -> AuthException.badRequest("管理员不存在"));
    }

    private void guardNotLastAdmin(String adminId) {
        long active = adminStore.findAll().stream()
                .filter(a -> !a.isDisabled())
                .count();
        AdminAccount target = adminStore.findById(adminId).orElse(null);
        if (target != null && !target.isDisabled() && active <= 1) {
            throw AuthException.forbidden("至少需保留一名启用状态的管理员", "last admin");
        }
    }

    private void validateUsername(String username) {
        if (username == null || !ADMIN_USERNAME_PATTERN.matcher(username).matches()) {
            throw AuthException.badRequest("管理员用户名需为 3-32 位字母、数字、下划线、点或连字符");
        }
    }

    private void validatePassword(String password) {
        if (password == null || password.length() < 8 || password.length() > 128) {
            throw AuthException.badRequest("密码长度需为 8-128 位");
        }
        int classes = 0;
        if (password.matches(".*[a-z].*")) classes++;
        if (password.matches(".*[A-Z].*")) classes++;
        if (password.matches(".*\\d.*")) classes++;
        if (password.matches(".*[^a-zA-Z0-9].*")) classes++;
        if (classes < 2) {
            throw AuthException.badRequest("密码需至少包含大小写字母、数字、符号中的两类");
        }
    }

    private static String stripPrefix(String rawToken) {
        if (rawToken == null) {
            return null;
        }
        if (rawToken.startsWith(TOKEN_PREFIX)) {
            return rawToken.substring(TOKEN_PREFIX.length());
        }
        return rawToken;
    }
}
