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

// This file is partially derived from the Python project:
//   https://github.com/python/cpython/blob/3.12/Lib/uuid.py

///|
/// The UUID variant.
pub(all) enum Variant {
  ReservedNCS
  RFC4122(Version)
  ReservedMicrosoft
  ReservedFuture
} derive(Debug)

///|
/// The UUID version number.
///
/// This is only used when the variant is `RFC4122`.
///
pub(all) enum Version {
  V1
  V2
  V3
  V4
  V5
  Unknown(Int)
} derive(Debug)

///|
fn Version::from_int64(int : Int64) -> Version {
  match int {
    1L => V1
    2L => V2
    3L => V3
    4L => V4
    5L => V5
    ver => Unknown(ver.to_int())
  }
}

///|
fn Version::to_int64(self : Version) -> Int64 {
  match self {
    V1 => 1L
    V2 => 2L
    V3 => 3L
    V4 => 4L
    V5 => 5L
    Unknown(ver) => ver.to_int64()
  }
}

///|
/// Retrieves the variant of the UUID.
pub fn UUID::variant(self : UUID) -> Variant {
  if (self.lo & (0x8000L << 48)) == 0L {
    ReservedNCS
  } else if (self.lo & (0x4000L << 48)) == 0L {
    RFC4122(
      Version::from_int64(
        (self.hi.reinterpret_as_uint64() >> 12).reinterpret_as_int64() & 0xfL,
      ),
    )
  } else if (self.lo & (0x2000L << 48)) == 0L {
    ReservedMicrosoft
  } else {
    ReservedFuture
  }
}

///|
/// Retrieves the version number of the UUID.
///
/// If the variant of the UUID is not `RFC4122`, returns `None`.
///
pub fn UUID::version(self : UUID) -> Version? {
  match self.variant() {
    RFC4122(ver) => Some(ver)
    _ => None
  }
}

///|
/// Converts this UUID to RFC-4122 with the given version number.
pub fn UUID::as_version(self : UUID, version : Version) -> UUID {
  let { hi, lo } = self
  // Set the variant to RFC 4122.
  let lo = (lo & (0xc000L << 48).lnot()) | (0x8000L << 48)
  let hi = (
    // Set the version number.
      hi & 0xf000L.lnot()
    ) |
    (version.to_int64() << 12)
  { hi, lo }
}

///|
test "variant" {
  debug_inspect(
    from_hex("81a277a6-771b-1a55-6246-fa3763226aa0").variant(),
    content="ReservedNCS",
  )
  debug_inspect(
    from_hex("42e0e55d-cee5-044c-b3a6-18a7dc5ae8b4").variant(),
    content="RFC4122(Unknown(0))",
  )
  debug_inspect(
    from_hex("1db64c57-6b48-fd91-dab2-d3accd060480").variant(),
    content="ReservedMicrosoft",
  )
  debug_inspect(
    from_hex("96b2d68d-f97d-9358-f2b1-79cbc6d43c83").variant(),
    content="ReservedFuture",
  )
}

///|
test "version" {
  let u = from_hex("907976bf-6784-5a4f-5d7d-33c04dd8fa1a")
  debug_inspect(u.version(), content="None")
  debug_inspect(u.as_version(V1).version(), content="Some(V1)")
  debug_inspect(u.as_version(V2).version(), content="Some(V2)")
  debug_inspect(u.as_version(V3).version(), content="Some(V3)")
  debug_inspect(u.as_version(V4).version(), content="Some(V4)")
  debug_inspect(u.as_version(V5).version(), content="Some(V5)")
  debug_inspect(u.as_version(Unknown(9)).version(), content="Some(Unknown(9))")
}

///|
test "show" {
  debug_inspect(
    RFC4122(V4),
    content=(
      #|RFC4122(V4)
    ),
  )
  debug_inspect(
    Unknown(7),
    content=(
      #|Unknown(7)
    ),
  )
}