// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
pub struct EscapeMangler {}

///|
pub(all) enum ManglerKind {
  Escape
  Hash
  Unicode
  None
} derive(Eq, Debug)

///|
pub fn ManglerKind::default() -> ManglerKind {
  Escape
}

///|
#warnings("-unnecessary_annotation")
pub fn EscapeMangler::default() -> EscapeMangler {
  EscapeMangler::{  }
}

///|
fn escape_mangler_count_char(text : String, ch : Int) -> Int {
  let mut count = 0
  for i in 0.. String {
  if comp.contains("/") {
    let count = escape_mangler_count_char(comp, 47) + 1
    let pieces : Array[String] = []
    for piece in comp.split("/") {
      pieces.push(escape_mangler_component(piece.to_owned()))
    }
    let suffix = pieces.join("_")
    return "import_\{count}_\{suffix}"
  }
  let underscores = escape_mangler_count_char(comp, 95)
  if underscores > 0 {
    "_\{underscores}\{comp}"
  } else {
    comp
  }
}

///|
fn escape_mangler_parse_count(text : String) -> (Int, String)? {
  let mut digits = 0
  while digits < text.length() {
    let code = text.code_unit_at(digits).to_int()
    if code < 48 || code > 57 {
      break
    }
    digits = digits + 1
  }
  if digits == 0 {
    return None
  }
  let count = @string.parse_int(text[:digits]) catch { _ => return None }
  Some((count, text[digits:].to_owned()))
}

///|
fn escape_mangler_unescape_component(
  parts : Array[String],
  cursor : Int,
) -> (String, Int)? {
  guard cursor < parts.length() else { return None }
  let part = parts[cursor]
  if part != "" {
    return Some((part, cursor + 1))
  }
  guard cursor + 1 < parts.length() else { return None }
  let first = parts[cursor + 1]
  guard escape_mangler_parse_count(first) is Some((count, rem)) else {
    return None
  }
  let end_index = cursor + 2 + count
  guard end_index <= parts.length() else { return None }
  let component_parts : Array[String] = [rem]
  for index in (cursor + 2).. Array[String] {
  let parts : Array[String] = []
  for part in mangled.split("_") {
    parts.push(part.to_owned())
  }
  parts
}

///|
pub fn EscapeMangler::unmangle(
  _self : EscapeMangler,
  mangled : String,
) -> (ModulePath, String)? {
  let parts = escape_mangler_split(mangled)
  guard parts.length() > 0 else { return None }
  let mut cursor = 1
  let origin = match parts[0] {
    "package" => PathOrigin::Absolute
    "self" => Relative(0)
    "super" => {
      let mut levels = 1
      while cursor < parts.length() && parts[cursor] == "super" {
        levels = levels + 1
        cursor = cursor + 1
      }
      Relative(levels)
    }
    "import" => {
      guard cursor < parts.length() else { return None }
      let count = @string.parse_int(parts[cursor][:]) catch { _ => return None }
      cursor = cursor + 1
      let package_parts : Array[String] = []
      for _ in 0.. {
      guard escape_mangler_unescape_component(parts, 0)
        is Some((package_name, next_cursor)) else {
        return None
      }
      cursor = next_cursor
      Package(package_name)
    }
  }
  let components : Array[String] = []
  while cursor < parts.length() {
    guard escape_mangler_unescape_component(parts, cursor)
      is Some((component, next_cursor)) else {
      return None
    }
    components.push(component)
    cursor = next_cursor
  }
  guard components.length() > 0 else { return None }
  let item = components[components.length() - 1]
  let path_components : Array[String] = []
  for index in 0..<(components.length() - 1) {
    path_components.push(components[index])
  }
  Some((ModulePath::new(origin, path_components), item))
}

///|
pub fn EscapeMangler::mangle(
  _self : EscapeMangler,
  path : ModulePath,
  item : String,
) -> String {
  let origin = match path.origin {
    Absolute => "package"
    Relative(0) => "self"
    Relative(levels) => {
      let pieces : Array[String] = []
      for _ in 0.. escape_mangler_component(name)
  }
  let escaped_item = escape_mangler_component(item)
  if path.components.length() == 0 {
    "\{origin}_\{escaped_item}"
  } else {
    let escaped_components : Array[String] = []
    for component in path.components {
      escaped_components.push(escape_mangler_component(component))
    }
    let escaped_path = escaped_components.join("_")
    "\{origin}_\{escaped_path}_\{escaped_item}"
  }
}

///|
fn hash_mangler_hash(path : ModulePath, item : String) -> UInt {
  let mut hash = 2166136261U
  let text = path.to_string() + "::" + item
  for index in 0.. String {
  text
  .replace(old="_", new="_")
  .replace(old="/", new="ᐟ")
  .replace(old=":", new="꞉")
  .replace(old="<", new="ᐸ")
  .replace(old=">", new="ᐳ")
  .replace(old=",", new="ˏ")
  .replace(old=" ", new="")
}

///|
fn unicode_mangler_uncomponent(text : String) -> String {
  text
  .replace(old="_", new="_")
  .replace(old="ᐟ", new="/")
  .replace(old="꞉", new=":")
  .replace(old="ᐸ", new="<")
  .replace(old="ᐳ", new=">")
  .replace(old="ˏ", new=",")
}

///|
fn unicode_mangler_last_separator(text : String) -> Int {
  let mut last = -1
  for index in 0.. String {
  match self {
    Escape => EscapeMangler::mangle(EscapeMangler::default(), path, item)
    Hash => "\{item}_\{hash_mangler_hash(path, item).to_string()}"
    Unicode =>
      if path.is_root() {
        unicode_mangler_component(item)
      } else {
        unicode_mangler_component(path.to_string()) +
        "_" +
        unicode_mangler_component(item)
      }
    None => item
  }
}

///|
pub fn ManglerKind::unmangle(
  self : ManglerKind,
  mangled : String,
) -> (ModulePath, String)? {
  match self {
    Escape => EscapeMangler::unmangle(EscapeMangler::default(), mangled)
    Unicode => {
      let sep = unicode_mangler_last_separator(mangled)
      if sep < 0 {
        return Some(
          (ModulePath::new_root(), unicode_mangler_uncomponent(mangled)),
        )
      }
      let path_text = unicode_mangler_uncomponent(mangled[:sep].to_owned())
      let item = unicode_mangler_uncomponent(
        mangled[sep + 1:mangled.length()].to_owned(),
      )
      let path = parse_module_path(path_text) catch { _ => return None }
      Some((path, item))
    }
    Hash | None => None
  }
}