package com.gzzm.lobster.workspace;

import com.gzzm.lobster.common.JsonUtil;
import com.gzzm.lobster.common.ResourceSourceType;

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

/**
 * ResourceMetadata —— 统一读写 {@link WorkspaceResource#getMetadataJson()} 的小工具 /
 * Lightweight accessor for the free-form metadata JSON column.
 *
 * <p>metadataJson 的字段约定（USER_UPLOAD 是主要使用者，但 OA_FILE / ARTIFACT 也可以用）：
 * <ul>
 *   <li>{@code origRef / mdRef / outlineRef}：USER_UPLOAD 的三份文件 ref</li>
 *   <li>{@code kind}：docx / pdf / text / oa / artifact 等粗分类</li>
 *   <li>{@code oaFileType / oaFileTypeLabel}：OA_FILE 的业务子类型，例如 MAIL/邮件、DOCUMENT/公文</li>
 *   <li>{@code sizeBytes}：原件大小</li>
 *   <li>{@code summary}：LLM 读完后回填的一句话摘要（也用于 workspace 索引段 / list_files 提示）</li>
 *   <li>{@code summaryUpdatedBy}：写入者标记（user / assistant / system）</li>
 *   <li>{@code summaryAt}：最后一次写入时间</li>
 *   <li>{@code outlineSummary.topSections}：上传解析时的结构标题列表</li>
 * </ul>
 *
 * <p>不做 schema 校验——上下游就按约定读写；字段缺失时读方法返回 null 由调用方决定默认.
 */
public final class ResourceMetadata {

    private ResourceMetadata() {}

    /** 解析 metadataJson，出错或空时返回新 map，调用方可以直接继续操作. */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> readMap(String metadataJson) {
        if (metadataJson == null || metadataJson.isEmpty()) return new LinkedHashMap<>();
        try {
            Map<String, Object> m = JsonUtil.fromJson(metadataJson, Map.class);
            if (m == null) return new LinkedHashMap<>();
            return new LinkedHashMap<>(m);
        } catch (Throwable t) {
            return new LinkedHashMap<>();
        }
    }

    public static String getSummary(WorkspaceResource r) {
        if (r == null) return null;
        Map<String, Object> m = readMap(r.getMetadataJson());
        Object s = m.get("summary");
        return s == null ? null : String.valueOf(s);
    }

    public static String getKind(WorkspaceResource r) {
        if (r == null) return null;
        Map<String, Object> m = readMap(r.getMetadataJson());
        Object s = m.get("kind");
        return s == null ? null : String.valueOf(s);
    }

    public static String getOaFileType(WorkspaceResource r) {
        if (r == null) return null;
        Map<String, Object> m = readMap(r.getMetadataJson());
        Object s = m.get("oaFileType");
        return s == null ? null : String.valueOf(s);
    }

    public static String getOaFileTypeLabel(WorkspaceResource r) {
        if (r == null) return null;
        Map<String, Object> m = readMap(r.getMetadataJson());
        Object s = m.get("oaFileTypeLabel");
        return s == null ? null : String.valueOf(s);
    }

    @SuppressWarnings("unchecked")
    public static List<String> getTopSections(WorkspaceResource r) {
        if (r == null) return null;
        Map<String, Object> m = readMap(r.getMetadataJson());
        Object os = m.get("outlineSummary");
        if (!(os instanceof Map)) return null;
        Object top = ((Map<String, Object>) os).get("topSections");
        if (!(top instanceof List)) return null;
        return (List<String>) top;
    }

    /** 把 summary 字段合并回 metadataJson 并返回新 JSON 字符串（幂等、保留其它字段）. */
    public static String writeSummary(String metadataJson, String summary, String updatedBy, long atMs) {
        Map<String, Object> m = readMap(metadataJson);
        if (summary == null || summary.isEmpty()) {
            m.remove("summary");
        } else {
            m.put("summary", summary);
        }
        if (updatedBy != null && !updatedBy.isEmpty()) m.put("summaryUpdatedBy", updatedBy);
        if (atMs > 0) m.put("summaryAt", atMs);
        return JsonUtil.toJson(m);
    }

    /** 标记 OA_FILE 的业务子类型（例如 MAIL/邮件、DOCUMENT/公文），保留其它 metadata 字段. */
    public static String writeOaFileType(String metadataJson, String type, String label) {
        Map<String, Object> m = readMap(metadataJson);
        if (type != null && !type.isEmpty()) m.put("oaFileType", type);
        if (label != null && !label.isEmpty()) m.put("oaFileTypeLabel", label);
        return JsonUtil.toJson(m);
    }

    public static String writeArtifactLifecycle(String metadataJson,
                                                String role,
                                                String visibility) {
        Map<String, Object> m = readMap(metadataJson);
        if (role != null && !role.isEmpty()) m.put("artifactRole", role);
        if (visibility != null && !visibility.isEmpty()) m.put("artifactVisibility", visibility);
        return JsonUtil.toJson(m);
    }

    public static String mergeArtifactLifecycle(String metadataJson, String lifecycleMetadataJson) {
        Map<String, Object> m = readMap(metadataJson);
        Map<String, Object> lifecycle = readMap(lifecycleMetadataJson);
        mergeIfPresent(m, lifecycle, "artifactRole");
        mergeIfPresent(m, lifecycle, "artifactVisibility");
        return JsonUtil.toJson(m);
    }

    public static String writeArtifactLifecycleDefaults(String metadataJson,
                                                        String artifactType,
                                                        String displayName,
                                                        String mimeType) {
        Map<String, Object> m = readMap(metadataJson);
        Map<String, Object> defaults = inferArtifactLifecycle(artifactType, displayName, mimeType);
        putIfMissing(m, "artifactRole", defaults.get("artifactRole"));
        putIfMissing(m, "artifactVisibility", defaults.get("artifactVisibility"));
        return JsonUtil.toJson(m);
    }

    public static Map<String, Object> artifactLifecycle(WorkspaceResource r) {
        Map<String, Object> out = new LinkedHashMap<>();
        if (r == null) return out;
        if (r.getSourceType() != ResourceSourceType.ARTIFACT
                && r.getSourceType() != ResourceSourceType.WORKSHOP_DOC) {
            return out;
        }
        Map<String, Object> m = readMap(r.getMetadataJson());
        Map<String, Object> defaults = inferArtifactLifecycle(r.getArtifactType(), r.getDisplayName(), r.getMimeType());
        out.put("artifactRole", firstNonEmpty(m.get("artifactRole"), defaults.get("artifactRole")));
        out.put("artifactVisibility", firstNonEmpty(m.get("artifactVisibility"), defaults.get("artifactVisibility")));
        return out;
    }

    public static String getArtifactRole(WorkspaceResource r) {
        Object v = artifactLifecycle(r).get("artifactRole");
        return v == null ? null : String.valueOf(v);
    }

    public static String getArtifactVisibility(WorkspaceResource r) {
        Object v = artifactLifecycle(r).get("artifactVisibility");
        return v == null ? null : String.valueOf(v);
    }

    private static Map<String, Object> inferArtifactLifecycle(String artifactType,
                                                             String displayName,
                                                             String mimeType) {
        Map<String, Object> out = new LinkedHashMap<>();
        String type = artifactType == null ? "" : artifactType;
        String name = displayName == null ? "" : displayName.toLowerCase(Locale.ROOT);
        String ext = extension(name);

        String role = "FINAL_DELIVERABLE";
        String visibility = "primary";

        if ("CODE_SCRIPT".equals(type)) {
            role = "STATE";
            visibility = "internal";
        } else if ("TASK_LEDGER".equals(type)) {
            role = "STATE";
            visibility = "internal";
        } else if ("SUMMARY_SNAPSHOT".equals(type) || "PENDING_ACTION_OBJECT".equals(type)) {
            role = "STATE";
            visibility = "internal";
        } else if ("MERGED_RESULT".equals(type)) {
            role = "INTERMEDIATE";
            visibility = "supporting";
        } else if ("WORKING_COPY".equals(type)) {
            role = "SOURCE";
            visibility = "supporting";
        } else if ("EXTRACTED_TABLE".equals(type)) {
            role = "INTERMEDIATE";
            visibility = "supporting";
        } else if (isReviewThumbnail(name, ext)) {
            role = "REVIEW_AUX";
            visibility = "internal";
        } else if (name.endsWith(".meta.json")) {
            role = "STATE";
            visibility = "internal";
        } else if ("json".equals(ext) && (name.contains("ledger") || name.contains("snapshot") || name.contains("state"))) {
            role = "STATE";
            visibility = "internal";
        }

        out.put("artifactRole", role);
        out.put("artifactVisibility", visibility);
        return out;
    }

    private static boolean isReviewThumbnail(String name, String ext) {
        if (!("jpg".equals(ext) || "jpeg".equals(ext) || "png".equals(ext) || "webp".equals(ext))) return false;
        return name.matches(".*(^|[\\\\/])slide-[0-9]{1,3}\\.(jpg|jpeg|png|webp)$")
                || name.matches("slide-[0-9]{1,3}\\.(jpg|jpeg|png|webp)$");
    }

    private static String extension(String lowerName) {
        if (lowerName == null) return "";
        int dot = lowerName.lastIndexOf('.');
        if (dot < 0 || dot == lowerName.length() - 1) return "";
        String ext = lowerName.substring(dot + 1);
        return ext.length() <= 12 ? ext : "";
    }

    private static void putIfMissing(Map<String, Object> m, String key, Object value) {
        if (m == null || key == null || value == null) return;
        Object existing = m.get(key);
        if (existing == null || String.valueOf(existing).trim().isEmpty()) {
            m.put(key, value);
        }
    }

    private static void mergeIfPresent(Map<String, Object> target, Map<String, Object> source, String key) {
        if (target == null || source == null || key == null) return;
        Object value = source.get(key);
        if (value != null && !String.valueOf(value).trim().isEmpty()) {
            target.put(key, value);
        }
    }

    private static Object firstNonEmpty(Object first, Object fallback) {
        if (first == null) return fallback;
        String s = String.valueOf(first);
        return s.trim().isEmpty() ? fallback : first;
    }
}
