package com.zioinfo.kintex.cms;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.zioinfo.kintex.cms.dto.MicrositeDto;
import com.zioinfo.kintex.cms.dto.MicrositeSaveRequest;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;

/**
 * M17 마이크로사이트 서비스 — 참가업체별 공개 소개 페이지(섹션 JSON·테마·SEO).
 * sections 는 jsonb 문자열 ↔ JsonNode 변환. 공개 읽기({@code publicView})는 draft 여부 무관 조회(공개 P5 팀이 상태 필터).
 */
@Service
public class MicrositeService {

    private final MicrositeMapper mapper;
    private final ObjectMapper objectMapper;

    public MicrositeService(MicrositeMapper mapper, ObjectMapper objectMapper) {
        this.mapper = mapper;
        this.objectMapper = objectMapper;
    }

    /** 조회 — 미존재 시 빈 초안(exhibitorId만) 반환(에디터가 새로 저장 가능). */
    public MicrositeDto get(String exhibitorId) {
        Map<String, Object> r = mapper.findByExhibitor(exhibitorId);
        if (r == null) {
            return new MicrositeDto(exhibitorId, null, null, null, "blue",
                    objectMapper.createArrayNode(), null, null, "ko", "draft", null, null);
        }
        return toDto(r);
    }

    /** 공개 읽기 — 미존재 시 404(무인증 경로). */
    public MicrositeDto publicView(String exhibitorId) {
        Map<String, Object> r = mapper.findByExhibitor(exhibitorId);
        if (r == null) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
        return toDto(r);
    }

    @Transactional
    public MicrositeDto save(String exhibitorId, MicrositeSaveRequest req) {
        String sectionsJson = "[]";
        if (req.sections() != null && !req.sections().isNull()) {
            try {
                sectionsJson = objectMapper.writeValueAsString(req.sections());
            } catch (Exception e) {
                throw new ApiException(ErrorCode.VALIDATION, "섹션 데이터 형식이 올바르지 않습니다.");
            }
        }
        String status = req.status() == null ? "draft" : req.status().trim();
        if (!"draft".equals(status) && !"published".equals(status)) {
            throw new ApiException(ErrorCode.VALIDATION, "알 수 없는 사이트 상태입니다: " + status);
        }
        Map<String, Object> p = new HashMap<>();
        p.put("exhibitorId", exhibitorId);
        p.put("eventId", blankToNull(req.eventId()));
        p.put("slug", blankToNull(req.slug()));
        p.put("exhibitorName", req.exhibitorName());
        p.put("theme", blankToNull(req.theme()));
        p.put("sectionsJson", sectionsJson);
        p.put("seoTitle", req.seoTitle());
        p.put("seoMeta", req.seoMeta());
        p.put("langs", blankToNull(req.langs()));
        p.put("status", status);
        mapper.upsert(p);
        return get(exhibitorId);
    }

    private MicrositeDto toDto(Map<String, Object> r) {
        JsonNode sections = parseSections(str(r.get("sections")));
        return new MicrositeDto(
                str(r.get("exhibitorId")), str(r.get("eventId")), str(r.get("slug")),
                str(r.get("exhibitorName")), str(r.get("theme")), sections,
                str(r.get("seoTitle")), str(r.get("seoMeta")), str(r.get("langs")),
                str(r.get("status")), str(r.get("publishedAt")), str(r.get("updatedAt")));
    }

    private JsonNode parseSections(String json) {
        if (json == null || json.isBlank()) {
            return objectMapper.createArrayNode();
        }
        try {
            JsonNode node = objectMapper.readTree(json);
            return node == null ? objectMapper.createArrayNode() : node;
        } catch (Exception e) {
            ArrayNode empty = objectMapper.createArrayNode();
            return empty;
        }
    }

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