"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fromLegacyBackup = exports.fromUnifiedBackup = exports.toUnifiedBackup = void 0;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const ZStackStructs = __importStar(require("../adapter/z-stack/structs"));
const ZStackUtils = __importStar(require("../adapter/z-stack/utils"));
/**
 * Converts internal backup format to unified backup storage format as described by
 * [zigpy/open-coordinator-backup](https://github.com/zigpy/open-coordinator-backup).
 *
 * @param backup Backup to create unified backup format from.
 */
const toUnifiedBackup = async (backup) => {
    const packageInfo = JSON.parse(fs.readFileSync(path.join(__dirname, '../../', 'package.json')).toString());
    return {
        metadata: {
            format: 'zigpy/open-coordinator-backup',
            version: 1,
            source: `${packageInfo.name}@${packageInfo.version}`,
            internal: {
                date: new Date().toISOString(),
                ...(backup.znp ? { znpVersion: backup.znp?.version ?? undefined } : /* v8 ignore next */ undefined),
                ...(backup.ezsp ? { ezspVersion: backup.ezsp?.version ?? undefined } : /* v8 ignore next */ undefined),
            },
        },
        stack_specific: {
            /* v8 ignore next */
            ...(backup.znp ? { zstack: { tclk_seed: backup.znp?.trustCenterLinkKeySeed?.toString('hex') || undefined } } : undefined),
            ...(backup.ezsp ? { ezsp: { hashed_tclk: backup.ezsp?.hashed_tclk?.toString('hex') || undefined } } : /* v8 ignore next */ undefined),
        },
        coordinator_ieee: backup.coordinatorIeeeAddress.toString('hex'),
        pan_id: backup.networkOptions.panId.toString(16),
        extended_pan_id: backup.networkOptions.extendedPanId.toString('hex'),
        nwk_update_id: backup.networkUpdateId || 0,
        security_level: backup.securityLevel,
        channel: backup.logicalChannel,
        channel_mask: backup.networkOptions.channelList,
        network_key: {
            key: backup.networkOptions.networkKey.toString('hex'),
            sequence_number: backup.networkKeyInfo.sequenceNumber,
            frame_counter: backup.networkKeyInfo.frameCounter,
        },
        devices: backup.devices.map((device) => {
            return {
                nwk_address: device.networkAddress !== null ? device.networkAddress.toString(16) : /* v8 ignore next */ null,
                ieee_address: device.ieeeAddress.toString('hex'),
                is_child: device.isDirectChild,
                link_key: device.linkKey
                    ? { key: device.linkKey.key.toString('hex'), rx_counter: device.linkKey.rxCounter, tx_counter: device.linkKey.txCounter }
                    : undefined,
            };
        }),
    };
};
exports.toUnifiedBackup = toUnifiedBackup;
/**
 * Converts unified backup storage format to internal backup format.
 *
 * @param backup Unified format to convert to internal backup format.
 */
const fromUnifiedBackup = (backup) => {
    const tclkSeedString = backup.stack_specific?.zstack?.tclk_seed || undefined;
    return {
        networkOptions: {
            panId: Number.parseInt(backup.pan_id, 16),
            extendedPanId: Buffer.from(backup.extended_pan_id, 'hex'),
            channelList: backup.channel_mask,
            networkKey: Buffer.from(backup.network_key.key, 'hex'),
            networkKeyDistribute: false,
        },
        logicalChannel: backup.channel,
        networkKeyInfo: {
            sequenceNumber: backup.network_key.sequence_number,
            frameCounter: backup.network_key.frame_counter,
        },
        coordinatorIeeeAddress: Buffer.from(backup.coordinator_ieee, 'hex'),
        securityLevel: backup.security_level,
        networkUpdateId: backup.nwk_update_id,
        devices: backup.devices.map((device) => ({
            networkAddress: device.nwk_address ? Number.parseInt(device.nwk_address, 16) : null,
            ieeeAddress: Buffer.from(device.ieee_address, 'hex'),
            isDirectChild: typeof device.is_child === 'boolean' ? device.is_child : true,
            linkKey: device.link_key
                ? { key: Buffer.from(device.link_key.key, 'hex'), rxCounter: device.link_key.rx_counter, txCounter: device.link_key.tx_counter }
                : undefined,
        })),
        znp: {
            version: backup.metadata.internal?.znpVersion || undefined,
            trustCenterLinkKeySeed: tclkSeedString ? Buffer.from(tclkSeedString, 'hex') : undefined,
        },
        ezsp: {
            version: backup.metadata.internal?.ezspVersion || undefined,
            hashed_tclk: backup.stack_specific?.ezsp?.hashed_tclk ? Buffer.from(backup.stack_specific.ezsp.hashed_tclk, 'hex') : undefined,
        },
    };
};
exports.fromUnifiedBackup = fromUnifiedBackup;
/**
 * Converts legacy Zigbee2MQTT format to internal backup format.
 *
 * @param backup Legacy format to convert.
 */
const fromLegacyBackup = (backup) => {
    if (!backup.data.ZCD_NV_NIB) {
        throw new Error('Backup corrupted - missing NIB');
    }
    else if (!backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO) {
        throw new Error('Backup corrupted - missing active key info');
    }
    else if (!backup.data.ZCD_NV_PRECFGKEY_ENABLE) {
        throw new Error('Backup corrupted - missing pre-configured key enable attribute');
    }
    else if (!backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE && !backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START) {
        throw new Error('Backup corrupted - missing network security material table');
    }
    else if (!backup.data.ZCD_NV_EXTADDR) {
        throw new Error('Backup corrupted - missing adapter IEEE address NV entry');
    }
    const ieeeAddress = Buffer.from(backup.data.ZCD_NV_EXTADDR.value).reverse();
    const nib = ZStackStructs.nib(Buffer.from(backup.data.ZCD_NV_NIB.value));
    const activeKeyInfo = ZStackStructs.nwkKeyDescriptor(Buffer.from(backup.data.ZCD_NV_NWK_ACTIVE_KEY_INFO.value));
    const preconfiguredKeyEnabled = backup.data.ZCD_NV_PRECFGKEY_ENABLE.value[0] !== 0x00;
    const nwkSecMaterialSource = backup.data.ZCD_NV_EX_NWK_SEC_MATERIAL_TABLE || backup.data.ZCD_NV_LEGACY_NWK_SEC_MATERIAL_TABLE_START;
    const nwkSecMaterialEntry = ZStackStructs.nwkSecMaterialDescriptorEntry(Buffer.from(nwkSecMaterialSource.value));
    return {
        networkOptions: {
            panId: nib.nwkPanId,
            extendedPanId: nib.extendedPANID,
            channelList: ZStackUtils.unpackChannelList(nib.channelList),
            networkKey: activeKeyInfo.key,
            networkKeyDistribute: preconfiguredKeyEnabled,
        },
        logicalChannel: nib.nwkLogicalChannel,
        networkKeyInfo: {
            sequenceNumber: activeKeyInfo.keySeqNum,
            frameCounter: nwkSecMaterialEntry.FrameCounter,
        },
        coordinatorIeeeAddress: ieeeAddress,
        securityLevel: nib.SecurityLevel,
        networkUpdateId: nib.nwkUpdateId,
        devices: [],
    };
};
exports.fromLegacyBackup = fromLegacyBackup;
//# sourceMappingURL=backup.js.map