///|
/// 本地文件系统实现:文档映射到 `root/slug.md`。
struct LocalStorage {
  root : String
}

///|
pub fn LocalStorage::new(root : String) -> LocalStorage {
  { root, }
}

///|
fn LocalStorage::path_for(self : LocalStorage, slug : String) -> String {
  self.root + "/" + slug + ".md"
}

///|
/// slug 路径安全校验:只允许字母数字、`-`、`_`、`/`,
/// 禁止 `..`/`.` 段逃逸和绝对路径。
pub fn valid_slug(slug : String) -> Bool {
  if slug.length() == 0 || slug[0] == '/' {
    return false
  }
  for part in slug.split("/") {
    if part == ".." || part == "." || part.length() == 0 {
      return false
    }
    for c in part {
      if !((c >= 'a' && c <= 'z') ||
        (c >= 'A' && c <= 'Z') ||
        (c >= '0' && c <= '9') ||
        c == '-' ||
        c == '_') {
        return false
      }
    }
  }
  true
}

///|
pub extend LocalStorage with Storage::{read, write, delete, list}

///|
pub impl Storage for LocalStorage with fn read(self, slug : String) -> String? {
  if !valid_slug(slug) {
    return None
  }
  let path = self.path_for(slug)
  guard @fs.exists(path) else { return None }
  let file = @fs.open(path, mode=ReadOnly)
  defer file.close()
  let bytes = file.read_exactly(file.size().to_int())
  Some(@utf8.decode_lossy(bytes[:]))
}

///|
pub impl Storage for LocalStorage with fn write(
  self,
  slug : String,
  content : String,
) -> Unit {
  if !valid_slug(slug) {
    raise StorageError::InvalidSlug(slug)
  }
  let path = self.path_for(slug)
  let parent = match path.rev_find("/") {
    Some(i) => path[0:i + 1]
    None => ""
  }
  if parent.length() > 0 && !@fs.exists(parent) {
    @fs.mkdir(parent, recursive=true)
  }
  @fs.write_file(path, content)
}

///|
pub impl Storage for LocalStorage with fn delete(self, slug : String) -> Unit {
  if !valid_slug(slug) {
    return
  }
  let path = self.path_for(slug)
  guard @fs.exists(path) else { return }
  @fs.remove(path)
}

///|
pub impl Storage for LocalStorage with fn list(self) -> Array[String] {
  let slugs : Array[String] = []
  @fs.walk(self.root, (dir, entries) => {
    let rel = dir[self.root.length():]
    let dir_slug = (if rel.has_prefix("/") { rel[1:] } else { rel }).to_owned()
    for entry in entries {
      if entry.has_suffix(".md") {
        let name = entry[0:entry.length() - 3].to_owned()
        let slug = if dir_slug.length() == 0 {
          name
        } else {
          dir_slug + "/" + name
        }
        slugs.push(slug)
      }
    }
  })
  slugs
}

///|
/// 存储层错误
pub(all) suberror StorageError {
  InvalidSlug(String)
}