import * as Bytes from './Bytes.js';
import * as Errors from './Errors.js';
import * as Hex from './Hex.js';
import * as Json from './Json.js';
/**
 * Asserts that a {@link ox#PublicKey.PublicKey} is valid.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * PublicKey.assert({
 *   prefix: 4,
 *   y: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
 * })
 * // @error: PublicKey.InvalidError: Value \`{"y":"1"}\` is not a valid public key.
 * // @error: Public key must contain:
 * // @error: - an `x` and `prefix` value (compressed)
 * // @error: - an `x`, `y`, and `prefix` value (uncompressed)
 * ```
 *
 * @param publicKey - The public key object to assert.
 */
export function assert(publicKey, options = {}) {
    const { compressed } = options;
    const { prefix, x, y } = publicKey;
    // Uncompressed
    if (compressed === false ||
        (typeof x === 'bigint' && typeof y === 'bigint')) {
        if (prefix !== 4)
            throw new InvalidPrefixError({
                prefix,
                cause: new InvalidUncompressedPrefixError(),
            });
        return;
    }
    // Compressed
    if (compressed === true ||
        (typeof x === 'bigint' && typeof y === 'undefined')) {
        if (prefix !== 3 && prefix !== 2)
            throw new InvalidPrefixError({
                prefix,
                cause: new InvalidCompressedPrefixError(),
            });
        return;
    }
    // Unknown/invalid
    throw new InvalidError({ publicKey });
}
/**
 * Compresses a {@link ox#PublicKey.PublicKey}.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.from({
 *   prefix: 4,
 *   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 *   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * })
 *
 * const compressed = PublicKey.compress(publicKey) // [!code focus]
 * // @log: {
 * // @log:   prefix: 3,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log: }
 * ```
 *
 * @param publicKey - The public key to compress.
 * @returns The compressed public key.
 */
export function compress(publicKey) {
    const { x, y } = publicKey;
    return {
        prefix: y % 2n === 0n ? 2 : 3,
        x,
    };
}
/**
 * Instantiates a typed {@link ox#PublicKey.PublicKey} object from a {@link ox#PublicKey.PublicKey}, {@link ox#Bytes.Bytes}, or {@link ox#Hex.Hex}.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.from({
 *   prefix: 4,
 *   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 *   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * })
 * // @log: {
 * // @log:   prefix: 4,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log:   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * // @log: }
 * ```
 *
 * @example
 * ### From Serialized
 *
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.from('0x048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5')
 * // @log: {
 * // @log:   prefix: 4,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log:   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * // @log: }
 * ```
 *
 * @param value - The public key value to instantiate.
 * @returns The instantiated {@link ox#PublicKey.PublicKey}.
 */
export function from(value) {
    const publicKey = (() => {
        if (Hex.validate(value))
            return fromHex(value);
        if (Bytes.validate(value))
            return fromBytes(value);
        const { prefix, x, y } = value;
        if (typeof x === 'bigint' && typeof y === 'bigint')
            return { prefix: prefix ?? 0x04, x, y };
        return { prefix, x };
    })();
    assert(publicKey);
    return publicKey;
}
/**
 * Deserializes a {@link ox#PublicKey.PublicKey} from a {@link ox#Bytes.Bytes} value.
 *
 * @example
 * ```ts twoslash
 * // @noErrors
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.fromBytes(new Uint8Array([128, 3, 131, ...]))
 * // @log: {
 * // @log:   prefix: 4,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log:   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * // @log: }
 * ```
 *
 * @param publicKey - The serialized public key.
 * @returns The deserialized public key.
 */
export function fromBytes(publicKey) {
    return fromHex(Hex.fromBytes(publicKey));
}
/**
 * Deserializes a {@link ox#PublicKey.PublicKey} from a {@link ox#Hex.Hex} value.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.fromHex('0x8318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5')
 * // @log: {
 * // @log:   prefix: 4,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log:   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * // @log: }
 * ```
 *
 * @example
 * ### Deserializing a Compressed Public Key
 *
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.fromHex('0x038318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed75')
 * // @log: {
 * // @log:   prefix: 3,
 * // @log:   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 * // @log: }
 * ```
 *
 * @param publicKey - The serialized public key.
 * @returns The deserialized public key.
 */
export function fromHex(publicKey) {
    if (publicKey.length !== 132 &&
        publicKey.length !== 130 &&
        publicKey.length !== 68)
        throw new InvalidSerializedSizeError({ publicKey });
    if (publicKey.length === 130) {
        const x = BigInt(Hex.slice(publicKey, 0, 32));
        const y = BigInt(Hex.slice(publicKey, 32, 64));
        return {
            prefix: 4,
            x,
            y,
        };
    }
    if (publicKey.length === 132) {
        const prefix = Number(Hex.slice(publicKey, 0, 1));
        const x = BigInt(Hex.slice(publicKey, 1, 33));
        const y = BigInt(Hex.slice(publicKey, 33, 65));
        return {
            prefix,
            x,
            y,
        };
    }
    const prefix = Number(Hex.slice(publicKey, 0, 1));
    const x = BigInt(Hex.slice(publicKey, 1, 33));
    return {
        prefix,
        x,
    };
}
/**
 * Serializes a {@link ox#PublicKey.PublicKey} to {@link ox#Bytes.Bytes}.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.from({
 *   prefix: 4,
 *   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 *   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * })
 *
 * const bytes = PublicKey.toBytes(publicKey) // [!code focus]
 * // @log: Uint8Array [128, 3, 131, ...]
 * ```
 *
 * @param publicKey - The public key to serialize.
 * @returns The serialized public key.
 */
export function toBytes(publicKey, options = {}) {
    return Bytes.fromHex(toHex(publicKey, options));
}
/**
 * Serializes a {@link ox#PublicKey.PublicKey} to {@link ox#Hex.Hex}.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const publicKey = PublicKey.from({
 *   prefix: 4,
 *   x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
 *   y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
 * })
 *
 * const hex = PublicKey.toHex(publicKey) // [!code focus]
 * // @log: '0x048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5'
 * ```
 *
 * @param publicKey - The public key to serialize.
 * @returns The serialized public key.
 */
export function toHex(publicKey, options = {}) {
    assert(publicKey);
    const { prefix, x, y } = publicKey;
    const { includePrefix = true } = options;
    const publicKey_ = Hex.concat(includePrefix ? Hex.fromNumber(prefix, { size: 1 }) : '0x', Hex.fromNumber(x, { size: 32 }), 
    // If the public key is not compressed, add the y coordinate.
    typeof y === 'bigint' ? Hex.fromNumber(y, { size: 32 }) : '0x');
    return publicKey_;
}
/**
 * Validates a {@link ox#PublicKey.PublicKey}. Returns `true` if valid, `false` otherwise.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * const valid = PublicKey.validate({
 *   prefix: 4,
 *   y: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
 * })
 * // @log: false
 * ```
 *
 * @param publicKey - The public key object to assert.
 */
export function validate(publicKey, options = {}) {
    try {
        assert(publicKey, options);
        return true;
    }
    catch (_error) {
        return false;
    }
}
/**
 * Thrown when a public key is invalid.
 *
 * @example
 * ```ts twoslash
 * import { PublicKey } from 'ox'
 *
 * PublicKey.assert({ y: 1n })
 * // @error: PublicKey.InvalidError: Value `{"y":1n}` is not a valid public key.
 * // @error: Public key must contain:
 * // @error: - an `x` and `prefix` value (compressed)
 * // @error: - an `x`, `y`, and `prefix` value (uncompressed)
 * ```
 */
export class InvalidError extends Errors.BaseError {
    constructor({ publicKey }) {
        super(`Value \`${Json.stringify(publicKey)}\` is not a valid public key.`, {
            metaMessages: [
                'Public key must contain:',
                '- an `x` and `prefix` value (compressed)',
                '- an `x`, `y`, and `prefix` value (uncompressed)',
            ],
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'PublicKey.InvalidError'
        });
    }
}
/** Thrown when a public key has an invalid prefix. */
export class InvalidPrefixError extends Errors.BaseError {
    constructor({ prefix, cause }) {
        super(`Prefix "${prefix}" is invalid.`, {
            cause,
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'PublicKey.InvalidPrefixError'
        });
    }
}
/** Thrown when the public key has an invalid prefix for a compressed public key. */
export class InvalidCompressedPrefixError extends Errors.BaseError {
    constructor() {
        super('Prefix must be 2 or 3 for compressed public keys.');
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'PublicKey.InvalidCompressedPrefixError'
        });
    }
}
/** Thrown when the public key has an invalid prefix for an uncompressed public key. */
export class InvalidUncompressedPrefixError extends Errors.BaseError {
    constructor() {
        super('Prefix must be 4 for uncompressed public keys.');
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'PublicKey.InvalidUncompressedPrefixError'
        });
    }
}
/** Thrown when the public key has an invalid serialized size. */
export class InvalidSerializedSizeError extends Errors.BaseError {
    constructor({ publicKey }) {
        super(`Value \`${publicKey}\` is an invalid public key size.`, {
            metaMessages: [
                'Expected: 33 bytes (compressed + prefix), 64 bytes (uncompressed) or 65 bytes (uncompressed + prefix).',
                `Received ${Hex.size(Hex.from(publicKey))} bytes.`,
            ],
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'PublicKey.InvalidSerializedSizeError'
        });
    }
}
//# sourceMappingURL=PublicKey.js.map