import { ENVIRONMENT } from "./core/types/dimo.js";
import { createPublicClient, createWalletClient, http } from "viem";
import { createKernelAccount, createKernelAccountClient, createZeroDevPaymasterClient, getCustomNonceKeyFromString, } from "@zerodev/sdk";
import { createBundlerClient } from "permissionless";
import { mintVehicleWithDeviceDefinition, mintVehicleWithDeviceDefinitionBatch, } from "./core/actions/mintVehicleWithDeviceDefinition.js";
import { setVehiclePermissions, setVehiclePermissionsBatch, setVehiclePermissionsBulk, } from "./core/actions/setPermissionsSACD.js";
import { CHAIN_ABI_MAPPING, ENV_MAPPING, ENV_NETWORK_MAPPING, OnChainErrors } from "./core/constants/mappings.js";
import { claimAftermarketDevice, claimAftermarketDeviceTypeHash } from "./core/actions/claimAftermarketDevice.js";
import { sendDIMOTokens } from "./core/actions/sendDIMOTokens.js";
import { pairAftermarketDevice } from "./core/actions/pairAftermarketDevice.js";
import { TurnkeyClient } from "@turnkey/http";
import { polygon } from "viem/chains";
import { burnVehicle, burnVehicleBatch } from "./core/actions/burnVehicle.js";
import { createAccount } from "@turnkey/viem";
import { walletClientToSmartAccountSigner } from "permissionless/utils";
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator";
import { privateKeyToAccount } from "viem/accounts";
import { transferVehicleAndAftermarketDeviceIDs } from "./core/actions/transferVehicleAndADs.js";
import { unpairAftermarketDevice } from "./core/actions/unpairAftermarketDevice.js";
import { ApiKeyStamper } from "@turnkey/api-key-stamper";
import { generateP256KeyPair, decryptBundle, getPublicKey } from "@turnkey/crypto";
import { uint8ArrayToHexString, uint8ArrayFromHexString } from "@turnkey/encoding";
import { claimAndPairDevice } from "./core/actions/claimAndPair.js";
export class KernelSigner {
    constructor(config) {
        var _a, _b, _c, _d, _e;
        this._init = false;
        this._walletSession = {
            active: false,
            expires: 0,
        };
        this.expire = 0;
        this.config = config;
        this.chain =
            (_c = ENV_NETWORK_MAPPING.get((_b = ENV_MAPPING.get((_a = this.config.environment) !== null && _a !== void 0 ? _a : "prod")) !== null && _b !== void 0 ? _b : ENVIRONMENT.PROD)) !== null && _c !== void 0 ? _c : polygon;
        this.contractMapping =
            CHAIN_ABI_MAPPING[(_e = ENV_MAPPING.get((_d = this.config.environment) !== null && _d !== void 0 ? _d : "prod")) !== null && _e !== void 0 ? _e : ENVIRONMENT.PROD].contracts;
        this.publicClient = createPublicClient({
            transport: http(this.config.rpcUrl),
            chain: this.chain,
        });
        this.bundlerClient = createBundlerClient({
            chain: this.chain,
            transport: http(this.config.bundlerUrl),
            entryPoint: this.config.entryPoint,
        });
    }
    async passkeyInit(subOrganizationId, walletAddress, stamper) {
        // let openSession = false;
        if (!this._init) {
            this._init = true;
            // openSession = true;
            this._subOrganizationId = subOrganizationId;
            this._walletAddress = walletAddress;
        }
        this.turnkeyClient = new TurnkeyClient({ baseUrl: this.config.turnkeyApiBaseUrl }, stamper);
        this.turnkeyPasskeyClient = new TurnkeyClient({ baseUrl: this.config.turnkeyApiBaseUrl }, stamper);
        const localAccount = await createAccount({
            // @ts-ignore
            client: this.turnkeyClient,
            organizationId: subOrganizationId,
            signWith: walletAddress,
            ethereumAddress: walletAddress,
        });
        const smartAccountClient = createWalletClient({
            account: localAccount,
            chain: this.chain,
            transport: http(this.config.rpcUrl),
        });
        const smartAccountSigner = walletClientToSmartAccountSigner(smartAccountClient);
        const ecdsaValidator = await signerToEcdsaValidator(this.publicClient, {
            signer: smartAccountSigner,
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        const account = await createKernelAccount(this.publicClient, {
            plugins: {
                sudo: ecdsaValidator,
            },
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        this.kernelClient = createKernelAccountClient({
            account,
            chain: this.chain,
            entryPoint: this.config.entryPoint,
            bundlerTransport: http(this.config.bundlerUrl),
            middleware: {
                // @ts-ignore
                sponsorUserOperation: async ({ userOperation }) => {
                    const zerodevPaymaster = createZeroDevPaymasterClient({
                        chain: this.chain,
                        entryPoint: this.config.entryPoint,
                        transport: http(this.config.paymasterUrl),
                    });
                    return zerodevPaymaster.sponsorUserOperation({
                        userOperation,
                        entryPoint: this.config.entryPoint,
                    });
                },
            },
        });
        this.kernelAddress = account.address;
        // if (openSession) {
        //   await this.openSessionWithPasskey();
        // }
    }
    async openSessionWithPasskey() {
        var _a;
        if (!this.config.useWalletSession) {
            return {
                active: false,
                expires: 0,
            };
        }
        if (new Date(this.expire) > new Date(Date.now())) {
            return this._walletSession;
        }
        if (!this.turnkeyClient) {
            throw new Error("Turnkey client not initialized");
        }
        if (!this._subOrganizationId) {
            throw new Error("Sub organization id not set");
        }
        if (!this._walletAddress) {
            throw new Error("Wallet address not set");
        }
        if (!this.turnkeyPasskeyClient) {
            throw new Error("Turnkey passkey client not initialized");
        }
        const timestamp = Date.now();
        const expiration = timestamp + parseInt(this.config.sessionTimeoutSeconds) * 1000;
        const key = generateP256KeyPair();
        const targetPubHex = key.publicKeyUncompressed;
        const sessionData = await this.turnkeyPasskeyClient.createReadWriteSession({
            organizationId: this._subOrganizationId,
            type: "ACTIVITY_TYPE_CREATE_READ_WRITE_SESSION_V2",
            timestampMs: timestamp.toString(),
            parameters: {
                targetPublicKey: targetPubHex,
                expirationSeconds: this.config.sessionTimeoutSeconds,
            },
        });
        const bundle = (_a = sessionData.activity.result.createReadWriteSessionResultV2) === null || _a === void 0 ? void 0 : _a.credentialBundle;
        const decryptedBundle = decryptBundle(bundle, key.privateKey);
        const privateKey = uint8ArrayToHexString(decryptedBundle);
        const apiStamper = new ApiKeyStamper({
            apiPublicKey: uint8ArrayToHexString(getPublicKey(uint8ArrayFromHexString(privateKey), true)),
            apiPrivateKey: privateKey,
        });
        const turnkeyClient = new TurnkeyClient({ baseUrl: this.config.turnkeyApiBaseUrl }, apiStamper);
        const localAccount = await createAccount({
            // @ts-ignore
            client: turnkeyClient,
            organizationId: this._subOrganizationId,
            signWith: this._walletAddress,
            ethereumAddress: this._walletAddress,
        });
        const smartAccountClient = createWalletClient({
            account: localAccount,
            chain: this.chain,
            transport: http(this.config.rpcUrl),
        });
        const smartAccountSigner = walletClientToSmartAccountSigner(smartAccountClient);
        const ecdsaValidator = await signerToEcdsaValidator(this.publicClient, {
            signer: smartAccountSigner,
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        const account = await createKernelAccount(this.publicClient, {
            plugins: {
                sudo: ecdsaValidator,
            },
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        this.sessionClient = createKernelAccountClient({
            account,
            chain: this.chain,
            entryPoint: this.config.entryPoint,
            bundlerTransport: http(this.config.bundlerUrl),
            middleware: {
                // @ts-ignore
                sponsorUserOperation: async ({ userOperation }) => {
                    const zerodevPaymaster = createZeroDevPaymasterClient({
                        chain: this.chain,
                        entryPoint: this.config.entryPoint,
                        transport: http(this.config.paymasterUrl),
                    });
                    return zerodevPaymaster.sponsorUserOperation({
                        userOperation,
                        entryPoint: this.config.entryPoint,
                    });
                },
            },
        });
        this._walletSession = {
            active: true,
            expires: expiration,
        };
        this.expire = expiration;
        return this._walletSession;
    }
    walletSessionActive() {
        return new Date(this.expire) < new Date(Date.now());
    }
    sessionInfos() {
        return this._walletSession;
    }
    async privateKeyInit(privateKey) {
        const ecdsaValidator = await signerToEcdsaValidator(this.publicClient, {
            signer: privateKeyToAccount(privateKey),
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        const account = await createKernelAccount(this.publicClient, {
            plugins: {
                sudo: ecdsaValidator,
            },
            entryPoint: this.config.entryPoint,
            // @ts-ignore
            kernelVersion: this.config.kernelVersion,
        });
        this.kernelClient = createKernelAccountClient({
            account,
            chain: this.chain,
            entryPoint: this.config.entryPoint,
            bundlerTransport: http(this.config.bundlerUrl),
            middleware: {
                // @ts-ignore
                sponsorUserOperation: async ({ userOperation }) => {
                    const zerodevPaymaster = createZeroDevPaymasterClient({
                        chain: this.chain,
                        entryPoint: this.config.entryPoint,
                        transport: http(this.config.paymasterUrl),
                    });
                    return zerodevPaymaster.sponsorUserOperation({
                        userOperation,
                        entryPoint: this.config.entryPoint,
                    });
                },
            },
        });
        this.kernelAddress = account.address;
    }
    async mintVehicleWithDeviceDefinition(args, waitForReceipt = true) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        let mintVehicleCallData;
        if (!Array.isArray(args)) {
            mintVehicleCallData = await mintVehicleWithDeviceDefinition(args, client, this.config.environment);
        }
        else {
            if (args.length >= 25) {
                throw Error("Batch minting limit: 25");
            }
            mintVehicleCallData = await mintVehicleWithDeviceDefinitionBatch(args, client, this.config.environment);
        }
        const nonceKey = getCustomNonceKeyFromString("nonce key", this.config.entryPoint);
        const nonce = await this.kernelClient.account.getNonce(nonceKey);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: mintVehicleCallData,
                nonce: nonce,
            },
        });
        if (!waitForReceipt) {
            return {
                userOperationHash: userOpHash,
                status: "pending",
            };
        }
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async setVehiclePermissions(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        let setVehiclePermissionsCallData;
        if (!Array.isArray(args)) {
            setVehiclePermissionsCallData = await setVehiclePermissions(args, client, this.config.environment);
        }
        else {
            if (args.length >= 25) {
                throw Error("Batch minting limit: 25");
            }
            setVehiclePermissionsCallData = await setVehiclePermissionsBatch(args, client, this.config.environment);
        }
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: setVehiclePermissionsCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async setVehiclePermissionsBulk(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const setVehiclePermissionsBulkCallData = await setVehiclePermissionsBulk(args, client, this.config.environment);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: setVehiclePermissionsBulkCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async sendDIMOTokens(args) {
        const setVehiclePermissionsCallData = await sendDIMOTokens(args, this.kernelClient, this.config.environment);
        const userOpHash = await this.kernelClient.sendUserOperation({
            userOperation: {
                callData: setVehiclePermissionsCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    claimAftermarketDeviceTypeHash(aftermarketDeviceNode, owner) {
        return claimAftermarketDeviceTypeHash(aftermarketDeviceNode, owner, this.config.environment);
    }
    async claimAftermarketDevice(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const claimADCallData = await claimAftermarketDevice(args, client, this.config.environment);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: claimADCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async pairAftermarketDevice(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const pairADCallData = await pairAftermarketDevice(args, client, this.config.environment);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: pairADCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async claimAndPairAftermarketDevice(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const claimAndPairCallData = await claimAndPairDevice(args, client, this.config.environment);
        const claimAndPairADHash = await client.sendUserOperation({
            userOperation: {
                callData: claimAndPairCallData,
            },
        });
        const result = await this.bundlerClient.waitForUserOperationReceipt({ hash: claimAndPairADHash });
        return result;
    }
    async burnVehicle(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        let burnVehicleCallData;
        if (!Array.isArray(args)) {
            burnVehicleCallData = await burnVehicle(args, client, this.config.environment);
        }
        else {
            if (args.length >= 25) {
                throw Error("Batch minting limit: 25");
            }
            burnVehicleCallData = await burnVehicleBatch(args, client, this.config.environment);
        }
        let userOpHash;
        try {
            userOpHash = await client.sendUserOperation({
                userOperation: {
                    callData: burnVehicleCallData,
                },
            });
            const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
            return txResult;
        }
        catch (error) {
            if (error) {
                error = error.toString();
            }
            for (const [failReason, cleanError] of Object.entries(OnChainErrors)) {
                if (error.includes(failReason)) {
                    throw new Error(cleanError);
                }
                throw new Error(error);
            }
        }
    }
    async transferVehicleAndAftermarketDevices(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const burnVehicleCallData = await transferVehicleAndAftermarketDeviceIDs(args, client, this.config.environment);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: burnVehicleCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async unpairAftermarketDevice(args) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        const unpairADCallData = await unpairAftermarketDevice(args, client, this.config.environment);
        const userOpHash = await client.sendUserOperation({
            userOperation: {
                callData: unpairADCallData,
            },
        });
        const txResult = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash });
        return txResult;
    }
    async signTypedData(arg) {
        let client = this.kernelClient;
        if (this.config.useWalletSession) {
            await this.openSessionWithPasskey();
            if (this.sessionClient) {
                client = this.sessionClient;
            }
        }
        return client.signTypedData(arg);
    }
}
//# sourceMappingURL=KernelSigner.js.map