package dev.awesomedev.mirim.service;

import dev.awesomedev.mirim.domain.ChecklistItem;
import dev.awesomedev.mirim.domain.Progress;
import dev.awesomedev.mirim.domain.User;
import dev.awesomedev.mirim.repository.ChecklistItemRepository;
import dev.awesomedev.mirim.repository.ProgressRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

/**
 * 이 파일이 하는 일:
 * 체크리스트 진행 상황(Progress)을 다루는 서비스다.
 * "체크 토글"과 "내가 체크한 항목 id 목록 조회" 두 가지 일을 한다.
 */
@Service
public class ProgressService {

    private final ProgressRepository progressRepository;
    private final ChecklistItemRepository checklistItemRepository;

    public ProgressService(ProgressRepository progressRepository,
                           ChecklistItemRepository checklistItemRepository) {
        this.progressRepository = progressRepository;
        this.checklistItemRepository = checklistItemRepository;
    }

    /**
     * 체크 상태를 토글한다.
     * 이미 체크되어 있으면 → 체크 기록을 삭제하고 false(체크 해제됨) 반환.
     * 체크 안 되어 있으면 → 체크 기록을 새로 만들고 true(체크됨) 반환.
     *
     * 학습 포인트: 조회와 삭제/저장이 한 트랜잭션 안에서 일어나야
     * 중간에 끊겨 어중간한 상태가 남는 일이 없다. @Transactional이 그 경계를 만든다.
     */
    @Transactional
    public boolean toggle(User user, Long itemId) {
        ChecklistItem item = checklistItemRepository.findById(itemId)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND, "체크리스트 항목을 찾을 수 없습니다: " + itemId));

        Optional<Progress> existing = progressRepository.findByUserAndItem(user, item);
        if (existing.isPresent()) {
            progressRepository.delete(existing.get());
            return false; // 체크 해제됨
        }
        progressRepository.save(new Progress(user, item));
        return true; // 체크됨
    }

    /**
     * 내가 체크한 체크리스트 항목의 id 목록을 반환한다.
     *
     * 학습 포인트: readOnly = true — "이 트랜잭션은 읽기만 한다"고 선언하면
     * JPA가 변경 감지(더티 체킹) 준비를 생략해 조회 성능이 좋아지고,
     * 실수로 이 안에서 데이터를 바꾸는 코드를 막는 안전장치도 된다.
     * 또한 Progress.user/item이 LAZY라 getItem()을 트랜잭션 안에서 호출해야
     * 하는데, 이 @Transactional이 그 경계를 만들어 준다.
     */
    @Transactional(readOnly = true)
    public List<Long> checkedItemIds(User user) {
        return progressRepository.findByUser(user).stream()
                .map(progress -> progress.getItem().getId())
                .toList();
    }

    /**
     * 이 사용자가 주차별로 몇 개를 체크했는지 집계한다. (멘토 뷰용)
     * 반환 예: {1: 4, 2: 6} — 1주차 4개, 2주차 6개 완료. 체크가 없는 주차는 키 자체가 없다.
     *
     * 학습 포인트: SQL의 GROUP BY로도 할 수 있지만(SQL 중급 코스 참고),
     * 데이터가 수십 건 규모라 자바에서 Map으로 세는 쪽이 읽기 쉽다.
     * TreeMap을 쓰면 키(주차)가 자동으로 정렬된다.
     */
    @Transactional(readOnly = true)
    public Map<Integer, Long> doneCountByWeek(User user) {
        Map<Integer, Long> counts = new TreeMap<>();
        for (Progress progress : progressRepository.findByUser(user)) {
            Integer week = progress.getItem().getWeek();
            counts.merge(week, 1L, Long::sum);
        }
        return counts;
    }

    /** 주차별 체크리스트 항목 총수. (멘토 뷰에서 분모로 쓴다) */
    @Transactional(readOnly = true)
    public Map<Integer, Long> totalCountByWeek() {
        Map<Integer, Long> counts = new TreeMap<>();
        for (ChecklistItem item : checklistItemRepository.findAll()) {
            counts.merge(item.getWeek(), 1L, Long::sum);
        }
        return counts;
    }
}
