import invariant from "tiny-invariant";
import type { Address, PublicClient, SignMessageReturnType } from "viem";
import type { ChainId } from "./constants.js";
import type { BaseConfig } from "./createConfig.js";
import { childLogger, createLogger } from "./logging/logger.js";
import type { Logger, LoggingOptions } from "./logging/types.js";
import type * as rustUtils from "./utils.d.ts";

export type CreateExternalKeyConfigParameters = {
    chainId: ChainId;
    /** URL of the relayer service */
    relayerUrl: string;
    /** URL of the websocket service */
    websocketUrl: string;
    /**
     * Function to sign messages using the user's own wallet.
     * This function receives the message as is, without hashing it.
     * ECDSA standards should be followed i.e. `v` should be `0x00` or `0x01`
     *
     */
    signMessage: (message: string) => Promise<SignMessageReturnType>;
    /** Wallet secrets generated by offline script */
    symmetricKey: `0x${string}`;
    walletId: string;
    publicKey: `0x${string}`;
    /** Viem client */
    viemClient?: PublicClient;
    /** Dark pool address */
    darkPoolAddress: Address;
    /** Utils */
    utils?: typeof rustUtils;
    /** Logging options */
    /** Optional logging configuration (silent by default). */
    logging?: LoggingOptions;
};

/**
 * Creates a configuration for users who want to manage their own wallet secrets.
 *
 * This configuration differs from the standard createConfig by allowing users to maintain
 * custody of their wallet secrets while still enabling interaction with the relayer for
 * wallet operations. Instead of managing keys internally, it accepts user-provided
 * cryptographic materials and signing functions.
 */
export function createExternalKeyConfig(
    parameters: CreateExternalKeyConfigParameters,
): ExternalConfig {
    const {
        relayerUrl,
        signMessage,
        symmetricKey,
        walletId,
        publicKey: initialPublicKey,
        websocketUrl,
        viemClient,
        darkPoolAddress,
    } = parameters;
    invariant(
        parameters.utils,
        "Utils must be provided by the package if not supplied by the user.",
    );

    let currentPublicKey = initialPublicKey;

    // Initialize logger (silent by default when not provided by consumer)
    const baseLogger = createLogger(parameters.logging);

    return {
        // External keychain
        signMessage,
        symmetricKey,
        walletId,
        get publicKey() {
            return currentPublicKey;
        },
        setPublicKey: (newPublicKey: `0x${string}`) => {
            currentPublicKey = newPublicKey;
        },
        // BaseConfig
        utils: parameters.utils,
        renegadeKeyType: "external" as const,
        relayerUrl,
        getLogger(namespace?: string): Logger {
            return namespace ? childLogger(baseLogger, { ns: namespace }) : baseLogger;
        },
        getBaseUrl: (route = "") => {
            const formattedRoute = route.startsWith("/") ? route : `/${route}`;
            return `${relayerUrl}/v0${formattedRoute}`;
        },
        getWebsocketBaseUrl: () => {
            return websocketUrl.replace(/\/$/, "");
        },
        getSymmetricKey: () => {
            return symmetricKey;
        },
        viemClient,
        darkPoolAddress,
        chainId: parameters.chainId,
    };
}

export type ExternalConfig = BaseConfig & {
    chainId: ChainId;
    relayerUrl: string;
    signMessage: (message: string) => Promise<SignMessageReturnType>;
    symmetricKey: `0x${string}`;
    walletId: string;
    publicKey: `0x${string}`;
    getBaseUrl: (route?: string) => string;
    renegadeKeyType: "external";
    setPublicKey: (newPublicKey: `0x${string}`) => void;
    viemClient?: PublicClient;
    darkPoolAddress: Address;
};
