package com.zioinfo.kintex.dashboard;

import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.dashboard.dto.DashboardDto;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 주최자 대시보드 서비스 — 실 테이블 집계로 KPI·참가업체·피드를 조립.
 * 마일스톤은 행사 시작일 기준 표준 일정을 파생(state 는 오늘 기준 판정) — 원천 값이 아닌 파생 스케줄.
 */
@Service
public class DashboardService {

    /** 시작일 기준 상대 마일스톤(라벨, 시작일로부터 -일수). 개장은 0. */
    private static final Object[][] MILESTONE_TEMPLATE = {
            {"홀 배정", 150},
            {"부스 배치 확정", 60},
            {"유틸리티 신청 마감", 25},
            {"신고서류 제출", 7},
            {"개장", 0},
    };

    private final DashboardMapper mapper;

    public DashboardService(DashboardMapper mapper) {
        this.mapper = mapper;
    }

    public DashboardDto getDashboard(String eventId) {
        Map<String, Object> header = mapper.findEventHeader(eventId);
        if (header == null) {
            throw new ApiException(ErrorCode.NOT_FOUND, "행사를 찾을 수 없습니다.");
        }
        return new DashboardDto(
                buildMilestones(str(header.get("startDate"))),
                buildKpis(mapper.findKpiCounts(eventId)),
                buildExhibitors(mapper.findExhibitors(eventId)),
                buildFeed(mapper.findRecentRenders(eventId)));
    }

    /** 행사 시작일에서 상대 일정을 파생. 시작일이 없으면 빈 목록(정직). */
    private List<DashboardDto.Milestone> buildMilestones(String startDate) {
        List<DashboardDto.Milestone> out = new ArrayList<>();
        LocalDate start = parseDate(startDate);
        if (start == null) {
            return out;
        }
        LocalDate today = LocalDate.now();
        for (Object[] tpl : MILESTONE_TEMPLATE) {
            String label = (String) tpl[0];
            int daysBefore = (int) tpl[1];
            LocalDate date = start.minusDays(daysBefore);
            String state;
            if (date.isBefore(today)) {
                state = "done";
            } else if (date.isEqual(today)) {
                state = "current";
            } else {
                state = "upcoming";
            }
            out.add(new DashboardDto.Milestone(label, date.toString(), state));
        }
        return out;
    }

    private List<DashboardDto.Kpi> buildKpis(Map<String, Object> c) {
        List<DashboardDto.Kpi> out = new ArrayList<>();
        if (c == null) {
            return out;
        }
        long boothCount = lng(c.get("boothCount"));
        long targetBooths = lng(c.get("targetBooths"));
        long designApproved = lng(c.get("designApproved"));
        long designTotal = lng(c.get("designTotal"));
        long utilityOrders = lng(c.get("utilityOrders"));
        long renderDone = lng(c.get("renderDone"));

        String boothValue = targetBooths > 0 ? boothCount + "/" + targetBooths : String.valueOf(boothCount);
        out.add(new DashboardDto.Kpi("부스 배치", boothValue, null, "flat"));
        out.add(new DashboardDto.Kpi("설계 승인", designApproved + "/" + designTotal, null, "flat"));
        out.add(new DashboardDto.Kpi("유틸리티 신청", String.valueOf(utilityOrders), null, "flat"));
        out.add(new DashboardDto.Kpi("렌더 완료", String.valueOf(renderDone), null, "flat"));
        return out;
    }

    private List<DashboardDto.Exhibitor> buildExhibitors(List<Map<String, Object>> rows) {
        List<DashboardDto.Exhibitor> out = new ArrayList<>();
        if (rows == null) {
            return out;
        }
        for (Map<String, Object> r : rows) {
            out.add(new DashboardDto.Exhibitor(
                    str(r.get("companyName")), str(r.get("boothNo")), str(r.get("status"))));
        }
        return out;
    }

    private List<DashboardDto.Feed> buildFeed(List<Map<String, Object>> rows) {
        List<DashboardDto.Feed> out = new ArrayList<>();
        if (rows == null) {
            return out;
        }
        for (Map<String, Object> r : rows) {
            String status = str(r.get("status"));
            String preset = str(r.get("shotPreset"));
            String message = "이미지 생성(" + preset + ") " + renderStatusLabel(status);
            out.add(new DashboardDto.Feed(str(r.get("id")), "render", message, str(r.get("at"))));
        }
        return out;
    }

    private static String renderStatusLabel(String status) {
        if (status == null) {
            return "";
        }
        return switch (status) {
            case "DONE" -> "완료";
            case "FAILED" -> "실패";
            case "RUNNING" -> "진행 중";
            default -> "대기";
        };
    }

    private static LocalDate parseDate(String s) {
        if (s == null || s.isBlank()) {
            return null;
        }
        try {
            return LocalDate.parse(s);
        } catch (DateTimeParseException e) {
            return null;
        }
    }

    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;
        }
    }

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