package kr.co.accura.wtm.domain.approval.handler;

import kr.co.accura.wbx.spring.approval.*;
import kr.co.accura.wbx.spring.common.NotFoundException;
import kr.co.accura.wtm.domain.approval.entity.TtApproval;
import kr.co.accura.wtm.domain.approval.entity.TtApprovalLine;
import kr.co.accura.wtm.domain.approval.repository.TtApprovalLineRepository;
import kr.co.accura.wtm.domain.approval.repository.TtApprovalRepository;
import kr.co.accura.wtm.domain.timesheet.TimesheetStatus;
import kr.co.accura.wtm.domain.timesheet.entity.Timesheet;
import kr.co.accura.wtm.domain.timesheet.repository.TimesheetRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Component
@RequiredArgsConstructor
public class TimesheetApprovalHandler implements ApprovalHandler {

    private final TtApprovalRepository approvalRepository;
    private final TtApprovalLineRepository lineRepository;
    private final TimesheetRepository timesheetRepository;
    private final ApplicationEventPublisher eventPublisher;

    @Override
    public String getTypeKey() {
        return "timesheet";
    }

    @Override
    public String getTypeDisplay() {
        return "시수 결재";
    }

    @Override
    @Transactional
    public ApprovalResult approve(Long lineId, Long approverId, String comment) {
        TtApprovalLine line = lineRepository.findById(lineId)
                .orElseThrow(() -> new NotFoundException("결재 라인을 찾을 수 없습니다."));
        line.approve();
        lineRepository.save(line);

        TtApproval approval = line.getApproval();
        Timesheet ts = timesheetRepository.findById(approval.getTimesheetId())
                .orElseThrow(() -> new NotFoundException("Timesheet not found"));

        Optional<TtApprovalLine> next = lineRepository.findNextPending(
                approval.getId(), line.getApprovalOrder());

        if (next.isPresent()) {
            ts.setStatus(TimesheetStatus.DL_APPROVED);
        } else {
            ts.setStatus(TimesheetStatus.APPROVED);
            approval.complete();
            approvalRepository.save(approval);
            eventPublisher.publishEvent(new ApprovalCompletedEvent(
                    "timesheet", ts.getId(), approverId, approval));
        }
        timesheetRepository.save(ts);

        return ApprovalResult.success("승인 완료");
    }

    @Override
    @Transactional
    public ApprovalResult reject(Long lineId, Long approverId, String comment) {
        TtApprovalLine line = lineRepository.findById(lineId)
                .orElseThrow(() -> new NotFoundException("결재 라인을 찾을 수 없습니다."));
        line.reject();
        lineRepository.save(line);

        TtApproval approval = line.getApproval();
        approval.reject();
        approvalRepository.save(approval);

        Timesheet ts = timesheetRepository.findById(approval.getTimesheetId())
                .orElseThrow(() -> new NotFoundException("Timesheet not found"));
        ts.setStatus(TimesheetStatus.REJECTED);
        timesheetRepository.save(ts);

        return ApprovalResult.success("반려 완료");
    }

    @Override
    @Transactional(readOnly = true)
    public ApprovalHistoryDto getApprovalHistory(Long timesheetId) {
        TtApproval approval = approvalRepository.findByTimesheetId(timesheetId)
                .orElseThrow(() -> new NotFoundException("결재 정보를 찾을 수 없습니다."));

        List<ApprovalLineDto> lineDtos = approval.getLines().stream()
                .map(l -> new ApprovalLineDto(
                        l.getId(), l.getApproverId(), null,
                        l.getApprovalOrder(), l.getRoleCode(), l.getStatus(),
                        null, l.getActedAt()))
                .toList();

        return new ApprovalHistoryDto(
                timesheetId, "timesheet", approval.getStatus(),
                null, approval.getSubmittedAt(), lineDtos);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<ApprovalPendingDto> getPending(Long approverId, Pageable pageable) {
        return lineRepository.findPendingByApproverId(approverId, pageable)
                .map(line -> new ApprovalPendingDto(
                        line.getId(), "timesheet",
                        "시수 결재 #" + line.getApproval().getTimesheetId(),
                        null, line.getApproval().getSubmittedAt()));
    }
}
