package gs.std.v0.core

/** Represents string-encoded data.
  *
  * See:
  *   - [[B64]]
  *   - [[Hex]]
  */
trait EncodedString:
  /** @return
    *   The encoded data (expressed as a string).
    */
  def data: String

  /** @return
    *   Decode the data to a byte array.
    */
  def decode(): Array[Byte]

  /** Represents Base64-encoded data.
    *
    * @param data
    *   The encoded data.
    */

/** Represents Base64-encoded data.
  *
  * @param data
  *   The encoded data.
  */
final class B64(
  val data: String
) extends EncodedString:
  /** @inheritDocs
    */
  def decode(): Array[Byte] = Base64Decoder.decode(this)

  /** @inheritDocs
    */
  override def equals(obj: Any): Boolean =
    obj match
      case other: B64 => data == other.data

  /** @inheritDocs
    */
  override def hashCode(): Int = data.hashCode()

  /** @inheritDocs
    */
  override def toString(): String = data

object B64:

  /** Instantiate [[B64]] from the given string. Assumes that the input is
    * base64-encoded.
    *
    * This function does NOT validate the input.
    *
    * @param data
    *   The encoded data.
    * @return
    *   The new [[B64]] instance.
    */
  def apply(
    data: String
  ): B64 = new B64(data)

  given CanEqual[B64, B64] = CanEqual.derived

end B64

/** Represents Base64-url-encoded data.
  *
  * @param data
  *   The encoded data.
  */
final class B64Url(
  val data: String
) extends EncodedString:
  /** @inheritDocs
    */
  def decode(): Array[Byte] = Base64Decoder.decodeUrl(this)

  /** @inheritDocs
    */
  override def equals(obj: Any): Boolean =
    obj match
      case other: B64Url => data == other.data

  /** @inheritDocs
    */
  override def hashCode(): Int = data.hashCode()

  /** @inheritDocs
    */
  override def toString(): String = data

object B64Url:

  /** Instantiate [[B64Url]] from the given string. Assumes that the input is
    * base64-encoded.
    *
    * This function does NOT validate the input.
    *
    * @param data
    *   The encoded data.
    * @return
    *   The new [[B64Url]] instance.
    */
  def apply(
    data: String
  ): B64Url = new B64Url(data)

  given CanEqual[B64Url, B64Url] = CanEqual.derived

end B64Url

/** Represents Hex-encoded data.
  *
  * @param data
  *   The encoded data.
  */
final class Hex(
  val data: String
) extends EncodedString:
  /** @inheritDocs
    */
  def decode(): Array[Byte] = HexDecoder.decode(this)

  /** @inheritDocs
    */
  override def equals(obj: Any): Boolean =
    obj match
      case other: Hex => data == other.data

  /** @inheritDocs
    */
  override def hashCode(): Int = data.hashCode()

  /** @inheritDocs
    */
  override def toString(): String = data

object Hex:

  /** Instantiate [[Hex]] from the given string. Assumes that the input is
    * hex-encoded.
    *
    * This function does NOT validate the input.
    *
    * @param data
    *   The encoded data.
    * @return
    *   The new [[Hex]] instance.
    */
  def apply(
    data: String
  ): Hex = new Hex(data)

  given CanEqual[Hex, Hex] = CanEqual.derived

end Hex
