package com.ycwl.basic.storage.adapters;

import com.ycwl.basic.storage.entity.StorageConfig;
import com.ycwl.basic.storage.entity.StorageFileObject;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.storage.exceptions.StorageConfigException;
import com.ycwl.basic.storage.exceptions.StorageException;
import com.ycwl.basic.storage.exceptions.UploadFileFailedException;
import com.ycwl.basic.storage.utils.StorageUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

public class LocalStorageAdapter extends AStorageAdapter {
    private volatile String basePath;
    private volatile String baseUrl;
    private volatile String prefix = "";
    private final Object configLock = new Object();

    @Override
    public String identity() {
        return "LOCAL_STORAGE";
    }

    @Override
    public void loadConfig(Map<String, String> config) {
        String newBasePath = config.get("basePath");
        String newBaseUrl = config.get("baseUrl");
        String newPrefix = StringUtils.isNotBlank(config.get("prefix")) ? config.get("prefix") : "";
        
        if (StringUtils.isBlank(newBasePath)) {
            throw new StorageConfigException("本地存储配置错误：basePath 不能为空");
        }
        if (StringUtils.isBlank(newBaseUrl)) {
            throw new StorageConfigException("本地存储配置错误：baseUrl 不能为空");
        }
        
        // 确保基础目录存在
        try {
            Files.createDirectories(Paths.get(newBasePath));
        } catch (IOException e) {
            throw new StorageConfigException("创建本地存储目录失败：" + e.getMessage());
        }
        
        synchronized (configLock) {
            this.basePath = newBasePath;
            this.baseUrl = newBaseUrl;
            this.prefix = newPrefix;
        }
    }

    @Override
    public void setConfig(StorageConfig config) {
        throw new StorageConfigException("LocalStorageAdapter 不支持 StorageConfig 类型配置");
    }

    @Override
    public String uploadFile(String contentType, InputStream inputStream, String... path) {
        if (inputStream == null) {
            return null;
        }
        
        String fullPath = buildLocalPath(path);
        try {
            // 确保父目录存在
            Path filePath = Paths.get(fullPath);
            Files.createDirectories(filePath.getParent());
            
            // 复制文件内容到本地
            Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
            
            return getUrl(path);
        } catch (IOException e) {
            throw new UploadFileFailedException("本地文件保存失败：" + e.getMessage());
        }
    }

    @Override
    public boolean deleteFile(String... path) {
        try {
            String fullPath = buildLocalPath(path);
            return Files.deleteIfExists(Paths.get(fullPath));
        } catch (IOException e) {
            return false;
        }
    }

    @Override
    public String getUrl(String... path) {
        String relativePath = buildRelativePath(path);
        return baseUrl + "/" + relativePath;
    }

    @Override
    public String getUrlForDownload(Date expireDate, String... path) {
        // 本地存储不支持带过期时间的URL，直接返回普通URL
        return getUrl(path);
    }

    @Override
    public String getUrlForUpload(Date expireDate, String contentType, String... path) {
        // 本地存储不支持预签名上传URL
        return getUrl(path);
    }

    @Override
    public List<StorageFileObject> listDir(String... path) {
        String fullPath = buildLocalPath(path);
        Path dirPath = Paths.get(fullPath);
        
        if (!Files.exists(dirPath) || !Files.isDirectory(dirPath)) {
            return new ArrayList<>();
        }
        
        List<StorageFileObject> result = new ArrayList<>();
        try (Stream<Path> paths = Files.list(dirPath)) {
            paths.forEach(filePath -> {
                try {
                    StorageFileObject obj = new StorageFileObject();
                    obj.setName(filePath.getFileName().toString());
                    obj.setPath(getRelativePath(filePath.getParent().toString()));
                    obj.setSize(Files.size(filePath));
                    obj.setModifyTime(new Date(Files.getLastModifiedTime(filePath).toMillis()));
                    obj.setRawObject(filePath.toFile());
                    result.add(obj);
                } catch (IOException e) {
                    // 忽略无法读取的文件
                }
            });
        } catch (IOException e) {
            throw new StorageException("列举本地文件失败：" + e.getMessage());
        }
        
        return result;
    }

    @Override
    public boolean deleteDir(String... path) {
        String fullPath = buildLocalPath(path);
        Path dirPath = Paths.get(fullPath);
        
        if (!Files.exists(dirPath)) {
            return true;
        }
        
        try {
            // 递归删除目录及其内容
            try (Stream<Path> paths = Files.walk(dirPath)) {
                paths.sorted((a, b) -> b.compareTo(a)) // 从深到浅排序，确保先删除文件再删除目录
                     .forEach(filePath -> {
                         try {
                             Files.deleteIfExists(filePath);
                         } catch (IOException e) {
                             // 忽略删除失败的文件
                         }
                     });
            }
            return !Files.exists(dirPath);
        } catch (IOException e) {
            return false;
        }
    }

    @Override
    public boolean setAcl(StorageAcl acl, String... path) {
        // 本地存储不支持ACL设置
        return true;
    }

    @Override
    public boolean isExists(String... path) {
        String fullPath = buildLocalPath(path);
        return Files.exists(Paths.get(fullPath));
    }
    
    private String buildLocalPath(String... paths) {
        String relativePath = buildRelativePath(paths);
        return Paths.get(basePath, relativePath).toString();
    }
    
    private String buildRelativePath(String... paths) {
        if (StringUtils.isNotBlank(prefix)) {
            return StorageUtil.joinPath(prefix, paths);
        } else {
            return StorageUtil.joinPath(paths);
        }
    }
    
    private String getRelativePath(String fullPath) {
        String basePathNormalized = Paths.get(basePath).toString();
        if (fullPath.startsWith(basePathNormalized)) {
            String relative = fullPath.substring(basePathNormalized.length());
            if (relative.startsWith(File.separator)) {
                relative = relative.substring(1);
            }
            return StorageUtil.getRelativePath(relative, prefix);
        }
        return fullPath;
    }
}
