package com.zioinfo.kintex.publicsite;

import com.zioinfo.kintex.publicsite.dto.PublicGuideDtos.MonthlySummaryDto;
import com.zioinfo.kintex.publicsite.dto.PublicGuideDtos.PublicCalendarEventDto;
import com.zioinfo.kintex.publicsite.dto.PublicGuideDtos.TransportInfoDto;
import com.zioinfo.kintex.publicsite.dto.PublicGuideDtos.VisitorGuideDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 공개 관람객 안내 서비스 — 연/월 전시 캘린더 + 방문가이드 + 교통(V43 근거, DB 서빙).
 * <p>전 경로 비인증 공개. 외부 실시간 호출 없이 적재 DB 만 조회한다.
 * <p>AI 관람 도우미({@code VisitorAssistantService})가 근거 수집에 이 서비스를 재사용한다.
 */
@Service
public class PublicGuideService {

    /** 캘린더 조회 상한(대량 데모 데이터 보호). */
    static final int CALENDAR_MAX = 500;
    private static final int CALENDAR_DEFAULT = 300;

    private static final Set<String> LIFECYCLES = Set.of("UPCOMING", "ONGOING", "ENDED");

    private final PublicGuideMapper mapper;

    public PublicGuideService(PublicGuideMapper mapper) {
        this.mapper = mapper;
    }

    // ── 연/월 캘린더 ───────────────────────────────────────────────────────────
    @Transactional(readOnly = true)
    public List<PublicCalendarEventDto> calendar(Integer year, Integer month, String lifecycle) {
        return calendar(year, month, lifecycle, CALENDAR_DEFAULT);
    }

    @Transactional(readOnly = true)
    public List<PublicCalendarEventDto> calendar(Integer year, Integer month, String lifecycle, int limit) {
        String lc = normalizeLifecycle(lifecycle);
        int cap = Math.min(Math.max(limit, 1), CALENDAR_MAX);
        return mapper.findCalendar(validYear(year), validMonth(month), lc, cap)
                .stream().map(PublicGuideService::toCalendarEvent).toList();
    }

    /** 행사명 토큰 부분검색(AI 도우미 근거 수집용). tokens 비면 빈 목록. */
    @Transactional(readOnly = true)
    public List<PublicCalendarEventDto> searchEventsByName(List<String> tokens, int limit) {
        if (tokens == null || tokens.isEmpty()) {
            return List.of();
        }
        int cap = Math.min(Math.max(limit, 1), CALENDAR_MAX);
        return mapper.searchEventsByName(tokens, cap)
                .stream().map(PublicGuideService::toCalendarEvent).toList();
    }

    // ── 월별 집계 ──────────────────────────────────────────────────────────────
    @Transactional(readOnly = true)
    public List<MonthlySummaryDto> monthlySummary(Integer year) {
        return mapper.findMonthlySummary(validYear(year)).stream()
                .map(m -> new MonthlySummaryDto(toInt(m.get("eventYear")), toInt(m.get("eventMonth")),
                        toInt(m.get("eventCount"))))
                .toList();
    }

    // ── 방문가이드 ─────────────────────────────────────────────────────────────
    @Transactional(readOnly = true)
    public List<VisitorGuideDto> guides(String category) {
        return mapper.findGuides(nullIfBlank(category)).stream()
                .map(PublicGuideService::toGuide).toList();
    }

    // ── 교통 ──────────────────────────────────────────────────────────────────
    @Transactional(readOnly = true)
    public List<TransportInfoDto> transport(String mode) {
        return mapper.findTransport(nullIfBlank(mode)).stream()
                .map(PublicGuideService::toTransport).toList();
    }

    // ── row → DTO ───────────────────────────────────────────────────────────
    static PublicCalendarEventDto toCalendarEvent(Map<String, Object> m) {
        return new PublicCalendarEventDto(
                str(m.get("id")), str(m.get("name")), str(m.get("category")),
                str(m.get("hallLabel")), str(m.get("posterUrl")),
                str(m.get("startDate")), str(m.get("endDate")),
                toInt(m.get("eventYear")), toInt(m.get("eventMonth")), str(m.get("lifecycle")));
    }

    static VisitorGuideDto toGuide(Map<String, Object> m) {
        return new VisitorGuideDto(
                str(m.get("id")), str(m.get("category")), str(m.get("title")),
                str(m.get("body")), str(m.get("summary")),
                toInt(m.get("displayOrder")), str(m.get("sourceUrl")));
    }

    static TransportInfoDto toTransport(Map<String, Object> m) {
        return new TransportInfoDto(
                str(m.get("id")), str(m.get("mode")), str(m.get("lineName")), str(m.get("route")),
                str(m.get("fromPoint")), str(m.get("travelTime")), str(m.get("note")),
                toInt(m.get("displayOrder")), str(m.get("sourceUrl")));
    }

    // ── helpers ──────────────────────────────────────────────────────────────
    private static String normalizeLifecycle(String lifecycle) {
        String v = nullIfBlank(lifecycle);
        if (v == null) {
            return null;
        }
        String up = v.toUpperCase(java.util.Locale.ROOT);
        return LIFECYCLES.contains(up) ? up : null;   // 알 수 없는 값은 무시(전체 조회)
    }

    private static Integer validYear(Integer year) {
        return (year != null && year >= 2000 && year <= 2100) ? year : null;
    }

    private static Integer validMonth(Integer month) {
        return (month != null && month >= 1 && month <= 12) ? month : null;
    }

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

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

    private static Integer toInt(Object o) {
        return (o instanceof Number n) ? n.intValue() : null;
    }
}
