package cn.nianxx.thhotel.platform.hotel.repository;

import cn.nianxx.thhotel.platform.common.dto.PlatformPageSnapshot;
import cn.nianxx.thhotel.platform.hotel.common.enums.PlatformHotelStatus;
import cn.nianxx.thhotel.platform.hotel.domain.PlatformHotelEntity;
import cn.nianxx.thhotel.platform.hotel.domain.PlatformUserHotelEntity;
import cn.nianxx.thhotel.platform.hotel.mapper.PlatformHotelMapper;
import cn.nianxx.thhotel.platform.hotel.mapper.PlatformUserHotelMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;

/**
 * 基于 MyBatis-Plus 的平台酒店仓储实现。
 */
@Repository
public class MybatisPlatformHotelRepository implements PlatformHotelRepository {

    private final PlatformHotelMapper hotelMapper;
    private final PlatformUserHotelMapper userHotelMapper;

    public MybatisPlatformHotelRepository(
            PlatformHotelMapper hotelMapper,
            PlatformUserHotelMapper userHotelMapper) {
        this.hotelMapper = hotelMapper;
        this.userHotelMapper = userHotelMapper;
    }

    @Override
    public Optional<PlatformHotelEntity> findHotelByHotelId(String hotelId) {
        return Optional.ofNullable(hotelMapper.selectOne(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .eq(PlatformHotelEntity::getHotelId, hotelId)
                .last("LIMIT 1")));
    }

    @Override
    public Optional<PlatformHotelEntity> findHotelById(Long id) {
        if (id == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(hotelMapper.selectById(id));
    }

    @Override
    public List<PlatformHotelEntity> listActiveHotels() {
        return hotelMapper.selectList(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .eq(PlatformHotelEntity::getHotelStatus, PlatformHotelStatus.ACTIVE.name())
                .orderByAsc(PlatformHotelEntity::getSortOrder)
                .orderByAsc(PlatformHotelEntity::getHotelId));
    }

    @Override
    public List<PlatformHotelEntity> listAllHotels() {
        return hotelMapper.selectList(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .orderByAsc(PlatformHotelEntity::getSortOrder)
                .orderByAsc(PlatformHotelEntity::getHotelId));
    }

    @Override
    public PlatformPageSnapshot<PlatformHotelEntity> queryHotels(String keyword, String hotelStatus, int pageNum, int pageSize) {
        Page<PlatformHotelEntity> page = hotelMapper.selectPage(Page.of(pageNum, pageSize),
                Wrappers.<PlatformHotelEntity>lambdaQuery()
                        .eq(hotelStatus != null && !hotelStatus.isBlank(), PlatformHotelEntity::getHotelStatus, hotelStatus)
                        .and(keyword != null && !keyword.isBlank(), wrapper -> wrapper
                                .like(PlatformHotelEntity::getHotelId, keyword)
                                .or()
                                .like(PlatformHotelEntity::getHotelName, keyword))
                        .orderByAsc(PlatformHotelEntity::getSortOrder)
                        .orderByAsc(PlatformHotelEntity::getHotelId));
        return new PlatformPageSnapshot<>(page.getRecords(), page.getTotal(), pageNum, pageSize);
    }

    @Override
    public List<PlatformHotelEntity> listHotelsByHotelIds(List<String> hotelIds) {
        if (hotelIds == null || hotelIds.isEmpty()) {
            return Collections.emptyList();
        }
        return hotelMapper.selectList(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .in(PlatformHotelEntity::getHotelId, hotelIds));
    }

    @Override
    public List<String> listUserHotelIds(Long userId) {
        return userHotelMapper.selectList(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                        .eq(PlatformUserHotelEntity::getUserId, userId)
                        .orderByDesc(PlatformUserHotelEntity::getDefaultHotel)
                        .orderByAsc(PlatformUserHotelEntity::getHotelId))
                .stream()
                .map(PlatformUserHotelEntity::getHotelId)
                .toList();
    }

    @Override
    public List<PlatformUserHotelEntity> listUserHotelRelationsByUserIds(List<Long> userIds) {
        if (userIds == null || userIds.isEmpty()) {
            return Collections.emptyList();
        }
        return userHotelMapper.selectList(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                .in(PlatformUserHotelEntity::getUserId, userIds)
                .orderByDesc(PlatformUserHotelEntity::getDefaultHotel)
                .orderByAsc(PlatformUserHotelEntity::getHotelId));
    }

    @Override
    public long countAuthorizedUsersByHotelId(String hotelId) {
        if (hotelId == null || hotelId.isBlank()) {
            return 0L;
        }
        return userHotelMapper.selectCount(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                .eq(PlatformUserHotelEntity::getHotelId, hotelId));
    }

    @Override
    public long countActiveHotels() {
        return hotelMapper.selectCount(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .eq(PlatformHotelEntity::getHotelStatus, PlatformHotelStatus.ACTIVE.name()));
    }

    @Override
    public long countActiveHotelsExcluding(String hotelId) {
        return hotelMapper.selectCount(Wrappers.<PlatformHotelEntity>lambdaQuery()
                .eq(PlatformHotelEntity::getHotelStatus, PlatformHotelStatus.ACTIVE.name())
                .ne(hotelId != null && !hotelId.isBlank(), PlatformHotelEntity::getHotelId, hotelId));
    }

    @Override
    public Optional<String> findUserDefaultHotelId(Long userId) {
        return Optional.ofNullable(userHotelMapper.selectOne(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                        .eq(PlatformUserHotelEntity::getUserId, userId)
                        .eq(PlatformUserHotelEntity::getDefaultHotel, true)
                        .last("LIMIT 1")))
                .map(PlatformUserHotelEntity::getHotelId);
    }

    @Override
    public void insertHotel(PlatformHotelEntity hotel) {
        hotelMapper.insert(hotel);
    }

    @Override
    public void updateHotel(PlatformHotelEntity hotel) {
        hotelMapper.updateById(hotel);
    }

    @Override
    public void ensureUserHotel(Long userId, String hotelId, boolean defaultHotel) {
        if (defaultHotel) {
            clearOtherDefaultHotels(userId, hotelId);
        }
        PlatformUserHotelEntity existing = findUserHotel(userId, hotelId).orElse(null);
        if (existing != null) {
            if (defaultHotel && !Boolean.TRUE.equals(existing.getDefaultHotel())) {
                existing.setDefaultHotel(true);
                userHotelMapper.updateById(existing);
            }
            return;
        }
        PlatformUserHotelEntity relation = new PlatformUserHotelEntity();
        relation.setUserId(userId);
        relation.setHotelId(hotelId);
        relation.setDefaultHotel(defaultHotel);
        relation.setCreatedAt(nowUtc());
        try {
            userHotelMapper.insert(relation);
        } catch (DuplicateKeyException ignored) {
            // 多实例并发初始化时唯一索引已经保证授权关系存在，重复插入后只需补齐默认酒店状态。
            findUserHotel(userId, hotelId).ifPresent(concurrentExisting -> {
                if (defaultHotel && !Boolean.TRUE.equals(concurrentExisting.getDefaultHotel())) {
                    concurrentExisting.setDefaultHotel(true);
                    userHotelMapper.updateById(concurrentExisting);
                }
            });
        }
    }

    @Override
    public void replaceUserHotels(Long userId, List<String> hotelIds, String defaultHotelId) {
        userHotelMapper.delete(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                .eq(PlatformUserHotelEntity::getUserId, userId));
        if (hotelIds == null || hotelIds.isEmpty()) {
            return;
        }
        for (String hotelId : hotelIds) {
            ensureUserHotel(userId, hotelId, hotelId.equals(defaultHotelId));
        }
    }

    /**
     * 查询单个用户酒店授权关系，用于写入前检查和并发重复插入后的状态补齐。
     */
    private Optional<PlatformUserHotelEntity> findUserHotel(Long userId, String hotelId) {
        return Optional.ofNullable(userHotelMapper.selectOne(Wrappers.<PlatformUserHotelEntity>lambdaQuery()
                .eq(PlatformUserHotelEntity::getUserId, userId)
                .eq(PlatformUserHotelEntity::getHotelId, hotelId)
                .last("LIMIT 1")));
    }

    /**
     * 清理同一用户其他默认酒店标记，保证普通用户默认酒店在写入逻辑上保持唯一。
     */
    private void clearOtherDefaultHotels(Long userId, String hotelId) {
        PlatformUserHotelEntity update = new PlatformUserHotelEntity();
        update.setDefaultHotel(false);
        userHotelMapper.update(update, Wrappers.<PlatformUserHotelEntity>lambdaUpdate()
                .eq(PlatformUserHotelEntity::getUserId, userId)
                .ne(PlatformUserHotelEntity::getHotelId, hotelId)
                .eq(PlatformUserHotelEntity::getDefaultHotel, true));
    }

    private LocalDateTime nowUtc() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }
}
