// 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.

///|
/// Maps the value of a Result if it is `Ok` into another, otherwise returns the `Err` value unchanged.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, Unit] = Ok(6)
///   let y = x.map((v : Int) => v * 7)
///   @test.assert_eq(y, Ok(42))
/// }
/// ```
pub fn[T, E, U] Result::map(self : Result[T, E], f : (T) -> U) -> Result[U, E] {
  match self {
    Ok(value) => Ok(f(value))
    Err(err) => Err(err)
  }
}

///|
test "map" {
  let x : Result[Int, Unit] = Ok(6)
  let y = x.map((v : Int) => v * 7)
  let z : Result[Int, Int] = Err(3)
  let w = z.map((v : Int) => v * 7)
  assert_true(y == Ok(42))
  assert_true(w == Err(3))
}

///|
/// Maps the value of a Result if it is `Err` into another, otherwise returns the `Ok` value unchanged.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, String] = Err("error")
///   let y = x.map_err((v : String) => v + "!")
///   @test.assert_eq(y, Err("error!"))
/// }
/// ```
pub fn[T, E, F] Result::map_err(
  self : Result[T, E],
  f : (E) -> F,
) -> Result[T, F] {
  match self {
    Ok(value) => Ok(value)
    Err(err) => Err(f(err))
  }
}

///|
test "map_err" {
  let x : Result[Int, String] = Err("error")
  let y = x.map_err((v : String) => v + "!")
  let z : Result[Int, Int] = Ok(6)
  let w = z.map_err((v : Int) => v + 6)
  assert_true(y == Err("error!"))
  assert_true(w == Ok(6))
}

///|
/// Return the contained `Ok` value or the provided default.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, String] = Ok(3)
///   let y : Result[Int, String] = Err("error")
///   @test.assert_eq(x.unwrap_or(5), 3)
///   @test.assert_eq(y.unwrap_or(5), 5)
/// }
/// ```
#alias(or)
pub fn[T, E] Result::unwrap_or(self : Result[T, E], default : T) -> T {
  match self {
    Ok(value) | (Err(_) with value = default) => value
  }
}

///|
test "unwrap_or" {
  let x : Result[Int, String] = Ok(3)
  let y : Result[Int, String] = Err("error")
  assert_true(x.unwrap_or(5) == 3)
  assert_true(y.unwrap_or(5) == 5)
}

///|
/// Return the contained `Ok` value or the provided default.
///
/// Default is lazily evaluated.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, String] = Ok(3)
///   let y : Result[Int, String] = Err("error")
///   @test.assert_eq(x.unwrap_or_else(() => 5), 3)
///   @test.assert_eq(y.unwrap_or_else(() => 5), 5)
/// }
/// ```
#alias(or_else)
pub fn[T, E] Result::unwrap_or_else(
  self : Result[T, E],
  default : () -> T raise?,
) -> T raise? {
  match self {
    Ok(value) => value
    Err(_) => default()
  }
}

///|
test "unwrap_or_else" {
  let x : Result[Int, String] = Ok(3)
  let y : Result[Int, String] = Err("error")
  assert_true(x.unwrap_or_else(() => 5) == 3)
  assert_true(y.unwrap_or_else(() => 5) == 5)
}

///|
/// Flatten a `Result` of `Result` into a single `Result`.
///
/// If the outer `Result` is an `Ok`, the inner `Result` is returned. If the outer `Result` is an `Err`, the inner `Result` is ignored and the `Err` is returned.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Result[Int, String], String] = Ok(Ok(6))
///   let y = x.flatten()
///   @test.assert_eq(y, Ok(6))
/// }
/// ```
pub fn[T, E] Result::flatten(self : Result[Result[T, E], E]) -> Result[T, E] {
  match self {
    Ok(value) => value
    Err(err) => Err(err)
  }
}

///|
test "flatten" {
  let x : Result[Result[Int, String], String] = Ok(Ok(6))
  let y = x.flatten()
  let z : Result[Result[Int, String], String] = Err("error")
  let w = z.flatten()
  assert_true(y == Ok(6))
  assert_true(w == Err("error"))
}

///|
/// Binds a result to a function that returns another result.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, String] = Ok(6)
///   let y = x.bind((v : Int) => Ok(v * 7))
///   @test.assert_eq(y, Ok(42))
/// }
/// ```
pub fn[T, E, U] Result::bind(
  self : Result[T, E],
  g : (T) -> Result[U, E],
) -> Result[U, E] {
  match self {
    Ok(value) => g(value)
    Err(err) => Err(err)
  }
}

///|
test "bind" {
  let x : Result[Int, String] = Ok(6)
  let y = x.bind((v : Int) => Ok(v * 7))
  assert_true(y == Ok(42))
}

///|
/// Converts a `Result` to an `Option`.
///
/// Converts `Ok` to `Some` and `Err` to `None`.
///
/// # Example
///
/// ```mbt check
/// test {
///   let x : Result[Int, String] = Ok(6)
///   let y = x.to_option()
///   @test.assert_eq(y, Some(6))
/// }
/// ```
pub fn[T, E] Result::to_option(self : Result[T, E]) -> T? {
  match self {
    Ok(value) => Some(value)
    Err(_) => None
  }
}

///|
test "to_option" {
  let x : Result[Int, String] = Ok(6)
  let y : Result[Int, String] = Err("error")
  let z = x.to_option()
  let w = y.to_option()
  assert_true(z == Some(6))
  assert_true(w == None)
}

///|
pub impl[T : Compare, E : Compare] Compare for Result[T, E] with fn compare(
  self : Result[T, E],
  other : Result[T, E],
) -> Int {
  match (self, other) {
    (Ok(x), Ok(y)) => x.compare(y)
    (Ok(_), Err(_)) => -1
    (Err(_), Ok(_)) => 1
    (Err(x), Err(y)) => x.compare(y)
  }
}

///|
test "compare" {
  let ok1 = Result::Ok(1)
  let ok2 = Result::Ok(2)
  let err1 = Result::Err(1)
  let err2 = Result::Err(2)
  assert_true(0 == ok1.compare(ok1))
  assert_true(0 == err2.compare(Err(2)))
  assert_true(-1 == ok1.compare(ok2))
  assert_true(1 == ok2.compare(ok1))
  assert_true(-1 == err1.compare(err2))
  assert_true(1 == err2.compare(err1))
  assert_true(-1 == ok2.compare(err1))
  assert_true(1 == err1.compare(ok2))
}

///|
/// Extract the wrapped value with `unwrap` semantics.
pub fn[T, E] Result::unwrap(self : Result[T, E]) -> T {
  match self {
    Ok(x) => x
    Err(_) => abort("called `Result::unwrap()` on an `Err` value")
  }
}

///|
/// Extracts the error value from a `Result[T, E]`. If the `Result` is `Ok`,
/// aborts with a runtime error message.
///
/// Parameters:
///
/// * `self` : The `Result` value to extract the error from.
///
/// Returns the error value of type `E` if `self` is `Err(e)`.
///
/// Example:
///
/// ```mbt check
/// test {
///   let err : Result[Int, String] = Err("error message")
///   inspect(err.unwrap_err(), content="error message")
/// }
/// ```
pub fn[T, E] Result::unwrap_err(self : Result[T, E]) -> E {
  match self {
    Ok(_) => abort("called `Result::unwrap_err()` on an `Ok` value")
    Err(e) => e
  }
}

///|
#warnings("-deprecated")
test "show" {
  let ok : Result[_, String] = Ok("hello")
  inspect(
    ok,
    content=(
      #|Ok(hello)
    ),
  )
  let err : Result[String, _] = Err("world")
  inspect(
    err,
    content=(
      #|Err(world)
    ),
  )
}

///|
/// Return the contained `Ok` value or the result of the `T::default()`.
pub fn[T : Default, E] Result::unwrap_or_default(self : Result[T, E]) -> T {
  match self {
    Ok(value) | (Err(_) with value = T::default()) => value
  }
}

///|
test "unwrap_or_default" {
  let x : Result[Int, String] = Ok(3)
  let y : Result[Int, String] = Err("error")
  assert_true(x.unwrap_or_default() == 3)
  assert_true(y.unwrap_or_default() == 0)
}

///|
/// Extract the wrapped value with `unwrap_or_error` semantics.
pub fn[T, E : Error] Result::unwrap_or_error(self : Result[T, E]) -> T raise E {
  match self {
    Ok(x) => x
    Err(e) => raise e
  }
}

///|
test "unwrap exn" {
  let result = try
    (Err(Failure("This is serious")) : Result[Unit, Failure]).unwrap_or_error()
    |> Ok
  catch {
    Failure(msg) => Err(msg)
  }
  assert_true(result == Err("This is serious"))
}

///|
pub impl[T : Eq, E : Eq] Eq for Result[T, E] with fn equal(self, other) {
  match (self, other) {
    (Ok(x), Ok(y)) => x == y
    (Err(x), Err(y)) => x == y
    _ => false
  }
}