package com.gzzm.lobster.api;

import com.gzzm.lobster.common.IdGenerator;
import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.common.SkillRuntimeKind;
import com.gzzm.lobster.common.SkillScope;
import com.gzzm.lobster.identity.UserContext;
import com.gzzm.lobster.identity.UserContextHolder;
import com.gzzm.lobster.skill.SkillDefinition;
import com.gzzm.lobster.skill.SkillDefinitionDao;
import com.gzzm.platform.commons.Tools;
import net.cyan.arachne.HttpMethod;
import net.cyan.arachne.annotation.Service;
import net.cyan.nest.annotation.Inject;

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

/**
 * UserSkillApi —— 用户个人 Skill 管理 / Personal skill management (user-level).
 *
 * <p>对比 {@link com.gzzm.lobster.api.admin.AdminSkillApi}：
 * <ul>
 *   <li>user 权限即可访问，无需 admin 角色</li>
 *   <li>scope 强制 {@code org}，orgId / ownerUserId 自动填当前用户</li>
 *   <li>所有写操作 owner-check：只能改/删自己创建的 skill</li>
 *   <li>仅支持纯 guidance 文本；bundle 上传涉及 §7.3 解压安全校验，留给 admin API</li>
 * </ul>
 *
 * <p>可见性：个人 skill 通过 {@link SkillDefinition#getScope()} = {@code org} 出现在
 * 同 orgId 用户的 list_skills 结果中。要做"严格个人不共享"需要扩 SkillScope 枚举，
 * 一期暂以 org 粒度为界.
 */
@Service
public class UserSkillApi {

    @Inject private SkillDefinitionDao skillDao;

    /** thunwind DAO 跨线程保护 —— 详见 feedback_thunwind_dao_thread_binding. */
    private SkillDefinitionDao skillDao() {
        try {
            SkillDefinitionDao d = Tools.getBean(SkillDefinitionDao.class);
            if (d != null) return d;
        } catch (Throwable ignore) { /* fallback */ }
        return skillDao;
    }

    /** 列出当前用户创建的个人 skills. */
    @Service(url = "/ai/api/user/skills/list", method = HttpMethod.all)
    public Map<String, Object> list() throws Exception {
        UserContext user = UserContextHolder.require();
        List<SkillDefinition> rows = skillDao().listOwnedBy(user.getUserId());
        List<Map<String, Object>> items = new ArrayList<>();
        for (SkillDefinition s : rows) items.add(toMap(s));
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("items", items);
        out.put("total", items.size());
        return out;
    }

    /** 单条详情——仍做 owner-check，不让用户看到同 org 别人的个人 skill. */
    @Service(url = "/ai/api/user/skills/{$0}", method = HttpMethod.all)
    public Map<String, Object> get(String skillId) throws Exception {
        UserContext user = UserContextHolder.require();
        SkillDefinition s = requireOwned(user, skillId);
        return toMap(s);
    }

    /**
     * 创建个人 skill. scope 强制 {@code org}，orgId / ownerUserId 自动填；
     * 接口只收 name / description / guidance / runtimeKind / enabled 五个业务字段.
     */
    @Service(url = "/ai/api/user/skills/create", method = HttpMethod.post)
    public Map<String, Object> create(String name, String description, String guidance,
                                      String runtimeKind, Boolean enabled) throws Exception {
        UserContext user = UserContextHolder.require();
        if (name == null || name.trim().isEmpty()) {
            throw new LobsterException("user.skill.invalid", "name required");
        }
        if (guidance == null || guidance.trim().isEmpty()) {
            throw new LobsterException("user.skill.invalid", "guidance required");
        }
        SkillDefinition s = new SkillDefinition();
        s.setSkillId(IdGenerator.prefixed("sk_"));
        s.setName(name.trim());
        s.setScope(SkillScope.org);
        s.setOrgId(user.getOrgId());
        s.setOwnerUserId(user.getUserId());
        s.setTriggerCondition(description);  // description → triggerCondition（对齐 Claude Code 语义）
        s.setGuidance(guidance);
        s.setRuntimeKind(runtimeKind == null || runtimeKind.isEmpty()
                ? SkillRuntimeKind.single_shot
                : SkillRuntimeKind.valueOf(runtimeKind));
        s.setVersion(1);
        s.setEnabled(enabled == null ? Boolean.TRUE : enabled);
        s.setCreateTime(new Date());
        s.setUpdateTime(new Date());
        skillDao().save(s);
        return toMap(s);
    }

    /** 更新自己的个人 skill. */
    @Service(url = "/ai/api/user/skills/{$0}/update", method = HttpMethod.post)
    public Map<String, Object> update(String skillId, String name, String description,
                                      String guidance, String runtimeKind, Boolean enabled) throws Exception {
        UserContext user = UserContextHolder.require();
        SkillDefinition s = requireOwned(user, skillId);
        if (name != null && !name.trim().isEmpty()) s.setName(name.trim());
        if (description != null) s.setTriggerCondition(description);
        if (guidance != null) s.setGuidance(guidance);
        if (runtimeKind != null && !runtimeKind.isEmpty()) {
            s.setRuntimeKind(SkillRuntimeKind.valueOf(runtimeKind));
        }
        if (enabled != null) s.setEnabled(enabled);
        s.setVersion((s.getVersion() == null ? 0 : s.getVersion()) + 1);
        s.setUpdateTime(new Date());
        skillDao().save(s);
        return toMap(s);
    }

    @Service(url = "/ai/api/user/skills/{$0}/delete", method = HttpMethod.post)
    public Map<String, Object> delete(String skillId) throws Exception {
        UserContext user = UserContextHolder.require();
        requireOwned(user, skillId);
        int n = skillDao().deleteById(skillId);
        Map<String, Object> out = new LinkedHashMap<>();
        out.put("deleted", n);
        return out;
    }

    @Service(url = "/ai/api/user/skills/{$0}/enabled", method = HttpMethod.post)
    public Map<String, Object> toggleEnabled(String skillId, Boolean enabled) throws Exception {
        UserContext user = UserContextHolder.require();
        SkillDefinition s = requireOwned(user, skillId);
        s.setEnabled(enabled == null ? !Boolean.TRUE.equals(s.getEnabled()) : enabled);
        s.setUpdateTime(new Date());
        skillDao().save(s);
        return toMap(s);
    }

    /** owner-check：skill 必须存在且 ownerUserId 与当前用户一致. */
    private SkillDefinition requireOwned(UserContext user, String skillId) throws Exception {
        if (skillId == null || skillId.isEmpty()) {
            throw new LobsterException("user.skill.invalid", "skillId required");
        }
        SkillDefinition s = skillDao().getSkill(skillId);
        if (s == null) {
            throw new LobsterException("user.skill.not_found", "Skill not found: " + skillId);
        }
        if (!user.getUserId().equals(s.getOwnerUserId())) {
            throw new LobsterException("user.skill.forbidden",
                    "Not the owner of this skill: " + skillId);
        }
        return s;
    }

    private static Map<String, Object> toMap(SkillDefinition s) {
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("skillId", s.getSkillId());
        m.put("name", s.getName());
        m.put("scope", s.getScope() == null ? null : s.getScope().name());
        m.put("description", s.getTriggerCondition());
        m.put("guidance", s.getGuidance());
        m.put("runtimeKind", s.getRuntimeKind() == null ? null : s.getRuntimeKind().name());
        m.put("version", s.getVersion());
        m.put("enabled", s.getEnabled());
        m.put("ownerUserId", s.getOwnerUserId());
        m.put("createTime", s.getCreateTime());
        m.put("updateTime", s.getUpdateTime());
        return m;
    }
}
