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

///|
fn[A] new_queue() -> Queue[A] {
  { inner: Deque([]) }
}

///|
/// Creates a new queue from an array.
///
/// # Example
/// ```mbt check
/// test {
///   let array = Array::makei(3, idx => idx + 1)
///   let queue : @queue.Queue[Int] = Queue(array)
///   @test.assert_eq(queue.length(), 3)
/// }
/// ```
#alias(from_array)
#as_free_fn(from_array)
#alias(of, deprecated="Use from_array instead")
#as_free_fn(of, deprecated="Use from_array instead")
pub fn[A] Queue::Queue(arr : ArrayView[A]) -> Queue[A] {
  { inner: Deque(arr) }
}

///|
#deprecated("Use @debug.Debug instead of Show for debugging purposes. See https://github.com/moonbitlang/core/blob/main/debug/README.mbt.md")
pub impl[A : Show] Show for Queue[A]

///|
pub impl[A : Show] Show for Queue[A] with fn output(self, logger) {
  logger.write_iter(self.iter(), prefix="@queue.from_array([", suffix="])")
}

///|
/// Tests if two queues are equal.
impl[A : Eq] Eq for Queue[A] with fn equal(self, other) {
  self.inner == other.inner
}

///|
/// Clears the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   queue.clear()
/// }
/// ```
pub fn[A] Queue::clear(self : Queue[A]) -> Unit {
  self.inner.clear()
}

///|
/// Get the length of the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = Queue([])
///   @test.assert_eq(queue.length(), 0)
/// }
/// ```
pub fn[A] Queue::length(self : Queue[A]) -> Int {
  self.inner.length()
}

///|
/// Checks if the queue is empty.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = Queue([])
///   assert_true(queue.is_empty())
/// }
/// ```
pub fn[A] Queue::is_empty(self : Queue[A]) -> Bool {
  self.inner.is_empty()
}

///|
/// Adds a value to the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = Queue([])
///   queue.push(1)
/// }
/// ```
#owned(x)
pub fn[A] Queue::push(self : Queue[A], x : A) -> Unit {
  self.inner.push_back(x)
}

///|
/// Peeks at the first value in the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   @test.assert_eq(queue.unsafe_peek(), 1)
/// }
/// ```
#internal(unsafe, "Panics if the queue is empty.")
#doc(hidden)
#alias(peek_exn, deprecated)
pub fn[A] Queue::unsafe_peek(self : Queue[A]) -> A {
  match self.inner.front() {
    None => abort("Queue is empty")
    Some(x) => x
  }
}

///|
/// Peeks at the first value in the queue, which returns None if the queue is empty.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   @test.assert_eq(queue.peek(), Some(1))
/// }
/// ```
pub fn[A] Queue::peek(self : Queue[A]) -> A? {
  self.inner.front()
}

///|
/// Pops the first value from the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   @test.assert_eq(queue.unsafe_pop(), 1)
/// }
/// ```
#internal(unsafe, "Panics if the queue is empty.")
#doc(hidden)
#alias(pop_exn, deprecated)
pub fn[A] Queue::unsafe_pop(self : Queue[A]) -> A {
  match self.inner.pop_front() {
    None => abort("Queue is empty")
    Some(x) => x
  }
}

///|
/// Pops the first value from the queue, which returns None if the queue is empty.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   @test.assert_eq(queue.pop(), Some(1))
/// }
/// ```
pub fn[A] Queue::pop(self : Queue[A]) -> A? {
  self.inner.pop_front()
}

///|
/// Iterates over the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   let mut sum = 0
///   queue.each(x => sum += x)
///   inspect(sum, content="10")
/// }
/// ```
#locals(f)
pub fn[A] Queue::each(self : Queue[A], f : (A) -> Unit) -> Unit {
  for x in self.inner {
    f(x)
  }
}

///|
/// Iterates over the queue with index.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   let mut sum = 0
///   queue.eachi((i, x) => sum += i * x)
///   inspect(sum, content="20")
/// }
/// ```
#locals(f)
pub fn[A] Queue::eachi(self : Queue[A], f : (Int, A) -> Unit) -> Unit {
  for i, x in self.inner {
    f(i, x)
  }
}

///|
/// Folds over the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = Queue([])
///   let sum = queue.fold(init=0, (acc, x) => acc + x)
///   @test.assert_eq(sum, 0)
/// }
/// ```
#locals(f)
pub fn[A, B] Queue::fold(self : Queue[A], init~ : B, f : (B, A) -> B) -> B {
  let mut acc = init
  for x in self.inner {
    acc = f(acc, x)
  }
  acc
}

///|
/// Returns a copy of the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([1, 2, 3, 4])
///   let queue2 : @queue.Queue[Int] = queue.copy()
///   @test.assert_eq(queue2.length(), 4)
/// }
/// ```
#alias(clone, deprecated)
pub fn[A] Queue::copy(self : Queue[A]) -> Queue[A] {
  { inner: self.inner.copy() }
}

///|
/// Transfers all elements from one queue to another.
///
/// Adds all of the elements of source to the end of destination, then clears source.
/// If source and destination are the same queue, this is a no-op.
///
/// # Example
/// ```mbt check
/// test {
///   let dst : @queue.Queue[Int] = Queue([])
///   let src : @queue.Queue[Int] = @queue.from_array([5, 6, 7, 8])
///   src.transfer(dst)
/// }
/// ```
pub fn[A] Queue::transfer(self : Queue[A], dst : Queue[A]) -> Unit {
  if physical_equal(self.inner, dst.inner) {
    return // no-op for self-transfer
  }
  dst.inner.append(self.inner)
  self.inner.clear()
}

///|
/// Creates an iter from the queue.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_array([5, 6, 7, 8])
///   let sum = queue.iter().fold((x, y) => x + y, init=0)
///   @test.assert_eq(sum, 26)
/// }
/// ```
#alias(iterator, deprecated)
pub fn[A] Queue::iter(self : Queue[A]) -> Iter[A] {
  self.inner.iter()
}

///|
/// Creates a new queue from an iter.
///
/// # Example
/// ```mbt check
/// test {
///   let queue : @queue.Queue[Int] = @queue.from_iter(Iter::empty())
///   @test.assert_eq(queue.length(), 0)
/// }
/// ```
#as_free_fn
#alias(from_iterator, deprecated)
#as_free_fn(from_iterator, deprecated)
pub fn[A] Queue::from_iter(iter : Iter[A]) -> Queue[A] {
  { inner: @deque.from_iter(iter) }
}

///|
test "from_fixed_array_2" {
  let q = from_array([1, 2, 3, 4])
  q.push(11)
  @test.assert_eq(q, from_array([1, 2, 3, 4, 11]))
  q.unsafe_pop() |> ignore
  @test.assert_eq(q, from_array([2, 3, 4, 11]))
}

///|
test "from_array_2" {
  let q = from_array([1, 2, 3, 4])
  q.push(11)
  @test.assert_eq(q, from_array([1, 2, 3, 4, 11]))
  q.unsafe_pop() |> ignore
  @test.assert_eq(q, from_array([2, 3, 4, 11]))
}

///|
test "equal" {
  let queue = from_array([1, 2, 3, 4])
  let queue2 = from_array([1, 2, 3, 4])
  let queue3 = from_array([1, 2, 3, 5])
  assert_true(queue == queue2)
  assert_false(queue == queue3)
  queue.unsafe_pop() |> ignore
  assert_false(queue == queue2)
  @test.assert_eq(queue, from_array([2, 3, 4]))
  assert_false(from_array([]) == from_array([1]))
}

///|
test "push" {
  let queue : Queue[Int] = new_queue()
  queue.push(1)
  queue.push(2)
  queue.push(3)
  queue.push(1)
  @test.assert_eq(queue.length(), 4)
  @test.assert_eq(queue, from_array([1, 2, 3, 1]))
}

///|
test "copy" {
  let queue : Queue[Int] = from_array([1, 2, 3, 4])
  let queue2 : Queue[Int] = queue.copy()
  @test.assert_eq(queue2.length(), 4)
  @test.assert_eq(queue2, from_array([1, 2, 3, 4]))
  @test.assert_eq(queue.length(), 4)
  @test.assert_eq(queue, from_array([1, 2, 3, 4]))
}

///|
test "transfer" {
  let queue : Queue[Int] = from_array([1, 2, 3, 4])
  let queue2 : Queue[Int] = from_array([5, 6, 7, 8])
  queue.transfer(queue2)
  @test.assert_eq(queue.length(), 0)
  @test.assert_eq(queue2.length(), 8)
  @test.assert_eq(queue2, from_array([5, 6, 7, 8, 1, 2, 3, 4]))
}