package com.gzzm.lobster.artifact;

import com.gzzm.lobster.common.ArtifactStatus;
import com.gzzm.lobster.common.ArtifactType;
import com.gzzm.lobster.common.IdGenerator;
import com.gzzm.lobster.common.LobsterException;
import com.gzzm.lobster.identity.UserContext;
import com.gzzm.lobster.storage.FileSystemContentStore;
import com.gzzm.lobster.thread.ThreadRoom;
import com.gzzm.platform.commons.Tools;
import net.cyan.arachne.annotation.Service;
import net.cyan.nest.annotation.Inject;

import java.util.Date;
import java.util.List;

/**
 * ArtifactService —— Artifact 领域服务 / Artifact domain service.
 *
 * <p>负责：创建、覆盖、读取、归档、状态流转。
 * 正文内容统一走 ContentStore，不直接落 DB 大字段。
 */
@Service
public class ArtifactService {

    @Inject
    private ArtifactDao artifactDao;

    /** 用具体类注入：nest 对接口注入不可靠（详见 LlmRuntime.contentStore 注释）。 */
    @Inject
    private FileSystemContentStore contentStore;

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

    public Artifact create(ThreadRoom thread, UserContext user, ArtifactType type,
                           String title, String content, String format,
                           String sourceMessageId, String sourceRunId, String sourceOaFileId) throws Exception {
        Artifact a = new Artifact();
        a.setArtifactId(IdGenerator.artifactId());
        a.setThreadId(thread.getThreadId());
        a.setWorkspaceId(thread.getWorkspaceId());
        a.setUserId(user.getUserId());
        a.setArtifactType(type);
        a.setTitle(title == null || title.isEmpty() ? (type.name() + "-" + a.getArtifactId().substring(0, 6)) : title);
        a.setFormat(format == null || format.isEmpty() ? "txt" : format);
        a.setSourceMessageId(sourceMessageId);
        a.setSourceRunId(sourceRunId);
        a.setSourceOaFileId(sourceOaFileId);
        a.setVersion(1);
        a.setStatus(ArtifactStatus.active);
        a.setCreateTime(new Date());
        a.setUpdateTime(new Date());
        if (content != null) {
            String ref = contentStore.write("artifact", user.getUserId(), content, a.getFormat());
            a.setContentRef(ref);
            a.setContentSize((long) content.getBytes().length);
        }
        artifactDao().save(a);
        return a;
    }

    public Artifact overwrite(String artifactId, UserContext user, String newContent, String newTitle) throws Exception {
        Artifact a = artifactDao().getArtifact(artifactId);
        if (a == null) throw new LobsterException("artifact.not_found", "Artifact not found: " + artifactId);
        if (!a.getUserId().equals(user.getUserId())) {
            throw new LobsterException("artifact.forbidden", "Artifact not owned by current user");
        }
        if (a.getContentRef() != null) {
            contentStore.delete(a.getContentRef());
        }
        String ref = contentStore.write("artifact", user.getUserId(), newContent == null ? "" : newContent, a.getFormat());
        a.setContentRef(ref);
        a.setContentSize((long) (newContent == null ? 0 : newContent.getBytes().length));
        if (newTitle != null && !newTitle.isEmpty()) a.setTitle(newTitle);
        a.setVersion((a.getVersion() == null ? 1 : a.getVersion()) + 1);
        a.setUpdateTime(new Date());
        artifactDao().save(a);
        return a;
    }

    public Artifact get(String artifactId) throws Exception {
        return artifactDao().getArtifact(artifactId);
    }

    public List<Artifact> listByThread(String threadId) throws Exception {
        return artifactDao().listByThread(threadId, ArtifactStatus.deleted);
    }

    public List<Artifact> listByThreadAndType(String threadId, ArtifactType type) throws Exception {
        return artifactDao().listByThreadAndType(threadId, type, ArtifactStatus.deleted);
    }

    /** 读取工件正文 / Read artifact body. */
    public String readArtifactContent(String artifactId, int offset, int limit) throws Exception {
        Artifact a = artifactDao().getArtifact(artifactId);
        if (a == null) return null;
        if (a.getContentRef() == null) return "";
        if (limit <= 0) return contentStore.read(a.getContentRef());
        return contentStore.read(a.getContentRef(), offset, limit);
    }

    public Artifact archive(String artifactId) throws Exception {
        artifactDao().updateStatus(ArtifactStatus.archived, artifactId);
        return artifactDao().getArtifact(artifactId);
    }

    /**
     * 二进制 API —— 创建（docx/xlsx/pptx/pdf 等二进制产物）.
     *
     * <p>正文走 {@link FileSystemContentStore#writeBinary}，落盘即二进制；不要把 bytes
     * 当字符串走默认编码写磁盘，Office 文件会损坏.
     */
    public Artifact createBinary(ThreadRoom thread, UserContext user, ArtifactType type,
                                 String title, byte[] content, String format,
                                 String sourceMessageId, String sourceRunId,
                                 String sourceOaFileId) throws Exception {
        Artifact a = new Artifact();
        a.setArtifactId(IdGenerator.artifactId());
        a.setThreadId(thread.getThreadId());
        a.setWorkspaceId(thread.getWorkspaceId());
        a.setUserId(user.getUserId());
        a.setArtifactType(type);
        a.setTitle(title == null || title.isEmpty() ? (type.name() + "-" + a.getArtifactId().substring(0, 6)) : title);
        a.setFormat(format == null || format.isEmpty() ? "bin" : format);
        a.setSourceMessageId(sourceMessageId);
        a.setSourceRunId(sourceRunId);
        a.setSourceOaFileId(sourceOaFileId);
        a.setVersion(1);
        a.setStatus(ArtifactStatus.active);
        a.setCreateTime(new Date());
        a.setUpdateTime(new Date());
        byte[] bytes = content == null ? new byte[0] : content;
        String ref = contentStore.writeBinary("artifact", user.getUserId(), bytes, a.getFormat());
        a.setContentRef(ref);
        a.setContentSize((long) bytes.length);
        artifactDao().save(a);
        return a;
    }

    public Artifact overwriteBinary(String artifactId, UserContext user,
                                    byte[] newContent, String newTitle) throws Exception {
        Artifact a = artifactDao().getArtifact(artifactId);
        if (a == null) throw new LobsterException("artifact.not_found", "Artifact not found: " + artifactId);
        if (!a.getUserId().equals(user.getUserId())) {
            throw new LobsterException("artifact.forbidden", "Artifact not owned by current user");
        }
        if (a.getContentRef() != null) {
            contentStore.delete(a.getContentRef());
        }
        byte[] bytes = newContent == null ? new byte[0] : newContent;
        String ref = contentStore.writeBinary("artifact", user.getUserId(), bytes, a.getFormat());
        a.setContentRef(ref);
        a.setContentSize((long) bytes.length);
        if (newTitle != null && !newTitle.isEmpty()) a.setTitle(newTitle);
        a.setVersion((a.getVersion() == null ? 1 : a.getVersion()) + 1);
        a.setUpdateTime(new Date());
        artifactDao().save(a);
        return a;
    }

    /** 读 Artifact 原始字节 / Read artifact as raw bytes. */
    public byte[] readArtifactBytes(String artifactId) throws Exception {
        Artifact a = artifactDao().getArtifact(artifactId);
        if (a == null) return null;
        if (a.getContentRef() == null) return new byte[0];
        return contentStore.readBinary(a.getContentRef());
    }

    /** 分段读 / Paginated read by byte range. */
    public byte[] readArtifactBytes(String artifactId, int offset, int limit) throws Exception {
        Artifact a = artifactDao().getArtifact(artifactId);
        if (a == null) return null;
        if (a.getContentRef() == null) return new byte[0];
        return contentStore.readBinary(a.getContentRef(), offset, limit);
    }
}
