package com.zioinfo.kintex.ops;

import com.zioinfo.kintex.ops.dto.OpsDto;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 홀 현장 운영 서비스.
 *
 * <p>실집계: ①혼잡 = M10 체크인(현장 인원)을 홀 판매면적 비중으로 분배한 추정치 + 시간대 추이,
 * ②전력 = M4 유틸리티 신청 부하(requestedKw) 홀별 합. HVAC/조명/주차는 센서·IoT 미연동 → null/빈배열.
 * 사람 계측·전력계 하드웨어 코드는 없다(표시 전용, PLANNING §2.6).
 */
@Service
public class OpsService {

    /** 홀 인원 밀도(명/㎡) 혼잡 임계 — 전시 통로 기준 근사치. */
    private static final double DENSITY_BUSY = 0.7;
    private static final double DENSITY_MODERATE = 0.3;

    private final OpsMapper mapper;

    public OpsService(OpsMapper mapper) {
        this.mapper = mapper;
    }

    public OpsDto getOps(String eventId) {
        long onSite = mapper.countCheckedIn(eventId);

        List<Map<String, Object>> rows = mapper.findHallOps(eventId);
        if (rows == null) {
            rows = new ArrayList<>();
        }

        double totalArea = 0;
        for (Map<String, Object> r : rows) {
            totalArea += dbl(r.get("salesArea"));
        }

        List<OpsDto.Hall> halls = new ArrayList<>();
        Double powerTotal = null;
        int hallCount = rows.size();
        for (Map<String, Object> r : rows) {
            double area = dbl(r.get("salesArea"));
            Double powerKw = r.get("powerKw") == null ? null : dbl(r.get("powerKw"));

            long est = 0L;
            if (onSite > 0) {
                if (totalArea > 0) {
                    est = Math.round(onSite * (area / totalArea));
                } else if (hallCount > 0) {
                    // 부스 미배치 → 균등 분배(면적 가중 불가).
                    est = Math.round((double) onSite / hallCount);
                }
            }

            halls.add(new OpsDto.Hall(
                    str(r.get("hallId")), str(r.get("label")), est, heatFor(est, area), powerKw));

            if (powerKw != null) {
                powerTotal = (powerTotal == null ? 0d : powerTotal) + powerKw;
            }
        }

        List<OpsDto.CheckinPoint> series = new ArrayList<>();
        List<Map<String, Object>> seriesRows = mapper.findCheckinSeries(eventId);
        if (seriesRows != null) {
            for (Map<String, Object> r : seriesRows) {
                series.add(new OpsDto.CheckinPoint(str(r.get("hour")), lng(r.get("count"))));
            }
        }

        // HVAC/조명/주차 센서·IoT 미연동 → null/빈배열(정직).
        OpsDto.Hvac hvac = new OpsDto.Hvac(null, null, null);
        return new OpsDto(halls, hvac, new ArrayList<>(), new ArrayList<>(), onSite, powerTotal, series);
    }

    /** 인원 밀도(명/㎡) 기준 혼잡 등급. 인원 0 또는 면적 부재 시 보수적 처리. */
    private static String heatFor(long est, double area) {
        if (est <= 0) {
            return "smooth";
        }
        if (area <= 0) {
            return "moderate";
        }
        double density = est / area;
        if (density >= DENSITY_BUSY) {
            return "busy";
        }
        if (density >= DENSITY_MODERATE) {
            return "moderate";
        }
        return "smooth";
    }

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

    private static double dbl(Object o) {
        if (o == null) {
            return 0d;
        }
        if (o instanceof Number n) {
            return n.doubleValue();
        }
        try {
            return Double.parseDouble(String.valueOf(o));
        } catch (NumberFormatException e) {
            return 0d;
        }
    }

    private static long lng(Object o) {
        if (o == null) {
            return 0L;
        }
        if (o instanceof Number n) {
            return n.longValue();
        }
        try {
            return Long.parseLong(String.valueOf(o));
        } catch (NumberFormatException e) {
            return 0L;
        }
    }
}
