package com.gameplatform.server.service.account;

import com.gameplatform.server.mapper.account.UserAccountMapper;
import com.gameplatform.server.model.dto.account.AccountCreateRequest;
import com.gameplatform.server.model.dto.account.AccountResponse;
import com.gameplatform.server.model.dto.account.AccountUpdateRequest;
import com.gameplatform.server.model.dto.account.PointsBalanceResponse;
import com.gameplatform.server.model.dto.common.PageResult;
import com.gameplatform.server.model.entity.account.UserAccount;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class AccountService {
    private final UserAccountMapper mapper;
    private final PasswordEncoder passwordEncoder;

    public AccountService(UserAccountMapper mapper, PasswordEncoder passwordEncoder) {
        this.mapper = mapper;
        this.passwordEncoder = passwordEncoder;
    }

    public Mono<PageResult<AccountResponse>> list(String userType, String status, String keyword,
                                                  Integer page, Integer size) {
        int p = (page == null || page < 1) ? 1 : page;
        int s = (size == null || size < 1 || size > 200) ? 20 : size;
        int offset = (p - 1) * s;
        return Mono.fromCallable(() -> {
                    long total = mapper.countByFilter(userType, status, keyword);
                    List<UserAccount> list = mapper.listByFilter(userType, status, keyword, s, offset);
                    List<AccountResponse> items = list.stream().map(this::toResp).collect(Collectors.toList());
                    return new PageResult<>(items, total, p, s);
                })
                .subscribeOn(Schedulers.boundedElastic());
    }

    @Transactional
    public Mono<AccountResponse> create(AccountCreateRequest req) {
        return Mono.fromCallable(() -> {
                    // basic normalize
                    String type = req.getUserType().toUpperCase();
                    if (!type.equals("ADMIN") && !type.equals("AGENT")) {
                        throw new IllegalArgumentException("userType 只能是 ADMIN 或 AGENT");
                    }
                    if (mapper.findByUsername(req.getUsername()) != null) {
                        throw new IllegalArgumentException("用户名已存在");
                    }

                    UserAccount acc = new UserAccount();
                    acc.setUserType(type);
                    acc.setUsername(req.getUsername());
                    acc.setStatus(req.getStatus() == null ? "ENABLED" : req.getStatus());
                    if ("ADMIN".equals(type)) {
                        acc.setPointsBalance(0L);
                    } else {
                        acc.setPointsBalance(req.getPointsBalance() == null ? 0L : req.getPointsBalance());
                    }
                    acc.setPasswordHash(passwordEncoder.encode(req.getPassword()));
                    mapper.insert(acc);
                    return toResp(acc);
                })
                .subscribeOn(Schedulers.boundedElastic());
    }

    public Mono<AccountResponse> get(Long id) {
        return Mono.fromCallable(() -> mapper.selectById(id))
                .subscribeOn(Schedulers.boundedElastic())
                .map(this::toResp);
    }

    @Transactional
    public Mono<AccountResponse> update(Long id, AccountUpdateRequest req) {
        return Mono.fromCallable(() -> {
                    UserAccount db = mapper.selectById(id);
                    if (db == null) return null;
                    
                    // 验证用户名唯一性（如果要更新用户名）
                    if (req.getUsername() != null && !req.getUsername().equals(db.getUsername())) {
                        UserAccount existing = mapper.findByUsername(req.getUsername());
                        if (existing != null) {
                            throw new IllegalArgumentException("用户名已存在");
                        }
                    }
                    
                    UserAccount patch = new UserAccount();
                    patch.setId(id);

                    // 更新用户类型
                    if (req.getUserType() != null) {
                        String type = req.getUserType().toUpperCase();
                        if (!type.equals("ADMIN") && !type.equals("AGENT")) {
                            throw new IllegalArgumentException("userType 只能是 ADMIN 或 AGENT");
                        }
                        patch.setUserType(type);
                    }
                    
                    // 更新用户名
                    if (req.getUsername() != null) {
                        patch.setUsername(req.getUsername());
                    }
                    
                    // 更新状态
                    if (req.getStatus() != null) {
                        patch.setStatus(req.getStatus());
                    }
                    
                    // 更新积分余额（仅对AGENT类型有效）
                    if (req.getPointsBalance() != null) {
                        String userType = req.getUserType() != null ? req.getUserType() : db.getUserType();
                        if ("AGENT".equals(userType)) {
                            patch.setPointsBalance(req.getPointsBalance());
                        } else if ("ADMIN".equals(userType)) {
                            // 管理员账户积分余额固定为0
                            patch.setPointsBalance(0L);
                        }
                    }
                    
                    mapper.updateById(patch);
                    return mapper.selectById(id);
                })
                .subscribeOn(Schedulers.boundedElastic())
                .map(this::toResp);
    }

    @Transactional
    public Mono<Boolean> setStatus(Long id, String status) {
        return Mono.fromCallable(() -> mapper.setStatus(id, status) > 0)
                .subscribeOn(Schedulers.boundedElastic());
    }

    @Transactional
    public Mono<Void> resetPassword(Long id, String newPassword, boolean forceLogout) {
        return Mono.fromRunnable(() -> {
                    String hash = passwordEncoder.encode(newPassword);
                    mapper.updatePassword(id, hash);
                    // TODO: forceLogout 可结合 token 版本/黑名单实现（后续扩展）
                })
                .subscribeOn(Schedulers.boundedElastic())
                .then();
    }

    public Mono<PointsBalanceResponse> getCurrentUserPointsBalance(Long userId) {
        return Mono.fromCallable(() -> {
                    UserAccount account = mapper.selectById(userId);
                    if (account == null) {
                        throw new IllegalArgumentException("用户不存在");
                    }
                    
                    PointsBalanceResponse response = new PointsBalanceResponse();
                    response.setUserId(account.getId());
                    response.setUsername(account.getUsername());
                    response.setUserType(account.getUserType());
                    response.setPointsBalance(account.getPointsBalance());
                    
                    return response;
                })
                .subscribeOn(Schedulers.boundedElastic());
    }

    @Transactional
    public Mono<Boolean> delete(Long id, Long currentUserId) {
        return Mono.fromCallable(() -> {
                    // 检查用户是否存在
                    UserAccount user = mapper.selectById(id);
                    if (user == null) {
                        throw new IllegalArgumentException("用户不存在");
                    }
                    
                    // 不能删除自己
                    if (id.equals(currentUserId)) {
                        throw new IllegalArgumentException("不能删除当前登录的用户");
                    }
                    
                    // 如果要删除的是管理员，检查是否是最后一个管理员
                    if ("ADMIN".equals(user.getUserType())) {
                        long adminCount = mapper.countByFilter("ADMIN", "ENABLED", null);
                        if (adminCount <= 1) {
                            throw new IllegalArgumentException("不能删除最后一个管理员账户");
                        }
                    }
                    
                    // 执行删除
                    return mapper.deleteById(id) > 0;
                })
                .subscribeOn(Schedulers.boundedElastic());
    }

    private AccountResponse toResp(UserAccount a) {
        if (a == null) return null;
        AccountResponse r = new AccountResponse();
        r.setId(a.getId());
        r.setUserType(a.getUserType());
        r.setUsername(a.getUsername());

        r.setStatus(a.getStatus());
        r.setPointsBalance(a.getPointsBalance());
        r.setCreatedAt(a.getCreatedAt());
        r.setUpdatedAt(a.getUpdatedAt());
        return r;
    }
}

