package com.zioinfo.mall.uiws.stats.service;

import com.zioinfo.mall.uiws.common.UiwsDataScope;
import com.zioinfo.mall.uiws.stats.dto.StatsDtos.PivotColumn;
import com.zioinfo.mall.uiws.stats.dto.StatsDtos.PivotResponse;
import com.zioinfo.mall.uiws.stats.mapper.StatsMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 근무현황 통계(모듈 04) — 동적 컬럼 피벗 서비스. 원본 com.urp.uiws.stats.service.StatsService 이식.
 * long-form 집계를 wide-form 으로 피벗. showStatus/showType 토글. 기본 조회기간 = 최근 1주일.
 * 데이터 스코프(ADMIN=전체/그 외=본인)는 UiwsDataScope 로 silo 판정.
 */
@Service
@RequiredArgsConstructor
public class StatsService {

    /** 행 식별 키 구분자(데이터에 등장하지 않는 제어문자). */
    private static final char SEP = '';

    private final StatsMapper mapper;

    @Transactional(readOnly = true)
    public PivotResponse personalWork(LocalDate fromDate, LocalDate toDate,
                                      String deptId, String userId,
                                      Boolean showStatus, Boolean showType) {
        LocalDate[] range = range(fromDate, toDate);
        UiwsDataScope.Scope scope = UiwsDataScope.current();
        // deptId 는 코어 dept 미이식으로 silo 에서 무시(파라미터 호환만 유지).
        List<Map<String, Object>> agg = mapper.personalWork(
                range[0], range[1], emptyToNull(userId), scope.all(), scope.ownerIds());

        boolean status = !Boolean.FALSE.equals(showStatus);
        boolean type = !Boolean.FALSE.equals(showType);

        List<PivotColumn> fixed = new ArrayList<>();
        fixed.add(new PivotColumn("worker", "근무자"));
        if (status) {
            fixed.add(new PivotColumn("workStatus", "근무상태"));
        }
        if (type) {
            fixed.add(new PivotColumn("workType", "근무유형"));
        }

        return pivot(agg, fixed, "company",
                row -> rowKey(row, status, type),
                row -> {
                    Map<String, Object> base = new LinkedHashMap<>();
                    base.put("worker", str(row.get("worker")));
                    if (status) {
                        base.put("workStatus", str(row.get("work_status")));
                    }
                    if (type) {
                        base.put("workType", str(row.get("work_type")));
                    }
                    return base;
                });
    }

    @Transactional(readOnly = true)
    public PivotResponse companyWork(LocalDate fromDate, LocalDate toDate,
                                     String companyId, String userId,
                                     Boolean showType) {
        LocalDate[] range = range(fromDate, toDate);
        UiwsDataScope.Scope scope = UiwsDataScope.current();
        List<Map<String, Object>> agg = mapper.companyWork(
                range[0], range[1], emptyToNull(companyId), emptyToNull(userId), scope.all(), scope.ownerIds());

        boolean type = !Boolean.FALSE.equals(showType);

        List<PivotColumn> fixed = new ArrayList<>();
        fixed.add(new PivotColumn("company", "근무처"));
        if (type) {
            fixed.add(new PivotColumn("workType", "근무유형"));
        }

        return pivot(agg, fixed, "worker",
                row -> str(row.get("company")) + SEP + (type ? str(row.get("work_type")) : ""),
                row -> {
                    Map<String, Object> base = new LinkedHashMap<>();
                    base.put("company", str(row.get("company")));
                    if (type) {
                        base.put("workType", str(row.get("work_type")));
                    }
                    return base;
                });
    }

    // ================================================================== 피벗 공통

    private interface RowKeyFn {
        String key(Map<String, Object> row);
    }

    private interface BaseFn {
        Map<String, Object> base(Map<String, Object> row);
    }

    private PivotResponse pivot(List<Map<String, Object>> agg, List<PivotColumn> fixed,
                                String dynPrefix, RowKeyFn keyFn, BaseFn baseFn) {
        Map<String, PivotColumn> dynCols = new LinkedHashMap<>();
        Map<String, Map<String, Object>> rowMap = new LinkedHashMap<>();

        for (Map<String, Object> r : agg) {
            String dynLabel = str(r.get("dyn"));
            String dynKey = dynPrefix + "_" + dynLabel;
            dynCols.putIfAbsent(dynKey, new PivotColumn(dynKey, dynLabel));

            String rk = keyFn.key(r);
            Map<String, Object> row = rowMap.computeIfAbsent(rk, k -> baseFn.base(r));
            long cnt = toLong(r.get("cnt"));
            row.merge(dynKey, cnt, (a, b) -> toLong(a) + toLong(b));
        }

        List<PivotColumn> dynamic = new ArrayList<>(dynCols.values());
        List<Map<String, Object>> rows = new ArrayList<>();
        for (Map<String, Object> row : rowMap.values()) {
            for (PivotColumn dc : dynamic) {
                row.putIfAbsent(dc.key(), 0L);
            }
            rows.add(row);
        }
        return new PivotResponse(fixed, dynamic, rows);
    }

    private String rowKey(Map<String, Object> row, boolean status, boolean type) {
        StringBuilder sb = new StringBuilder(str(row.get("worker")));
        if (status) {
            sb.append(SEP).append(str(row.get("work_status")));
        }
        if (type) {
            sb.append(SEP).append(str(row.get("work_type")));
        }
        return sb.toString();
    }

    private LocalDate[] range(LocalDate fromDate, LocalDate toDate) {
        LocalDate to = (toDate != null) ? toDate : LocalDate.now();
        LocalDate from = (fromDate != null) ? fromDate : to.minusWeeks(1);
        return new LocalDate[]{from, to};
    }

    private static String emptyToNull(String s) {
        return (s == null || s.isBlank()) ? null : s;
    }

    private static String str(Object o) {
        return o == null ? "" : o.toString();
    }

    private static long toLong(Object o) {
        if (o == null) {
            return 0L;
        }
        return (o instanceof Number n) ? n.longValue() : Long.parseLong(o.toString());
    }
}
