package com.zioinfo.kintex.work.worklog;

import com.zioinfo.kintex.auth.KintexPrincipal;
import com.zioinfo.kintex.common.PageResponse;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.system.SystemAccessGuard;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/** 업무일지 서비스. DataScope: 일반 사용자는 본인 것만, 매니저/관리자/홀매니저는 전체 열람. 편집은 작성자 본인만. */
@Service
public class WorklogService {

    private final WorklogMapper mapper;
    private final SystemAccessGuard scope;

    public WorklogService(WorklogMapper mapper, SystemAccessGuard scope) {
        this.mapper = mapper;
        this.scope = scope;
    }

    public PageResponse<WorklogDto> list(KintexPrincipal principal, String eventId, String fromDate,
                                         String toDate, String status, int page, int size) {
        int p = Math.max(page, 0);
        int s = size <= 0 ? 20 : Math.min(size, 200);
        Map<String, Object> q = new HashMap<>();
        q.put("mineOnly", !scope.canSeeAll(principal));
        q.put("userId", principal.userId());
        q.put("eventId", eventId);
        q.put("fromDate", fromDate);
        q.put("toDate", toDate);
        q.put("status", status);
        q.put("size", s);
        q.put("offset", p * s);
        List<Map<String, Object>> rows = mapper.search(q);
        long total = mapper.count(q);
        return PageResponse.of(rows.stream().map(WorklogService::toDto).toList(), p, s, total);
    }

    public WorklogDto get(KintexPrincipal principal, String id) {
        Map<String, Object> r = mapper.findById(id);
        if (r == null) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
        if (!scope.canSeeAll(principal) && !principal.userId().equals(str(r.get("writerId")))) {
            throw new ApiException(ErrorCode.FORBIDDEN);
        }
        return toDto(r);
    }

    @Transactional
    public WorklogDto create(KintexPrincipal principal, WorklogSaveRequest req) {
        String id = "wl-" + UUID.randomUUID().toString().substring(0, 12);
        Map<String, Object> p = base(req);
        p.put("id", id);
        p.put("writerId", principal.userId());
        p.put("writerName", principal.displayName());
        mapper.insert(p);
        return get(principal, id);
    }

    @Transactional
    public WorklogDto update(KintexPrincipal principal, String id, WorklogSaveRequest req) {
        Map<String, Object> p = base(req);
        p.put("id", id);
        p.put("writerId", principal.userId());
        if (mapper.update(p) == 0) {
            throw new ApiException(ErrorCode.FORBIDDEN, "본인이 작성한 업무일지만 수정할 수 있습니다.");
        }
        return get(principal, id);
    }

    @Transactional
    public void delete(KintexPrincipal principal, String id) {
        if (mapper.delete(id, principal.userId()) == 0) {
            throw new ApiException(ErrorCode.FORBIDDEN, "본인이 작성한 업무일지만 삭제할 수 있습니다.");
        }
    }

    private static Map<String, Object> base(WorklogSaveRequest req) {
        Map<String, Object> p = new HashMap<>();
        p.put("eventId", req.eventId());
        p.put("workDate", req.workDate());
        p.put("title", req.title());
        p.put("workType", req.workType());
        p.put("progress", req.progress());
        p.put("status", req.status());
        p.put("content", req.content());
        p.put("hours", req.hours());
        return p;
    }

    private static WorklogDto toDto(Map<String, Object> r) {
        return new WorklogDto(str(r.get("id")), str(r.get("eventId")), str(r.get("writerId")),
                str(r.get("writerName")), str(r.get("workDate")), str(r.get("title")),
                str(r.get("workType")), str(r.get("progress")), str(r.get("status")),
                str(r.get("content")), dbl(r.get("hours")), str(r.get("createdAt")), str(r.get("updatedAt")));
    }
    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static double dbl(Object o) {
        if (o instanceof Number n) return n.doubleValue();
        try { return o == null ? 0 : Double.parseDouble(String.valueOf(o)); }
        catch (NumberFormatException e) { return 0; }
    }
}
