"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecuXKey = exports.SecuXError = void 0;
const terra_js_1 = require("@terra-money/terra.js");
const terra_js_2 = require("@terra-money/terra.js");
const transport_webusb_1 = require("@secux/transport-webusb");
const transport_webble_1 = require("@secux/transport-webble");
const app_1 = __importDefault(require("./app"));
const secp256k1_1 = require("secp256k1");
const LUNA_COIN_TYPE = 330;
const REQUIRED_APP_VERSION = "2.16";
class SecuXError extends Error {
    constructor(message) {
        super(message);
        this.name = "SecuXError";
    }
}
exports.SecuXError = SecuXError;
class SecuXKey extends terra_js_1.Key {
    constructor(transport) {
        super();
        this.transport = transport;
        this.path = [44, LUNA_COIN_TYPE, 0, 0, 0];
        this.app = new app_1.default(this.transport);
    }
    get accAddress() {
        if (!this.publicKey) {
            throw new Error("SecuX is unintialized. Initialize it first.");
        }
        this._accAddress = app_1.default.getBech32FromPK("terra", this.compressed_pk);
        return this._accAddress;
    }
    static create(transport, index, bluetooth) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!transport) {
                transport = yield createTransport(bluetooth);
            }
            const key = new SecuXKey(transport);
            if (index != undefined) {
                key.path[4] = index;
            }
            if (transport && typeof transport.on === "function") {
                transport.on("disconnect", () => {
                    key.transport = null;
                });
            }
            yield key.initialize(index).catch(handleConnectError);
            return key;
        });
    }
    initialize(index) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = yield this.app.initialize();
            const deviceFwVersion = this.app.getVersion();
            console.log(deviceFwVersion);
            yield this.loadAccountDetails(index);
        });
    }
    loadAccountDetails(index) {
        return __awaiter(this, void 0, void 0, function* () {
            this.compressed_pk = yield this.transport.getPublickey(`m/44'/330'/0'/0/${index}`, 0);
            this.publicKey = new terra_js_2.SimplePublicKey(Buffer.from(this.compressed_pk).toString("base64"));
            this._accAddress = app_1.default.getBech32FromPK("terra", this.compressed_pk);
            return this;
        });
    }
    sign(message) {
        return __awaiter(this, void 0, void 0, function* () {
            const res = yield this.app.sign(this.path, message);
            var DER_signature = (0, secp256k1_1.signatureExport)(Buffer.from(res.signature).slice(0, 64));
            console.log(Buffer.from((0, secp256k1_1.signatureImport)(DER_signature)).toString('hex'));
            return Buffer.from((0, secp256k1_1.signatureImport)(DER_signature));
        });
    }
    createSignature(_tx) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error("direct sign mode is not supported");
        });
    }
    getAppAddressAndPubKey() {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error("getAppAddressAndPubKey is not supported");
        });
    }
    getAppInfo() {
        return this.app.getInfo();
    }
    getAppDeviceInfo() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.app.getDeviceInfo();
        });
    }
    getAppVersion() {
        return this.app.getVersion();
    }
    showAddressAndPubKey() {
        return __awaiter(this, void 0, void 0, function* () {
            return this.app.showAddressAndPubKey(this.path, 'terra');
        });
    }
}
exports.SecuXKey = SecuXKey;
const handleConnectError = (err) => {
    const message = err.message.trim();
    if (message.startsWith("The device is already open")) {
        return;
    }
    if (err.name === "TransportOpenUserCancelled") {
        throw new SecuXError("Couldn't find the SecuX. Check the SecuX is plugged in and unlocked.");
    }
    if (message.startsWith("No WebUSB interface found for the SecuX device")) {
        throw new SecuXError(`Couldn't connect to a SecuX device. Use SecuX Live to upgrade the SecuX firmware to version ${REQUIRED_APP_VERSION} or later.`);
    }
    if (message.startsWith("Unable to claim interface")) {
        throw new SecuXError("Couldn't access SecuX device. Is it being used in another tab?");
    }
    if (message.startsWith("any not defined")) {
        throw new SecuXError("Couldn't access SecuX device. Is it being used in another tab?");
    }
    if (message.startsWith("Not supported")) {
        throw new SecuXError("This browser doesn't support WebUSB yet. Update it to the latest version.");
    }
    if (message.startsWith("No device selected")) {
        throw new SecuXError("Couldn't find the SecuX. Check the SecuX is plugged in and unlocked.");
    }
    throw err;
};
const checkSecuXErrors = (response) => {
    if (!response) {
        return;
    }
    const { error_message, device_locked } = response;
    if (device_locked) {
        throw new SecuXError("SecuX's screensaver mode is on");
    }
    if (error_message.startsWith("TransportRaceCondition")) {
        throw new SecuXError("Finish previous action in SecuX");
    }
    else if (error_message.startsWith("DisconnectedDeviceDuringOperation")) {
        throw new SecuXError("Open the Terra app in the SecuX");
    }
    switch (error_message) {
        case "U2F: Timeout":
            throw new SecuXError("Couldn't find a connected and unlocked SecuX device");
        case "App does not seem to be open":
            throw new SecuXError("Open the Terra app in the SecuX");
        case "Command not allowed":
            throw new SecuXError("Transaction rejected");
        case "Transaction rejected":
            throw new SecuXError("User rejected the transaction");
        case "Unknown Status Code: 26628":
            throw new SecuXError("SecuX's screensaver mode is on");
        case "Instruction not supported":
            throw new SecuXError("Check the SecuX is running latest version of Terra");
        case "No errors":
            break;
        default:
            throw new SecuXError(error_message);
    }
};
const isWindows = (platform) => platform.indexOf("Win") > -1;
const checkBrowser = (userAgent) => {
    const ua = userAgent.toLowerCase();
    const isChrome = /chrome|crios/.test(ua) && !/edge|opr\//.test(ua);
    const isBrave = isChrome && !window.google;
    if (!isChrome && !isBrave) {
        throw new SecuXError("This browser doesn't support SecuX devices");
    }
    return isChrome ? "chrome" : "brave";
};
function createTransport(bluetooth) {
    return __awaiter(this, void 0, void 0, function* () {
        let transport;
        if (bluetooth) {
            transport = yield transport_webble_1.SecuxWebBLE.Create(() => console.log('connected'), () => __awaiter(this, void 0, void 0, function* () {
                console.log('disconnected');
            }));
            yield transport.Connect();
            yield transport.SendOTP('42960705');
        }
        else {
            transport = yield transport_webusb_1.SecuxWebUSB.Create(() => console.log('connected'), () => __awaiter(this, void 0, void 0, function* () {
                console.log('disconnected');
            }));
            yield transport.Connect();
        }
        return transport;
    });
}
