// Copyright 2017-2025 @pezkuwi/types-codec authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '@pezkuwi/util/types';
import type { AnyJson, BareOpts, Codec, Inspect, IU8a, Registry } from '../types/index.js';

/**
 * @name Base
 * @description A type extends the Base class, when it holds a value
 */
export abstract class AbstractBase<T extends Codec> implements Codec {
  readonly registry: Registry;

  public createdAtHash?: IU8a | undefined;
  public initialU8aLength?: number | undefined;
  public isStorageFallback?: boolean;

  readonly #raw: T;

  protected constructor (registry: Registry, value: T, initialU8aLength?: number) {
    this.initialU8aLength = initialU8aLength;
    this.#raw = value;
    this.registry = registry;
  }

  /**
   * @description The length of the value when encoded as a Uint8Array
   */
  public get encodedLength (): number {
    return this.toU8a().length;
  }

  /**
   * @description returns a hash of the contents
   */
  public get hash (): IU8a {
    return this.registry.hash(this.toU8a());
  }

  /**
   * @description returns the inner (wrapped value)
   */
  public get inner (): T {
    return this.#raw;
  }

  /**
   * @description Checks if the value is an empty value
   */
  public get isEmpty (): boolean {
    return this.#raw.isEmpty;
  }

  /**
   * @description Compares the value of the input to see if there is a match
   */
  public eq (other?: unknown): boolean {
    return this.#raw.eq(other);
  }

  /**
   * @description Returns a breakdown of the hex encoding for this Codec
   */
  public inspect (): Inspect {
    return this.#raw.inspect();
  }

  /**
   * @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation
   */
  public toHex (isLe?: boolean): HexString {
    return this.#raw.toHex(isLe);
  }

  /**
   * @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
   */
  public toHuman (isExtended?: boolean, disableAscii?: boolean): AnyJson {
    return this.#raw.toHuman(isExtended, disableAscii);
  }

  /**
   * @description Converts the Object to JSON, typically used for RPC transfers
   */
  public toJSON (): AnyJson {
    return this.#raw.toJSON();
  }

  /**
   * @description Converts the value in a best-fit primitive form
   */
  public toPrimitive (disableAscii?: boolean): AnyJson {
    return this.#raw.toPrimitive(disableAscii);
  }

  /**
   * @description Returns the string representation of the value
   */
  public toString (): string {
    return this.#raw.toString();
  }

  /**
   * @description Encodes the value as a Uint8Array as per the SCALE specifications
   * @param isBare true when the value has none of the type-specific prefixes (internal)
   */
  public toU8a (isBare?: BareOpts): Uint8Array {
    return this.#raw.toU8a(isBare);
  }

  /**
   * @description Returns the base runtime type name for this instance
   */
  public abstract toRawType (): string;

  /**
   * @description Returns the inner wrapped value (equivalent to valueOf)
   */
  public unwrap (): T {
    return this.#raw;
  }

  /**
   * @description Returns the inner wrapped value
   */
  public valueOf (): T {
    return this.#raw;
  }
}
