import { Stream } from 'stream';
import { Container, Scope } from 'typescript-ioc';
import { AliOssService } from './AliOss';
import { HuaWeiObsService } from './HuaWeiObs';

export type ListQuery = {
    /**
     * 对象名前缀
     */
    prefix?: string,
    marker?: string,
    delimiter?: string,

    /**
     * 最大对象条数
     * 
     * ali-oss默认100,华为obs默认1000
     */
    maxKeys?: any
}

export type ListResponse = {
    name: string,
    [prop: string]: any
}[];

/**
 * OSS 对象存储服务(Object Storage Service)
 */
export abstract class OssService {
    /**
     * 获取对象存放根目录
     * 
     * `return process.env["KT_PID"] || IocConfigProvider.getBasicConfig()?.tapdId || 'non-standard';`
     */
    abstract getRootPath(): string;

    /**
     * 上传对象到OSS服务器
     * 
     * ```
     * //文件路劲
     * let filePath = path.normalize('D:\\1.rar');
     * let url = await this.ossService.put(filePath, 'ReportExcel/111111.rar');
     * 
     * //Buffer
     * let url = await this.ossService.put(Buffer.from('xxx'), 'ReportExcel/111111.rar');
     * 
     * //stream
     * let stream = await downloadFileStream('https://image-cn.keytop.cn/452/172.19.2.6/2022/08/25/20220825143509132_172.19.2.6_024231.jpg');
     * let url = await putFile(stream, `${uuid()}.jpg`);
     * ```
     * **注：如果对象名以/开头，在查询、删除对象时，ali-oss对象名不能以`/`开头(上传忽略掉开头/)，华为obs对象名必须以'/'开头，需要注意**
     * @param data 对象,`华为obs`不直接支持buffer(buffer转stream)，所以data建议使用stream
     * @param name 对象名，如果对象名不包含根目录，那么会自动加上根目录 {@link getRootPath} `rootPath/name`
     */
    abstract put(file: string | Buffer | Stream, name: string): Promise<string>;

    /**
     * 查询对象
     * 
     * ```
     * let lsit = this.ossService.list({ prefix: 'ReportExcel2', maxKeys: 5 });
     * ```
     * **注：如果对象名以/开头，在查询、删除对象时，ali-oss对象名不能以`/`开头(上传忽略掉开头/)，华为obs对象名必须以'/'开头，需要注意**
     */
    abstract list(query?: ListQuery): Promise<ListResponse>;

    /**
     * 删除对象
     * 
     * ```
     * await this.ossService.delete('123456/1.rar');
     * ```
     * **注：如果对象名以/开头，在查询、删除对象时，ali-oss对象名不能以`/`开头(上传忽略掉开头/)，华为obs对象名必须以'/'开头，需要注意**
     * @return 平台接口返回值
     */
    abstract delete(name: string): Promise<any>;
}

if (process.env['KT_CLOUD_TYPE']?.toLocaleUpperCase() == 'HUAWEI') {
    Container.bind(OssService).to(HuaWeiObsService).scope(Scope.Singleton);
} else {
    Container.bind(OssService).to(AliOssService).scope(Scope.Singleton);
}

/**
 * 上传base64到`OSS`服务器
 * 
 * **注：华为云容器(process.env['KT_CLOUD_TYPE']?.toLocaleUpperCase() == 'HUAWEI')自动使用华为obs服务，非华为云容器使用ali-oss服务**
 * ```
 * import { downloadFile2Base64, putBase64File, uuid } from "refs/nslib";
 * 
 * let base64 = await downloadFile2Base64('https://image-cn.keytop.cn/452/172.19.2.6/2022/08/25/20220825143509132_172.19.2.6_024231.jpg');
 * let url = await putBase64File(base64, `${uuid()}.jpg`);
 * ```
 * @param data base64字符串
 * @param name 对象名,如果对象名不包含根目录，默认会加上根目录 {@link getRootPath}
 * @return 对象访问地址
 * @throw {@link Error}
 */
export async function putBase64File(data: string, name: string) {
    let buf = null;

    //data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAA
    if (data.startsWith('data:')) {
        buf = Buffer.from(data.split(',')[1], 'base64');
    } else {
        buf = Buffer.from(data, 'base64');
    }

    return await putFile(buf, name);
}

/**
 * 上传对象到`OSS`服务
 * 
 * 注：
 * 
 * 1.华为云容器(process.env['KT_CLOUD_TYPE']?.toLocaleUpperCase() == 'HUAWEI')自动使用华为obs服务，非华为云容器使用ali-oss服务**
 * 
 * 2.如果是使用华为云需要手动添加依赖：esdk-obs-nodejs
 * 
 * ```
 * import { downloadFileStream, putFile, uuid } from "refs/nslib";
 * 
 * //文件路劲
 * let filePath = path.normalize('D:\\1.rar');
 * let url = await this.ossService.put(filePath, 'ReportExcel/111111.rar');
 * 
 * //Buffer 
 * let url = await this.ossService.put(Buffer.from('xxx'), 'ReportExcel/111111.rar');
 * 
 * //stream
 * let stream = await downloadFileStream('https://image-cn.keytop.cn/452/172.19.2.6/2022/08/25/20220825143509132_172.19.2.6_024231.jpg');
 * let url = await putFile(stream, `${uuid()}.jpg`);
 * ```
 * @param name 对象名,如果对象名不包含根目录，默认会加上根目录 {@link getRootPath}
 * @return 对象地址
 */
export async function putFile(file: string | Buffer | Stream, name: string): Promise<string> {
    let service = Container.get(OssService);
    return await service.put(file, name);
}