package com.yaoai.storage.service.impl;

import com.volcengine.tos.TOSV2;
import com.volcengine.tos.comm.common.ACLType;
import com.volcengine.tos.model.object.*;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.storage.config.TosProperties;
import com.yaoai.storage.service.TosService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(name = "storage.local.enabled", havingValue = "false", matchIfMissing = true)
public class TosServiceImpl implements TosService {

    private final TOSV2 tosClient;
    private final TosProperties properties;
    private final Set<String> migratedLocalKeys = ConcurrentHashMap.newKeySet();

    @Value("${storage.local-migration.enabled:false}")
    private boolean localMigrationEnabled;

    @Value("${local-storage.base-dir:./.local-data/storage}")
    private String localStorageBaseDir;

    @Override
    public String upload(String key, InputStream content, long contentLength, String contentType) {
        ObjectMetaRequestOptions options = new ObjectMetaRequestOptions()
                .setContentType(contentType)
                .setContentDisposition("inline")
                .setAclType(ACLType.ACL_PUBLIC_READ);
        PutObjectInput input = new PutObjectInput()
                .setBucket(properties.getBucketName())
                .setKey(key)
                .setContent(content)
                .setContentLength(contentLength)
                .setOptions(options);
        tosClient.putObject(input);
        log.debug("TOS upload success: key={}, size={}", key, contentLength);
        return key;
    }

    @Override
    public void delete(String key) {
        tosClient.deleteObject(DeleteObjectInput.builder()
                .bucket(properties.getBucketName())
                .key(key)
                .build());
        log.debug("TOS delete success: key={}", key);
    }

    @Override
    public String readText(String key) {
        try {
            GetObjectV2Output output = tosClient.getObject(GetObjectV2Input.builder()
                    .bucket(properties.getBucketName())
                    .key(key)
                    .build());
            try (InputStream in = output.getContent()) {
                byte[] bytes = in.readAllBytes();
                log.debug("TOS readText: key={}, bytes={}", key, bytes.length);
                return new String(bytes, StandardCharsets.UTF_8);
            }
        } catch (IOException e) {
            log.error("TOS readText failed: key={}", key, e);
            throw new BizException(ErrorCode.INTERNAL_ERROR, "读取文件内容失败: " + e.getMessage());
        }
    }

    @Override
    public InputStream getInputStream(String key) throws IOException {
        GetObjectV2Output output = tosClient.getObject(GetObjectV2Input.builder()
                .bucket(properties.getBucketName())
                .key(key)
                .build());
        return output.getContent();
    }

    @Override
    public String presignedGetUrl(String key, long expiresSeconds) {
        migrateLocalObjectIfPresent(key);
        return tosClient.preSignedURL(PreSignedURLInput.builder()
                .httpMethod("GET")
                .bucket(properties.getBucketName())
                .key(key)
                .expires(expiresSeconds)
                .build()).getSignedUrl();
    }

    @Override
    public String publicUrl(String key) {
        migrateLocalObjectIfPresent(key);
        // bucket 须在火山引擎控制台设置为 public-read
        return String.format("https://%s.tos-%s.volces.com/%s",
                properties.getBucketName(), properties.getRegion(), key);
    }

    @Override
    public String normalizePublicUrl(String url) {
        // A simple implementation, can be expanded if needed
        return url;
    }

    private void migrateLocalObjectIfPresent(String key) {
        if (!localMigrationEnabled || key == null || key.isBlank()) {
            return;
        }
        Path root = Paths.get(localStorageBaseDir).toAbsolutePath().normalize();
        Path source = root.resolve(key).normalize();
        if (!source.startsWith(root) || !Files.isRegularFile(source)) {
            return;
        }
        if (!migratedLocalKeys.add(key)) {
            return;
        }

        try {
            byte[] bytes = Files.readAllBytes(source);
            String contentType = probeContentType(source);
            upload(key, new ByteArrayInputStream(bytes), bytes.length, contentType);
            log.info("Migrated local storage object to TOS: key={}, size={}", key, bytes.length);
        } catch (Exception e) {
            migratedLocalKeys.remove(key);
            log.warn("Local storage migration to TOS failed: key={}, reason={}", key, e.getMessage());
        }
    }

    private String probeContentType(Path path) {
        try {
            String detected = Files.probeContentType(path);
            if (detected != null && !detected.isBlank()) {
                return detected;
            }
        } catch (IOException ignored) {
            // fallback below
        }
        String name = path.getFileName().toString().toLowerCase();
        if (name.endsWith(".png")) {
            return "image/png";
        }
        if (name.endsWith(".webp")) {
            return "image/webp";
        }
        if (name.endsWith(".jpg") || name.endsWith(".jpeg")) {
            return "image/jpeg";
        }
        if (name.endsWith(".mp4")) {
            return "video/mp4";
        }
        return "application/octet-stream";
    }
}
