package tech.easyflow.skill.store;

import com.easyagents.skill.store.SkillContentStore;
import com.easyagents.skill.util.SkillHashes;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import tech.easyflow.common.filestorage.FileStorageService;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.skill.entity.SkillAssetContent;
import tech.easyflow.skill.service.SkillAssetContentService;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

/**
 * 基于数据库索引和现有文件存储的 Skill 内容存储。
 */
@Component
public class DBSkillContentStore implements SkillContentStore {

    private static final String DEFAULT_MEDIA_TYPE = "application/octet-stream";

    private final SkillAssetContentService skillAssetContentService;
    private final FileStorageService fileStorageService;

    /**
     * 创建数据库 Skill 内容存储。
     *
     * @param skillAssetContentService asset 内容索引服务
     * @param fileStorageService 文件存储服务
     */
    public DBSkillContentStore(SkillAssetContentService skillAssetContentService,
                               @Qualifier("default") FileStorageService fileStorageService) {
        this.skillAssetContentService = skillAssetContentService;
        this.fileStorageService = fileStorageService;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String put(byte[] bytes) {
        String contentRef = SkillHashes.sha256Ref(bytes);
        SkillAssetContent existing = skillAssetContentService.getById(contentRef);
        if (existing != null) {
            existing.setRefCount((existing.getRefCount() == null ? 0 : existing.getRefCount()) + 1);
            existing.setModified(new Date());
            skillAssetContentService.updateById(existing);
            return contentRef;
        }
        String hex = contentRef.substring("sha256:".length());
        String prePath = "skill-assets/" + hex.substring(0, Math.min(2, hex.length()));
        MultipartFile file = new ByteArrayMultipartFile(bytes, contentRef + ".bin", DEFAULT_MEDIA_TYPE);
        String filePath = fileStorageService.save(file, prePath);
        SkillAssetContent content = new SkillAssetContent();
        content.setContentRef(contentRef);
        content.setContentHash(hex);
        content.setFilePath(filePath);
        content.setMediaType(DEFAULT_MEDIA_TYPE);
        content.setSize((long) (bytes == null ? 0 : bytes.length));
        content.setRefCount(1);
        content.setCreated(new Date());
        content.setModified(new Date());
        skillAssetContentService.save(content);
        return contentRef;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public InputStream open(String contentRef) {
        SkillAssetContent content = requireContent(contentRef);
        try {
            return fileStorageService.readStream(content.getFilePath());
        } catch (IOException e) {
            throw new BusinessException("读取 Skill asset 失败");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public byte[] readAllBytes(String contentRef) {
        try (InputStream inputStream = open(contentRef)) {
            return inputStream.readAllBytes();
        } catch (IOException e) {
            throw new BusinessException("读取 Skill asset 失败");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean exists(String contentRef) {
        if (contentRef == null || contentRef.isBlank()) {
            return false;
        }
        return skillAssetContentService.count(QueryWrapper.create().eq(SkillAssetContent::getContentRef, contentRef)) > 0;
    }

    private SkillAssetContent requireContent(String contentRef) {
        SkillAssetContent content = skillAssetContentService.getById(contentRef);
        if (content == null) {
            throw new BusinessException("Skill asset 内容不存在");
        }
        return content;
    }

    private static class ByteArrayMultipartFile implements MultipartFile {

        private final byte[] bytes;
        private final String filename;
        private final String contentType;

        private ByteArrayMultipartFile(byte[] bytes, String filename, String contentType) {
            this.bytes = bytes == null ? new byte[0] : bytes;
            this.filename = filename;
            this.contentType = contentType;
        }

        @Override public String getName() { return "file"; }
        @Override public String getOriginalFilename() { return filename; }
        @Override public String getContentType() { return contentType; }
        @Override public boolean isEmpty() { return bytes.length == 0; }
        @Override public long getSize() { return bytes.length; }
        @Override public byte[] getBytes() { return bytes; }
        @Override public InputStream getInputStream() { return new ByteArrayInputStream(bytes); }
        @Override public void transferTo(File dest) throws IOException { org.springframework.util.FileCopyUtils.copy(bytes, dest); }
    }
}
