/*
 * 文件工具类
 * EZOpenSDK
 *
 * Created by junecheng on 2024/5/7.
 * Copyright (c) 2024年 Ezviz. All rights reserved.
 */
import fs, { ReadTextOptions, WriteOptions } from '@ohos.file.fs'
import { BusinessError } from '@kit.BasicServicesKit'
import picker from '@ohos.file.picker'
import EZLog from './EZLog'

export default class EZFileUtil {
  private constructor() {
  }

  static readonly separator: string = '/'

  /**
   * 获取文件目录下的文件夹路径或文件路径。
   * @param dirPath 文件路径；支持完整路径和相对路径（download/wps/doc）；dirPath传空字符串表示根目录
   * @param fileName 文件名（test.text）；fileName传空字符串表示文件夹路径
   * @param blHap true：HAP级别文件路径、 false：App级别文件路径
   * @returns
   */
  static getFilesDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string {
    let filePath = blHap ? getContext().filesDir : getContext().getApplicationContext().filesDir //根目录
    if (dirPath) {
      if (EZFileUtil.startsWith(dirPath, filePath)) { //路径中包含根目录，是完整路径。
        filePath = dirPath
      } else { //路径中不包含根目录，拼接成完整路径。
        filePath = filePath + EZFileUtil.separator + dirPath
      }
      if (!EZFileUtil.accessSync(filePath)) {
        EZFileUtil.mkdirSync(filePath) //如果文件夹不存在就创建
      }
    }
    if (fileName) {
      filePath = filePath + EZFileUtil.separator + fileName
    }
    return filePath
  }

  /**
   * 获取缓存目录下的文件夹路径或文件路径。
   * @param dirPath 文件路径；支持完整路径和相对路径（download/wps/doc）；dirPath传空字符串表示根目录
   * @param fileName 文件名（test.text）；fileName传空字符串表示文件夹路径
   * @param blHap true：HAP级别文件路径、 false：App级别文件路径
   * @returns
   */
  static getCacheDirPath(dirPath: string = "", fileName: string = "", blHap: boolean = true): string {
    let filePath = blHap ? getContext().cacheDir : getContext().getApplicationContext().cacheDir //根目录
    if (dirPath) {
      if (EZFileUtil.hasDirPath(dirPath)) { //路径中包含根目录，是完整路径。
        filePath = dirPath
      } else { //路径中不包含根目录，拼接成完整路径。
        filePath = filePath + EZFileUtil.separator + dirPath
      }
      if (!EZFileUtil.accessSync(filePath)) {
        EZFileUtil.mkdirSync(filePath) //如果文件夹不存在就创建
      }
    }
    if (fileName) {
      filePath = filePath + EZFileUtil.separator + fileName
    }
    return filePath
  }

  /**
   * 检查文件是否存在，以同步方法。
   * @param path 文件应用沙箱路径。
   * @returns
   */
  static accessSync(path: string): boolean {
    return fs.accessSync(path)
  }

  /**
   * 判断是否是完整路径
   * @param path 文件路径
   */
  static hasDirPath(path: string): boolean {
    return EZFileUtil.startsWith(path, "/data/storage/el1/") || EZFileUtil.startsWith(path, "/data/storage/el2/")
  }

  /**
   * 创建目录以同步方法，当recursion指定为true，可多层级创建目录。
   * @param path 目录的应用沙箱路径。
   * @param recursion 是否多层级创建目录。recursion指定为true时，可多层级创建目录。recursion指定为false时，仅可创建单层目录。
   */
  static mkdirSync(path: string, recursion: boolean = true) {
    if (recursion) {
      fs.mkdirSync(path, recursion)
    } else {
      fs.mkdirSync(path)
    }
  }

  /**
   * 将数据写入文件，并关闭文件。
   * @param path string 文件的应用沙箱路径或URI。
   * @param buffer ArrayBuffer|string 待写入文件的数据，可来自缓冲区或字符串。
   * @param append 是否追加，true-追加，false-不追加（直接覆盖）
   * @returns
   */
  static async writeEasy(path: string, buffer: ArrayBuffer | string, append: boolean = true): Promise<number> {
    try {
      let file = EZFileUtil.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
      let offset = append ? EZFileUtil.statSync(file.fd).size : 0
      let options: WriteOptions = {
        offset: offset, encoding: 'utf-8'
      }
      return await EZFileUtil.write(file.fd, buffer, options).finally(() => {
        EZFileUtil.close(file.fd) //关闭文件
      })
    } catch (err) {
      let error = err as BusinessError
      EZLog.error(`EZFileUtil-writeEasy-error: code: ${error.code}  message: ${error.message}`)
      return -1
    }
  }

  /**
   * 打开文件，支持使用URI打开文件。以同步方法。
   * @param path string 文件的应用沙箱路径或URI。
   * @param mode number 打开文件的选项，必须指定如下选项中的一个，默认以只读方式打开。
   * @returns
   */
  static openSync(path: string, mode: number = fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE): fs.File {
    return fs.openSync(path, mode)
  }

  /**
   * 获取文件详细属性信息，以同步方法。
   * @param file string|number 文件应用沙箱路径path或已打开的文件描述符fd。
   * @returns
   */
  static statSync(file: string | number): fs.Stat {
    return fs.statSync(file)
  }

  /**
   * 将数据写入文件，使用Promise异步回调。
   * @param fd number 已打开的文件描述符。
   * @param buffer ArrayBuffer|string 待写入文件的数据，可来自缓冲区或字符串。
   * @param options 支持如下选项：
   *                 offset，number类型，表示期望写入文件的位置。可选，默认从当前位置开始写。
   *                 length，number类型，表示期望写入数据的长度。可选，默认缓冲区长度。
   *                 encoding，string类型，当数据是string类型时有效，表示数据的编码方式，默认 'utf-8'。当前仅支持 'utf-8'。
   * @returns
   */
  static write(fd: number, buffer: ArrayBuffer | string, options?: WriteOptions): Promise<number> {
    return fs.write(fd, buffer, options)
  }


  /**
   * 关闭文件，使用Promise异步回调。
   * @param file 已打开的File对象或已打开的文件描述符fd。
   * @returns
   */
  static close(file: fs.File | number): Promise<void> {
    return fs.close(file)
  }


  /**
   * 复制文件，使用Promise异步回调。
   * @param src string|number 待复制文件的路径或待复制文件的文件描述符。
   * @param dest string|number 目标文件路径或目标文件的文件描述符。
   * @param mode number 提供覆盖文件的选项，当前仅支持0，且默认为0。0：完全覆盖目标文件。
   * @returns
   */
  static copyFile(src: string | number, dest: string | number, mode: number = 0): Promise<void> {
    return fs.copyFile(src, dest, mode)
  }

  /**
   * 基于文本方式读取文件（即直接读取文件的文本内容），使用Promise异步回调。
   * @param filePath 文件的应用沙箱路径。
   * @param options 支持如下选项：
   *                 offset，number类型，表示期望读取文件的位置。可选，默认从当前位置开始读取。
   *                 length，number类型，表示期望读取数据的长度。可选，默认文件长度。
   *                 encoding，string类型，当数据是 string 类型时有效，表示数据的编码方式，默认 'utf-8'，仅支持 'utf-8'。
   * @returns
   */
  static readText(filePath: string, options?: ReadTextOptions): Promise<string> {
    return fs.readText(filePath, options)
  }


  /**
   * 通过保存模式拉起documentPicker界面，用户可以保存一个或多个文件。
   * @param options
   * @returns
   */
  static async saveDocument(newFileNames?: Array<string>): Promise<Array<string>> {
    try {
      let documentPicker = new picker.DocumentViewPicker()
      if (newFileNames == undefined || newFileNames == null || newFileNames.length == 0) {
        return await documentPicker.save()
      } else {
        let documentSaveOptions = new picker.DocumentSaveOptions()
        documentSaveOptions.newFileNames = newFileNames
        return await documentPicker.save(documentSaveOptions)
      }
    } catch (err) {
      let error = err as BusinessError
      EZLog.error(`EZFileUtil-saveDocument-error code: ${error.code} message: ${error.message}`)
      return []
    }
  }

  /**
   * 检查字符串是否以给定的字符串开头
   * @param string 要检索的字符串
   * @param target 要检索字符
   * @param position 检索的位置
   * @returns 如果字符串以字符串开头，那么返回 true，否则返回 false
   */
  static startsWith(string: string = '', target: string, position: number = 0): boolean {
    return string.startsWith(target, position)
  }
}