package com.zioinfo.signage.uiws.schedule.service;

import com.zioinfo.signage.uiws.common.UiwsApiException;
import com.zioinfo.signage.uiws.common.UiwsCurrentUser;
import com.zioinfo.signage.uiws.common.UiwsErrorCode;
import com.zioinfo.signage.uiws.schedule.dto.ScheduleDtos.*;
import com.zioinfo.signage.uiws.schedule.mapper.ScheduleMapper;
import com.zioinfo.signage.uiws.schedule.model.UiwsDiary;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 일지(모듈 02) 서비스 — 원본 com.urp.uiws.schedule.service.DiaryService MyBatis 변환 이식.
 * 목록(페이징)·CRUD·첨부 연계.
 */
@Service
@RequiredArgsConstructor
public class DiaryService {

    private final ScheduleMapper mapper;
    private final AttachmentService attachmentService;

    @Transactional(readOnly = true)
    public DiaryPage list(LocalDate fromDate, LocalDate toDate, int page, int size) {
        LocalDate to = (toDate != null) ? toDate : LocalDate.now();
        LocalDate from = (fromDate != null) ? fromDate : to.minusMonths(1);
        long total = mapper.countDiary(from, to);
        List<UiwsDiary> rows = mapper.searchDiary(from, to, size, page * size);
        Map<String, String> names = userNames(rows.stream().map(UiwsDiary::getWriterId).toList());
        List<DiaryDto> content = rows.stream()
                .map(d -> new DiaryDto(d.getDiaryId(), d.getTitle(),
                        names.getOrDefault(d.getWriterId(), d.getWriterId()),
                        d.getDiaryDate() != null ? d.getDiaryDate().toString() : null,
                        d.getScheduleId()))
                .toList();
        return new DiaryPage(content, total, page, size, totalPages(total, size));
    }

    @Transactional
    public DiaryDetailDto create(DiarySaveDto dto) {
        String actor = UiwsCurrentUser.id();
        UiwsDiary d = new UiwsDiary();
        applySave(d, dto);
        d.setWriterId(actor);
        d.setCreatedBy(actor);
        d.setCreatedAt(LocalDateTime.now());
        mapper.insertDiary(d);
        attachmentService.link(AttachmentService.REF_DIARY, d.getDiaryId(), dto.attachmentIds());
        return toDetail(d);
    }

    @Transactional(readOnly = true)
    public DiaryDetailDto detail(Long id) {
        return toDetail(find(id));
    }

    @Transactional
    public DiaryDetailDto update(Long id, DiarySaveDto dto) {
        String actor = UiwsCurrentUser.id();
        UiwsDiary d = find(id);
        applySave(d, dto);
        d.setUpdatedBy(actor);
        d.setUpdatedAt(LocalDateTime.now());
        mapper.updateDiary(d);
        attachmentService.link(AttachmentService.REF_DIARY, d.getDiaryId(), dto.attachmentIds());
        return toDetail(d);
    }

    @Transactional
    public void delete(Long id) {
        if (!mapper.existsDiaryById(id)) {
            throw new UiwsApiException(UiwsErrorCode.DIARY_NOT_FOUND);
        }
        attachmentService.deleteByRef(AttachmentService.REF_DIARY, id);
        mapper.deleteDiaryById(id);
    }

    // ================================================================== helpers

    private void applySave(UiwsDiary d, DiarySaveDto dto) {
        d.setTitle(dto.title());
        d.setContent(dto.content());
        d.setScheduleId(dto.scheduleId());
        d.setDiaryDate(parseDateNullable(dto.diaryDate()));
    }

    private DiaryDetailDto toDetail(UiwsDiary d) {
        String writerNm = userNames(List.of(d.getWriterId())).getOrDefault(d.getWriterId(), d.getWriterId());
        List<AttachmentDto> attachments = attachmentService.list(AttachmentService.REF_DIARY, d.getDiaryId());
        return new DiaryDetailDto(d.getDiaryId(), d.getTitle(), writerNm,
                d.getDiaryDate() != null ? d.getDiaryDate().toString() : null,
                d.getScheduleId(), d.getContent(), attachments);
    }

    private UiwsDiary find(Long id) {
        UiwsDiary d = mapper.findDiaryById(id);
        if (d == null) {
            throw new UiwsApiException(UiwsErrorCode.DIARY_NOT_FOUND);
        }
        return d;
    }

    private Map<String, String> userNames(List<String> ids) {
        List<String> clean = ids.stream().filter(s -> s != null && !s.isBlank()).distinct().toList();
        if (clean.isEmpty()) {
            return Map.of();
        }
        return mapper.findUserNames(clean).stream()
                .collect(Collectors.toMap(r -> r.get("username"), r -> r.get("fullName"), (a, b) -> a));
    }

    private static int totalPages(long total, int size) {
        return size <= 0 ? 0 : (int) ((total + size - 1) / size);
    }

    private static LocalDate parseDateNullable(String s) {
        if (s == null || s.isBlank()) {
            return null;
        }
        try {
            return LocalDate.parse(s);
        } catch (Exception e) {
            throw new UiwsApiException(UiwsErrorCode.INVALID_REQUEST, "날짜 형식이 올바르지 않습니다(yyyy-MM-dd).");
        }
    }
}
