package com.zioinfo.cms.menu;

import com.zioinfo.cms.content.CmsContent;
import com.zioinfo.cms.content.mapper.ContentMapper;
import com.zioinfo.cms.menu.mapper.MenuMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

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

/** 메뉴/카테고리 서비스 — 트리 조립 + 사이트맵 생성. */
@Service
@RequiredArgsConstructor
public class MenuService {

    private final MenuMapper mapper;
    private final ContentMapper contentMapper;

    public List<CmsMenu> listFlat(String menuType) {
        return mapper.findAll(menuType);
    }

    /** 트리 형태로 조립. */
    public List<CmsMenu> tree(String menuType) {
        List<CmsMenu> all = mapper.findAll(menuType);
        Map<Long, CmsMenu> byId = new LinkedHashMap<>();
        for (CmsMenu m : all) { m.setChildren(new ArrayList<>()); byId.put(m.getId(), m); }
        List<CmsMenu> roots = new ArrayList<>();
        for (CmsMenu m : all) {
            if (m.getParentId() != null && byId.containsKey(m.getParentId())) {
                byId.get(m.getParentId()).getChildren().add(m);
            } else {
                roots.add(m);
            }
        }
        return roots;
    }

    public CmsMenu get(Long id) {
        CmsMenu m = mapper.findById(id);
        if (m == null) throw new RuntimeException("ERR-CMS-MENU-404: 메뉴 없음");
        return m;
    }

    public CmsMenu create(Map<String, Object> req) {
        CmsMenu m = bind(new CmsMenu(), req);
        if (m.getParentId() != null) {
            CmsMenu parent = mapper.findById(m.getParentId());
            m.setDepth(parent == null ? 0 : (parent.getDepth() == null ? 0 : parent.getDepth()) + 1);
        } else {
            m.setDepth(0);
        }
        mapper.insert(m);
        return mapper.findById(m.getId());
    }

    public CmsMenu update(Long id, Map<String, Object> req) {
        CmsMenu m = get(id);
        bind(m, req);
        mapper.update(m);
        return mapper.findById(id);
    }

    public void delete(Long id) {
        if (mapper.countChildren(id) > 0) {
            throw new RuntimeException("ERR-CMS-MENU-409: 하위 메뉴가 있어 삭제 불가");
        }
        mapper.delete(id);
    }

    /** 사이트맵 생성 — 발행 콘텐츠 slug 기반 XML. */
    public String sitemapXml(String baseUrl) {
        List<CmsContent> published = contentMapper.findAll(null, "PUBLISHED", null, null);
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
        sb.append("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n");
        String base = baseUrl == null ? "" : baseUrl.replaceAll("/+$", "");
        for (CmsContent c : published) {
            sb.append("  <url><loc>").append(base).append("/").append(c.getSlug()).append("</loc>");
            if (c.getPublishedAt() != null) {
                sb.append("<lastmod>").append(c.getPublishedAt().toLocalDate()).append("</lastmod>");
            }
            sb.append("</url>\n");
        }
        sb.append("</urlset>");
        return sb.toString();
    }

    private CmsMenu bind(CmsMenu m, Map<String, Object> r) {
        if (r.containsKey("parentId")) m.setParentId(longOf(r.get("parentId")));
        if (r.containsKey("name")) m.setName(str(r.get("name")));
        if (r.containsKey("menuType")) m.setMenuType(str(r.get("menuType")));
        if (r.containsKey("url")) m.setUrl(str(r.get("url")));
        if (r.containsKey("slug")) m.setSlug(str(r.get("slug")));
        if (r.containsKey("sortOrder")) m.setSortOrder(intOf(r.get("sortOrder")));
        if (r.containsKey("active")) m.setActive(Boolean.parseBoolean(String.valueOf(r.get("active"))));
        if (m.getMenuType() == null) m.setMenuType("MENU");
        return m;
    }

    private String str(Object o) { return o == null ? null : String.valueOf(o); }
    private Long longOf(Object o) { if (o == null || String.valueOf(o).isBlank()) return null; try { return Long.valueOf(String.valueOf(o).trim()); } catch (Exception e) { return null; } }
    private Integer intOf(Object o) { if (o == null) return null; try { return Integer.valueOf(String.valueOf(o).trim()); } catch (Exception e) { return null; } }
}
