// 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(all) suberror JsonDecodeError {
  JsonDecodeError(path~ : JsonPath, String)
} derive(Eq, Show, Debug)

///|
/// Trait for types that can be converted from `Json`
pub(open) trait FromJson {
  from_json(Json, JsonPath) -> Self raise JsonDecodeError
}

///|
pub fn[T : FromJson] from_json(
  json : Json,
  path? : JsonPath = Root,
) -> T raise JsonDecodeError {
  FromJson::from_json(json, path)
}

///|
fn[T] decode_error(path : JsonPath, msg : String) -> T raise JsonDecodeError {
  raise JsonDecodeError(path~, msg)
}

///|
pub impl FromJson for Bool with from_json(json, path) {
  match json {
    Bool(true) => true
    Bool(false) => false
    _ => decode_error(path, "Bool::from_json: expected boolean")
  }
}

///|
pub impl FromJson for Int with from_json(json, path) {
  guard json is Number(n) else {
    decode_error(path, "Int::from_json: expected number")
  }
  n.to_int() catch {
    error => decode_error(path, "Int::from_json: parsing failure \{error}")
  }
}

///|
pub impl FromJson for Int64 with from_json(json, path) {
  guard json is Number(n) else {
    decode_error(path, "Int64::from_json: expected number")
  }
  n.to_int64() catch {
    error => decode_error(path, "Int64::from_json: parsing failure \{error}")
  }
}

///|
pub impl FromJson for UInt with from_json(json, path) {
  guard json is Number(n) else {
    decode_error(path, "UInt::from_json: expected number")
  }
  n.to_uint() catch {
    error => decode_error(path, "UInt::from_json: parsing failure \{error}")
  }
}

///|
pub impl FromJson for UInt64 with from_json(json, path) {
  guard json is Number(n) else {
    decode_error(path, "UInt64::from_json: expected number")
  }
  n.to_uint64() catch {
    error => decode_error(path, "UInt64::from_json: parsing failure \{error}")
  }
}

///|
pub impl FromJson for Double with from_json(json, path) {
  match json {
    String("NaN") => @double.not_a_number
    String("Infinity") => @double.infinity
    String("-Infinity") => @double.neg_infinity
    Number(n) =>
      n.to_double() catch {
        error =>
          decode_error(path, "Double::from_json: parsing failure \{error}")
      }
    _ => decode_error(path, "Double::from_json: expected number")
  }
}

///|
pub impl FromJson for Float with from_json(json, path) {
  match json {
    String("NaN") => @float.not_a_number
    String("Infinity") => @float.infinity
    String("-Infinity") => @float.neg_infinity
    Number(n) =>
      n.to_float() catch {
        error =>
          decode_error(path, "Float::from_json: parsing failure \{error}")
      }
    _ => decode_error(path, "Float::from_json: expected number")
  }
}

///|
pub impl FromJson for String with from_json(json, path) {
  guard json is String(a) else {
    decode_error(path, "String::from_json: expected string")
  }
  a
}

///|
pub impl FromJson for StringView with from_json(json, path) {
  guard json is String(a) else {
    decode_error(path, "View::from_json: expected string")
  }
  a[:]
}

///|
pub impl FromJson for Char with from_json(json, path) {
  guard json is String(a) else {
    decode_error(path, "Char::from_json: expected string")
  }
  let len = a.length()
  if len == 1 {
    a.unsafe_get(0).unsafe_to_char()
  } else if len == 2 {
    let c1 = a.unsafe_get(0).to_int()
    let c2 = a.unsafe_get(1).to_int()
    if c1 is (0xD800..=0xDBFF) && c2 is (0xDC00..=0xDFFF) {
      let c3 = (c1 << 10) + c2 - 0x35fdc00
      c3.unsafe_to_char()
    } else {
      decode_error(path, "Char::from_json: invalid surrogate pair")
    }
  } else {
    decode_error(path, "Char::from_json: expected single character")
  }
}

///|
pub impl[X : FromJson] FromJson for Array[X] with from_json(json, path) {
  guard json is Array(a) else {
    decode_error(path, "Array::from_json: expected array")
  }
  guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
  a.mapi((i, x) => {
    new_path.index = i
    FromJson::from_json(x, new_path)
  })
}

///|
pub impl[X : FromJson] FromJson for ArrayView[X] with from_json(json, path) {
  guard json is Array(a) else {
    decode_error(path, "ArrayView::from_json: expected array")
  }
  guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
  a.mapi((i, x) => {
    new_path.index = i
    FromJson::from_json(x, new_path)
  })
}

///|
pub impl[X : FromJson] FromJson for MutArrayView[X] with from_json(json, path) {
  guard json is Array(a) else {
    decode_error(path, "MutArrayView::from_json: expected array")
  }
  guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
  a
  .mapi((i, x) => {
    new_path.index = i
    FromJson::from_json(x, new_path)
  })
  .mut_view()
}

///|
pub impl[X : FromJson] FromJson for FixedArray[X] with from_json(json, path) {
  guard json is Array(a) else {
    decode_error(path, "FixedArray::from_json: expected array")
  }
  let len = a.length()
  if len == 0 {
    return []
  }
  guard JsonPath::Index(path, index=0) is (Index(_) as new_path)
  let res = FixedArray::make(
    len,
    FromJson::from_json(a.unsafe_get(0), new_path),
  )
  for i in 1.. ()
    [.. "\\x", '0'..='9' | 'a'..='f' as x, '0'..='9' | 'a'..='f' as y, .. rest] => {
      let upper = (x.to_int() & 0xF) + (x.to_int() >> 6) * 9
      let lower = (y.to_int() & 0xF) + (y.to_int() >> 6) * 9
      buffer.write_byte(((upper << 4) | lower).to_byte())
      continue rest
    }
    [' '..='~' as ch, .. rest] => {
      guard ch != '\\' && ch != '"' else {
        decode_error(path, "Bytes::from_json: invalid escape sequence")
      }
      buffer.write_byte(ch.to_uint().to_byte())
      continue rest
    }
    _ => decode_error(path, "Bytes::from_json: invalid byte sequence")
  }
  buffer.to_bytes()
}