package com.honey.honey.service;

import com.honey.honey.dto.*;
import com.honey.honey.model.Promotion;
import com.honey.honey.model.Promotion.PromotionStatus;
import com.honey.honey.model.Promotion.PromotionType;
import com.honey.honey.model.PromotionReward;
import com.honey.honey.model.PromotionUser;
import com.honey.honey.repository.PromotionRepository;
import com.honey.honey.repository.PromotionRewardRepository;
import com.honey.honey.repository.PromotionUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AdminPromotionService {

    private final PromotionRepository promotionRepository;
    private final PromotionRewardRepository promotionRewardRepository;
    private final PromotionUserRepository promotionUserRepository;

    public List<AdminPromotionDto> listPromotions() {
        return promotionRepository.findAllByOrderByStartTimeDesc().stream()
                .map(this::toDto)
                .collect(Collectors.toList());
    }

    public Optional<AdminPromotionDto> getPromotion(int id) {
        return promotionRepository.findById(id).map(this::toDto);
    }

    @Transactional
    public AdminPromotionDto createPromotion(AdminPromotionRequest req) {
        Promotion p = Promotion.builder()
                .type(PromotionType.valueOf(req.getType()))
                .startTime(req.getStartTime())
                .endTime(req.getEndTime())
                .status(PromotionStatus.valueOf(req.getStatus()))
                .totalReward(req.getTotalReward())
                .createdAt(java.time.Instant.now())
                .updatedAt(java.time.Instant.now())
                .build();
        p = promotionRepository.save(p);
        return toDto(p);
    }

    @Transactional
    public Optional<AdminPromotionDto> updatePromotion(int id, AdminPromotionRequest req) {
        return promotionRepository.findById(id).map(p -> {
            p.setType(PromotionType.valueOf(req.getType()));
            p.setStartTime(req.getStartTime());
            p.setEndTime(req.getEndTime());
            p.setStatus(PromotionStatus.valueOf(req.getStatus()));
            p.setTotalReward(req.getTotalReward());
            return toDto(promotionRepository.save(p));
        });
    }

    @Transactional
    public boolean deletePromotion(int id) {
        if (!promotionRepository.existsById(id)) return false;
        promotionRepository.deleteById(id);
        return true;
    }

    public List<AdminPromotionRewardDto> listRewards(int promoId) {
        return promotionRewardRepository.findByPromotionIdOrderByPlaceAsc(promoId).stream()
                .map(this::toRewardDto)
                .collect(Collectors.toList());
    }

    @Transactional
    public AdminPromotionRewardDto createReward(int promoId, AdminPromotionRewardRequest req) {
        Promotion promo = promotionRepository.findById(promoId)
                .orElseThrow(() -> new IllegalArgumentException("Promotion not found: " + promoId));
        PromotionReward r = PromotionReward.builder()
                .promotion(promo)
                .place(req.getPlace())
                .reward(req.getReward())
                .build();
        r = promotionRewardRepository.save(r);
        return toRewardDto(r);
    }

    @Transactional
    public Optional<AdminPromotionRewardDto> updateReward(int rewardId, AdminPromotionRewardRequest req) {
        return promotionRewardRepository.findById(rewardId).map(r -> {
            r.setPlace(req.getPlace());
            r.setReward(req.getReward());
            return toRewardDto(promotionRewardRepository.save(r));
        });
    }

    @Transactional
    public boolean deleteReward(int rewardId) {
        if (!promotionRewardRepository.existsById(rewardId)) return false;
        promotionRewardRepository.deleteById(rewardId);
        return true;
    }

    public Page<AdminPromotionUserDto> getPromotionUsers(int promoId, int page, int size,
                                                        String sortBy, String sortDir,
                                                        Integer searchUserId) {
        Sort.Direction direction = "asc".equalsIgnoreCase(sortDir) ? Sort.Direction.ASC : Sort.Direction.DESC;
        String property = "points".equalsIgnoreCase(sortBy) ? "points" : "userId";
        Sort sort = Sort.by(direction, property);
        Pageable pageable = PageRequest.of(page, size, sort);

        Page<PromotionUser> pageResult;
        if (searchUserId != null) {
            pageResult = promotionUserRepository.findByPromoIdAndUserId(promoId, searchUserId, pageable);
        } else {
            pageResult = promotionUserRepository.findByPromoId(promoId, pageable);
        }

        return pageResult.map(this::toUserDto);
    }

    @Transactional
    public Optional<AdminPromotionUserDto> updatePromotionUserPoints(int promoId, int userId, BigDecimal points) {
        Optional<PromotionUser> opt = promotionUserRepository.findByPromoIdAndUserId(promoId, userId);
        if (opt.isEmpty()) {
            PromotionUser pu = PromotionUser.builder()
                    .promoId(promoId)
                    .userId(userId)
                    .points(points != null ? points : BigDecimal.ZERO)
                    .updatedAt(java.time.Instant.now())
                    .build();
            pu = promotionUserRepository.save(pu);
            return Optional.of(toUserDto(pu));
        }
        PromotionUser pu = opt.get();
        pu.setPoints(points != null ? points : BigDecimal.ZERO);
        return Optional.of(toUserDto(promotionUserRepository.save(pu)));
    }

    private AdminPromotionDto toDto(Promotion p) {
        return AdminPromotionDto.builder()
                .id(p.getId())
                .type(p.getType().name())
                .startTime(p.getStartTime())
                .endTime(p.getEndTime())
                .status(p.getStatus().name())
                .totalReward(p.getTotalReward())
                .createdAt(p.getCreatedAt())
                .updatedAt(p.getUpdatedAt())
                .build();
    }

    private AdminPromotionRewardDto toRewardDto(PromotionReward r) {
        return AdminPromotionRewardDto.builder()
                .id(r.getId())
                .promoId(r.getPromotion().getId())
                .place(r.getPlace())
                .reward(r.getReward())
                .createdAt(r.getCreatedAt())
                .updatedAt(r.getUpdatedAt())
                .build();
    }

    private AdminPromotionUserDto toUserDto(PromotionUser pu) {
        return AdminPromotionUserDto.builder()
                .promoId(pu.getPromoId())
                .userId(pu.getUserId())
                .points(pu.getPoints())
                .updatedAt(pu.getUpdatedAt())
                .build();
    }
}
