// 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 impl[X : Eq] Eq for X? with fn equal(self, other) {
  match (self, other) {
    (None, None) => true
    (Some(x), Some(y)) => x == y
    _ => false
  }
}

///|
/// Convert to `string`.
#deprecated("Option does not have a meaningful string representation")
pub fn[X : Show] Option::to_string(self : X?) -> String {
  match self {
    None => "None"
    Some(x) => "Some(" + x.to_string() + ")"
  }
}

///|
/// Extract the value in `Some`.
/// 
/// If the value is `None`, it throws a panic.
pub fn[X] Option::unwrap(self : X?) -> X {
  match self {
    None => panic()
    Some(x) => x
  }
}

///|
/// Return the contained `Some` value or the provided default.
#alias(or, deprecated)
pub fn[T] Option::unwrap_or(self : T?, default : T) -> T {
  match self {
    Some(t) | (None with t = default) => t
  }
}

///|
/// Return the contained `Some` value or the provided default.
///
/// Default is lazily evaluated
#alias(or_else, deprecated)
pub fn[T] Option::unwrap_or_else(
  self : T?,
  default : () -> T raise?,
) -> T raise? {
  match self {
    None => default()
    Some(t) => t
  }
}

///|
/// Return the contained `Some` value or the result of the `T::default()`.
#alias(or_default, deprecated)
pub fn[T : Default] Option::unwrap_or_default(self : T?) -> T {
  match self {
    Some(t) | (None with t = T::default()) => t
  }
}

///|
pub impl[X : Compare] Compare for X? with fn compare(self, other) {
  match (self, other) {
    (Some(x), Some(y)) => x.compare(y)
    (Some(_), None) => 1
    (None, Some(_)) => -1
    (None, None) => 0
  }
}

///|
/// Extract the wrapped value with `unwrap_or_error` semantics.
#alias(or_error, deprecated)
pub fn[T, Err : Error] Option::unwrap_or_error(
  self : T?,
  err : Err,
) -> T raise Err {
  match self {
    Some(v) => v
    None => raise err
  }
}

///|
/// `None`
pub impl[X] Default for X? with fn default() {
  None
}

///|
/// Return an iterator via `iter`.
#alias(iterator, deprecated)
pub fn[T] Option::iter(self : T?) -> Iter[T] {
  match self {
    Some(v) => Iter::singleton(v)
    None => Iter::empty()
  }
}

///|
/// Maps the value of an `Option` using a provided function.
///
/// # Example
///
/// ```mbt check
/// test {
///   let a = Some(5)
///   @test.assert_eq(a.map(x => x * 2), Some(10))
///   let b = None
///   @test.assert_eq(b.map(x => x * 2), None)
/// }
/// ```
pub fn[T, U] Option::map(self : T?, f : (T) -> U raise?) -> U? raise? {
  match self {
    Some(t) => Some(f(t))
    None => None
  }
}

///|
/// Returns the provided default result (if none), or applies a function to the contained value (if any).
/// Arguments passed to map_or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use `map_or_else`, which is lazily evaluated.
///
/// # Example
///
/// ```mbt check
/// test {
///   let a = Some(5)
///   @test.assert_eq(a.map_or(3, x => x * 2), 10)
/// }
/// ```
pub fn[T, U] Option::map_or(
  self : T?,
  default : U,
  f : (T) -> U raise?,
) -> U raise? {
  match self {
    None => default
    Some(x) => f(x)
  }
}

///|
/// Computes a default function result (if none), or applies a different function to the contained value (if any).
///
/// # Example
///
/// ```mbt check
/// test {
///   let a = Some(5)
///   @test.assert_eq(a.map_or_else(() => 3, x => x * 2), 10)
/// }
/// ```
pub fn[T, U] Option::map_or_else(
  self : T?,
  default : () -> U raise?,
  f : (T) -> U raise?,
) -> U raise? {
  match self {
    None => default()
    Some(x) => f(x)
  }
}

///|
/// Binds an option to a function that returns another option.
///
/// # Example
///
/// ```mbt check
/// test {
///   let a = Option::Some(5)
///   let r1 = a.bind(x => Some(x * 2))
///   @test.assert_eq(r1, Some(10))
///   let b : Int? = None
///   let r2 = b.bind(x => Some(x * 2))
///   @test.assert_eq(r2, None)
/// }
/// ```
pub fn[T, U] Option::bind(self : T?, f : (T) -> U? raise?) -> U? raise? {
  match self {
    Some(t) => f(t)
    None => None
  }
}

///|
/// Flatten nested option/result value.
#deprecated("use `option.bind(x => x)` instead")
pub fn[T] Option::flatten(self : T??) -> T? {
  match self {
    Some(inner) => inner
    None => None
  }
}

///|
/// Checks if the option is empty.
#deprecated("use `x is None` instead")
pub fn[T] Option::is_empty(self : T?) -> Bool {
  self is None
}

///|
/// Filters the option by applying the given predicate function `f`.
///
/// If the predicate function `f` returns `true` for the value contained in the option,
/// the same option is returned. Otherwise, `None` is returned.
///
/// # Example
/// ```mbt check
/// test {
///   let x = Some(3)
///   @test.assert_eq(x.filter(x => x > 5), None)
///   @test.assert_eq(x.filter(x => x < 5), Some(3))
/// }
/// ```
pub fn[T] Option::filter(self : T?, f : (T) -> Bool raise?) -> T? raise? {
  match self {
    Some(t) => if f(t) { self } else { None }
    None => None
  }
}

///|
/// Checks if the option contains a value.
#deprecated("use `x is Some(_)` instead")
pub fn[T] Option::is_some(self : T?) -> Bool {
  self is Some(_)
}

///|
/// Checks if the option is None.
#deprecated("use `x is None` instead")
pub fn[T] Option::is_none(self : T?) -> Bool {
  self is None
}