package kr.co.accura.wtm.domain.timesheet.rule;

import kr.co.accura.wtm.domain.timesheet.TimesheetEntryType;
import kr.co.accura.wtm.domain.timesheet.entity.Timesheet;
import kr.co.accura.wtm.domain.timesheet.entity.TimesheetEntry;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class TimesheetRuleEngine {

    private static final BigDecimal MAX_DAILY_HOURS = new BigDecimal("8");
    private static final BigDecimal MAX_WEEKLY_HOURS = new BigDecimal("52");
    private static final BigDecimal WARN_DAILY_HOURS = new BigDecimal("10");
    private static final BigDecimal HARD_DAILY_LIMIT = new BigDecimal("24");

    /**
     * Validate timesheet entries against business rules.
     */
    public ValidationResult validate(Timesheet timesheet) {
        var result = new ValidationResult();

        // 1. Daily hour limits
        Map<LocalDate, BigDecimal> dailyTotals = timesheet.getEntries().stream()
                .collect(Collectors.groupingBy(
                        TimesheetEntry::getEntryDate,
                        Collectors.reducing(BigDecimal.ZERO, TimesheetEntry::getHours, BigDecimal::add)
                ));

        for (var entry : dailyTotals.entrySet()) {
            LocalDate date = entry.getKey();
            BigDecimal total = entry.getValue();

            if (total.compareTo(HARD_DAILY_LIMIT) > 0) {
                result.addError(date, "일 최대 24시간을 초과할 수 없습니다.");
            } else if (total.compareTo(WARN_DAILY_HOURS) > 0) {
                result.addWarning(date,
                        String.format("일 %s시간 입력 — 기준(%sh) 초과", total, MAX_DAILY_HOURS));
            }
        }

        // 2. Weekly total limit (52h)
        if (timesheet.getTotalHours().compareTo(MAX_WEEKLY_HOURS) > 0) {
            result.addError(null,
                    String.format("주간 합계 %s시간 — 최대 %sh 초과",
                            timesheet.getTotalHours(), MAX_WEEKLY_HOURS));
        }

        // 3. EPC entries require project and WBS
        timesheet.getEntries().stream()
                .filter(e -> e.getEntryType() == TimesheetEntryType.EPC)
                .forEach(e -> {
                    if (e.getEpcProjectId() == null) {
                        result.addError(e.getEntryDate(), "EPC 시수 — 프로젝트 필수");
                    }
                    if (e.getCanonicalWbsId() == null) {
                        result.addError(e.getEntryDate(), "EPC 시수 — WBS 선택 필수");
                    }
                });

        // 4. No future dates
        LocalDate today = LocalDate.now();
        timesheet.getEntries().stream()
                .filter(e -> e.getEntryDate().isAfter(today))
                .forEach(e -> result.addError(e.getEntryDate(), "미래 날짜에 시수를 입력할 수 없습니다."));

        return result;
    }
}
