package com.xmomen.module.resource.api;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.csource.common.IniFileReader;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

/**
 * FastDfsService 类实现了 DfsService 接口，用于与 FastDFS 分布式文件系统进行交互。
 * 该类采用单例模式，确保在整个应用中只有一个实例与 FastDFS 进行通信。
 */
public class FastDfsService implements DfsService {

    // 单例实例
    private static FastDfsService instance = null;

    // 基础 URL，用于构建文件的完整 HTTP 访问路径
    private String baseUrl = "";
    // DMS 基础 URL，用于构建 DMS 文件的完整 HTTP 访问路径
    private String dmsBaseUrl = "";
    // 默认图片 URL
    private String defaultPicUrl = "";

    // 配置文件中基础 URL 的键名
    private static String BASE_URL = "httpBaseUrl";
    // 配置文件中 DMS 基础 URL 的键名
    private static String DMS_BASE_URL = "dmsBaseUrl";
    // 配置文件中默认图片 URL 的键名
    private static String DEFAULT_PIC_URL = "defaultPicUrl";

    // FastDFS 键名，用于元数据中存储文件的唯一标识
    private static String FAST_DFS_KEY = "FastDfsKey";

    /**
     * 存储文件的桶名
     */
    private final String bucketName;

    /**
     * 私有构造函数，用于初始化 FastDfsService 实例。
     * 该构造函数会读取 FastDFS 配置文件，并初始化客户端全局配置。
     *
     * @param bucketName 存储文件的桶名
     */
    private FastDfsService(String bucketName) {
        this.bucketName = bucketName;
        String configFilePath = null;
        try {
            // 获取 FastDFS 配置文件的路径
            configFilePath = FastDfsService.class.getResource("/").toURI().getPath() + "fdfs_client.conf";
        } catch (URISyntaxException e1) {
            // 若无法找到配置文件，抛出异常并处理
            Exception e = new Exception("Cann't found fdfs_client.conf file under " + FastDfsService.class.getResource("/"));
            handleException(e);
        }
        try {
            // 初始化 FastDFS 客户端全局配置
            ClientGlobal.init(configFilePath);
            // 读取配置文件
            IniFileReader reader = new IniFileReader(configFilePath);
            // 设置基础 URL
            setBaseUrl(reader.getStrValue(BASE_URL));
            // 设置 DMS 基础 URL
            setDmsBaseUrl(reader.getStrValue(DMS_BASE_URL));
            // 设置默认图片 URL
            setDefaultPicUrl(reader.getStrValue(DEFAULT_PIC_URL));
        } catch (Exception e) {
            // 处理初始化过程中出现的异常
            handleException(e);
        }
    }

    /**
     * 获取 FastDfsService 单例实例的方法。
     * 采用双重检查锁定机制确保线程安全。
     *
     * @param bucketName 存储文件的桶名
     * @return FastDfsService 单例实例
     */
    public static synchronized FastDfsService getInstance(String bucketName) {
        if (null == instance) {
            instance = new FastDfsService(bucketName);
        }
        return instance;
    }

    /**
     * 从文件系统中删除指定的文件。
     *
     * @param remotePath 远程文件路径
     * @param key        文件的键，在 FastDFS 中未使用
     * @return 删除成功返回 true，失败返回 false
     */
    @Override
    public boolean deleteObject(String remotePath, String key) {
        int result = 0;
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = null;
        try {
            // 获取 Tracker 服务器连接
            trackerServer = trackerClient.getConnection();
            // 创建存储客户端
            StorageClient storageClient = new StorageClient(trackerServer, null);
            // 调用 FastDFS API 删除文件
            result = storageClient.delete_file(bucketName, remotePath);
        } catch (Exception e) {
            // 处理删除过程中出现的异常
            handleException(e);
        } finally {
            if (null != trackerServer) {
                try {
                    // 关闭 Tracker 服务器连接
                    trackerServer.close();
                } catch (Exception e) {
                    // 处理关闭连接时出现的异常
                    handleException(e);
                }
            }
        }
        if (0 != result) {
            // 删除失败，记录日志并返回 false
            //logger.info("delete faild, the faild code is: " + result);
            return false;
        }
        return true;
    }

    /**
     * 根据远程路径构建文件的完整 HTTP 访问路径。
     *
     * @param remotePath 远程文件路径
     * @return 文件的完整 HTTP 访问路径
     */
    @Override
    public String getHttpPath(String remotePath) {
        return baseUrl + "/" + bucketName + "/" + remotePath;
    }

    /**
     * 根据 DMS 文件路径构建其完整的 HTTP 访问路径。
     *
     * @param dmsFilePath DMS 文件路径
     * @return DMS 文件的完整 HTTP 访问路径
     */
    @Override
    public String getDmsHttpPath(String dmsFilePath) {
        return dmsBaseUrl + dmsFilePath;
    }

    /**
     * 从文件系统中获取指定的文件。
     *
     * @param remotePath 远程文件路径
     * @param key        文件的键，在 FastDFS 中未使用，可以为 null
     * @return 若获取成功，返回 DfsFile 对象；否则返回 null
     */
    @Override
    public DfsFile getObject(String remotePath, String key) {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = null;
        StorageServer storageServer = null;
        try {
            // 获取 Tracker 服务器连接
            trackerServer = trackerClient.getConnection();
            // 获取存储服务器
            storageServer = trackerClient.getFetchStorage(trackerServer, bucketName, remotePath);
            // 创建存储客户端
            StorageClient storageClient = new StorageClient(trackerServer, storageServer);
            // 下载文件
            byte[] bytes = storageClient.download_file(this.bucketName, remotePath);
            if (null != bytes) {
                // 获取文件的元数据
                NameValuePair[] value_pairs = storageClient.get_metadata(bucketName, remotePath);
                Map<String, String> metadata = new HashMap<String, String>();
                for (int i = 0; i < value_pairs.length; i++) {
                    // 将元数据存储到 Map 中
                    metadata.put(value_pairs[i].getName(), value_pairs[i].getValue());
                }
                // 创建 DfsFile 对象
                DfsFile dfsFile = new DfsFile(metadata.get(FAST_DFS_KEY), bytes, metadata);
                return dfsFile;
            }
        } catch (Exception e) {
            // 处理获取文件过程中出现的异常
            handleException(e);
        } finally {
            if (null != storageServer) {
                try {
                    // 关闭存储服务器连接
                    storageServer.close();
                } catch (Exception e) {
                    // 处理关闭连接时出现的异常
                    handleException(e);
                }
            }
            if (null != trackerServer) {
                try {
                    // 关闭 Tracker 服务器连接
                    trackerServer.close();
                } catch (Exception e) {
                    // 处理关闭连接时出现的异常
                    handleException(e);
                }
            }
        }
        // 获取文件失败，记录日志并返回 null
        //logger.info("Get object failed, get null object");
        return null;
    }

    /**
     * 获取文件系统中的文件列表。
     * 目前由于 FastDFS API 不支持，该方法未实现。
     *
     * @return 始终返回 null
     */
    @Override
    public List<DfsFile> listObjects() {
        // 待实现
        // 因为 FastDFS API 不支持列出对象的方法
        return null;
    }

    /**
     * 通过文件路径将文件上传到文件系统。
     * 该方法会将文件转换为 File 对象，然后调用 putObject(File file, String key, Map<String, String> metadata) 方法进行上传。
     *
     * @param filePath  本地文件路径
     * @param key       文件的键，在 FastDFS 中未使用，可以为 null
     * @param metadata  文件的元数据，可以为 null
     * @return 若上传成功，返回 DfsPath 对象；否则返回 null
     */
    @Override
    public DfsPath putObject(String filePath, String key, Map<String, String> metadata) {
        File file = new File(filePath);
        return putObject(file, key, metadata);
    }

    /**
     * 通过字节数组将文件上传到文件系统。
     *
     * @param bytes     文件的字节数组
     * @param key       文件的键，在 FastDFS 中未使用
     * @param metadata  文件的元数据，可以为 null
     * @param extName   文件的扩展名，可以为 null
     * @return 若上传成功，返回 DfsPath 对象；否则返回 null
     */
    @Override
    public DfsPath putObject(byte[] bytes, String key, Map<String, String> metadata, String extName) {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = null;
        StorageServer storageServer = null;
        try {
            // 获取 Tracker 服务器连接
            trackerServer = trackerClient.getConnection();
            // 获取存储服务器列表
            StorageServer[] storageServers = trackerClient.getStoreStorages(trackerServer, bucketName);
            if (null != storageServers) {
                // 选择第一个存储服务器
                storageServer = storageServers[0];
                // 创建存储客户端
                StorageClient storageClient = new StorageClient(trackerServer, storageServer);

                NameValuePair[] meta_list;
                int i = 0;
                if (null == metadata) {
                    // 若元数据为空，创建一个包含一个元素的元数据数组
                    meta_list = new NameValuePair[1];
                } else {
                    // 若元数据不为空，创建一个包含元数据和 FastDFS 键的数组
                    meta_list = new NameValuePair[metadata.size() + 1];
                    for (Map.Entry<String, String> entry : metadata.entrySet()) {
                        meta_list[i++] = new NameValuePair(entry.getKey(), entry.getValue());
                    }
                }
                // 添加 FastDFS 键到元数据数组
                meta_list[i] = new NameValuePair(FAST_DFS_KEY, key);

                // 调用 FastDFS API 上传文件
                String[] results = storageClient.upload_file(bytes, extName, meta_list);

                if (null == results) {
                    // 上传失败，记录日志并返回 null
                    //logger.info("upload file fail, error codes: " + storageClient.getErrorCode());
                    return null;
                } else {
                    // 获取远程文件名
                    String remote_fileName = results[1];
                    // 构建文件的完整 HTTP 访问路径
                    String httpPath = this.getHttpPath(remote_fileName);
                    // 创建 DfsPath 对象
                    DfsPath dfsPath = new DfsPath(httpPath, remote_fileName);
                    return dfsPath;
                }
            }
        } catch (Exception e) {
            // 处理上传过程中出现的异常
            handleException(e);
        } finally {
            if (null != storageServer) {
                try {
                    // 关闭存储服务器连接
                    storageServer.close();
                } catch (Exception e) {
                    // 处理关闭连接时出现的异常
                    handleException(e);
                }
            }
            if (null != trackerServer) {
                try {
                    // 关闭 Tracker 服务器连接
                    trackerServer.close();
                } catch (Exception e) {
                    // 处理关闭连接时出现的异常
                    handleException(e);
                }
            }
        }
        // 上传失败，记录日志并返回 null
        //logger.info("Upload file faild, because can not get storage servers!");
        return null;
    }

    /**
     * 将文件上传到文件系统。
     * 该方法会将文件转换为字节数组，然后调用 putObject(byte[] bytes, String key, Map<String, String> metadata, String extName) 方法进行上传。
     *
     * @param file      文件对象
     * @param key       文件的键，在 FastDFS 中未使用，可以为 null
     * @param metadata  文件的元数据，可以为 null
     * @return 若上传成功，返回 DfsPath 对象；否则返回 null
     */
    @Override
    public DfsPath putObject(File file, String key, Map<String, String> metadata) {
        FileInputStream in = null;
        byte[] file_buff = null;
        try {
            // 打开文件输入流
            in = new FileInputStream(file);
            if (null != in) {
                // 获取文件长度
                int len = in.available();
                // 创建字节数组
                file_buff = new byte[len];
                // 读取文件内容到字节数组
                in.read(file_buff);
            }
        } catch (Exception e) {
            // 处理读取文件过程中出现的异常
            handleException(e);
        } finally {
            if (null != in) {
                try {
                    // 关闭文件输入流
                    in.close();
                } catch (Exception e) {
                    // 处理关闭流时出现的异常
                    handleException(e);
                }
            }
        }
        String file_ext_name = "";
        if (file.getName().lastIndexOf(".") > 0) {
            // 获取文件扩展名
            file_ext_name = file.getName().substring(file.getName().lastIndexOf(".") + 1);
        }
        // 调用 putObject(byte[] bytes, String key, Map<String, String> metadata, String extName) 方法上传文件
        return putObject(file_buff, key, metadata, file_ext_name);
    }

    /**
     * 获取基础 URL。
     *
     * @return 基础 URL
     */
    public String getBaseUrl() {
        return baseUrl;
    }

    /**
     * 设置基础 URL。
     *
     * @param baseUrl 要设置的基础 URL
     */
    private void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * 获取 DMS 基础 URL。
     *
     * @return DMS 基础 URL
     */
    public String getDmsBaseUrl() {
        return dmsBaseUrl;
    }

    /**
     * 设置 DMS 基础 URL。
     *
     * @param dmsBaseUrl 要设置的 DMS 基础 URL
     */
    public void setDmsBaseUrl(String dmsBaseUrl) {
        this.dmsBaseUrl = dmsBaseUrl;
    }

    /**
     * 获取默认图片 URL。
     *
     * @return 默认图片 URL
     */
    public String getDefaultPicUrl() {
        return defaultPicUrl;
    }

    /**
     * 设置默认图片 URL。
     *
     * @param defaultPicUrl 要设置的默认图片 URL
     */
    public void setDefaultPicUrl(String defaultPicUrl) {
        this.defaultPicUrl = defaultPicUrl;
    }

    /**
     * 处理异常的方法。
     * 根据异常类型抛出不同的 DfsException 异常。
     *
     * @param e 要处理的异常
     */
    private void handleException(Exception e) {
        if (e instanceof IOException) {
            // 处理 IOException，抛出 DfsException，错误码为 100
            //logger.error("Exception occured : DFSException code: 100,"  + " exception message :" + e.getMessage());
            throw new DfsException("100", e.getMessage());
        } else if (e instanceof FileNotFoundException) {
            // 处理 FileNotFoundException，抛出 DfsException，错误码为 200
            //logger.error("Exception occured : DFSException code: 200,"  + " exception message : file not found." + e.getMessage());
            throw new DfsException("200", e.getMessage());
        } else if (e instanceof MyException) {
            // 处理 MyException，抛出 DfsException，错误码为 300
            //logger.error("Exception occured : DFSException code: 300,"  + " exception message :" + e.getMessage());
            throw new DfsException("300", e.getMessage());
        } else if (e instanceof Exception) {
            // 处理其他异常，抛出 DfsException，错误码为 400
            //logger.error("Exception occured : DFSException code: 400,"  + " exception message :" + e.getMessage());
            throw new DfsException("400", e.getMessage());
        }
    }

    /**
     * 根据类型获取默认路径。
     * 如果类型为 "PICTURE"，则返回默认图片 URL；否则也返回默认图片 URL。
     *
     * @param type 路径类型
     * @return 默认路径
     */
    @Override
    public String getDefaultPath(String type) {
        if ("PICTURE".equalsIgnoreCase(type)) {
            return defaultPicUrl;
        }
        return defaultPicUrl;
    }
}