import { __esDecorate, __runInitializers } from "tslib";
import { getImplementedVersion } from "@zwave-js/cc";
import { ConfigManager } from "@zwave-js/config";
import { MessagePriority, NOT_KNOWN, SecurityClass, ZWaveError, ZWaveErrorCodes, securityClassOrder, } from "@zwave-js/core";
import { Message, MessageType, expectedResponse, messageTypes, priority, } from "@zwave-js/serial";
import { SendDataRequest } from "@zwave-js/serial/serialapi";
import sinon from "sinon";
import * as nodeUtils from "../node/utils.js";
const MockRequestMessageWithExpectation_FunctionType = 0xfa;
const MockRequestMessageWithoutExpectation_FunctionType = 0xfb;
const MockResponseMessage_FunctionType = 0xff;
let MockRequestMessageWithExpectation = (() => {
    let _classDecorators = [messageTypes(MessageType.Request, MockRequestMessageWithExpectation_FunctionType), expectedResponse(MockResponseMessage_FunctionType), priority(MessagePriority.Normal)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = Message;
    var MockRequestMessageWithExpectation = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            MockRequestMessageWithExpectation = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
    };
    return MockRequestMessageWithExpectation = _classThis;
})();
export { MockRequestMessageWithExpectation };
let MockRequestMessageWithoutExpectation = (() => {
    let _classDecorators = [messageTypes(MessageType.Request, MockRequestMessageWithoutExpectation_FunctionType), priority(MessagePriority.Normal)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = Message;
    var MockRequestMessageWithoutExpectation = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            MockRequestMessageWithoutExpectation = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
    };
    return MockRequestMessageWithoutExpectation = _classThis;
})();
export { MockRequestMessageWithoutExpectation };
let MockResponseMessage = (() => {
    let _classDecorators = [messageTypes(MessageType.Response, MockResponseMessage_FunctionType)];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = Message;
    var MockResponseMessage = class extends _classSuper {
        static { _classThis = this; }
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
            MockResponseMessage = _classThis = _classDescriptor.value;
            if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            __runInitializers(_classThis, _classExtraInitializers);
        }
    };
    return MockResponseMessage = _classThis;
})();
export { MockResponseMessage };
export const mockDriverDummyCallbackId = 0xfe;
// oxlint-disable-next-line typescript/explicit-module-boundary-types
export function createEmptyMockDriver() {
    const ret = {
        sendMessage: sinon.stub().callsFake(() => Promise.resolve()),
        sendCommand: sinon.stub(),
        getSupportedCCVersion: sinon.stub(),
        getSafeCCVersion: sinon.stub(),
        isCCSecure: sinon.stub().callsFake(() => false),
        getNextCallbackId: sinon
            .stub()
            .callsFake(() => mockDriverDummyCallbackId),
        controller: {
            nodes: new Map(),
            ownNodeId: 1,
        },
        get nodes() {
            return ret.controller.nodes;
        },
        get ownNodeId() {
            return ret.controller.ownNodeId;
        },
        valueDB: new Map(),
        getValueDB: (nodeId) => {
            return ret.controller.nodes.get(nodeId).valueDB;
        },
        metadataDB: new Map(),
        networkCache: new Map(),
        cacheGet: sinon.stub().callsFake((cacheKey, options) => {
            let _ret = ret.networkCache.get(cacheKey);
            if (_ret !== undefined
                && typeof options?.reviver === "function") {
                try {
                    _ret = options.reviver(_ret);
                }
                catch {
                    // ignore, invalid entry
                }
            }
            return _ret;
        }),
        cacheSet: sinon.stub().callsFake((cacheKey, value, options) => {
            if (value !== undefined
                && typeof options?.serializer === "function") {
                value = options.serializer(value);
            }
            if (value === undefined) {
                ret.networkCache.delete(cacheKey);
            }
            else {
                ret.networkCache.set(cacheKey, value);
            }
        }),
        options: {
            timeouts: {
                ack: 1000,
                byte: 150,
                response: 1600,
                report: 1600,
                nonce: 5000,
                sendDataCallback: 65000,
            },
            attempts: {
                sendData: 3,
                controller: 3,
            },
        },
        driverLog: new Proxy({}, {
            get() {
                return () => {
                    /* intentionally empty */
                };
            },
        }),
        controllerLog: new Proxy({}, {
            get() {
                return () => {
                    /* intentionally empty */
                };
            },
        }),
        configManager: new ConfigManager(),
        getLogConfig: () => {
            return {
                enabled: false,
                level: "info",
            };
        },
    };
    ret.sendCommand.callsFake(async (command, options) => {
        const msg = new SendDataRequest({ command });
        const resp = await ret.sendMessage(msg, options);
        return resp?.command;
    });
    ret.getSupportedCCVersion.callsFake((ccId, nodeId, endpointIndex = 0) => {
        if (ret.controller?.nodes instanceof Map
            && ret.controller.nodes.has(nodeId)) {
            const node = ret.controller.nodes.get(nodeId);
            const ccVersion = node
                .getEndpoint(endpointIndex)
                .getCCVersion(ccId);
            return ccVersion;
        }
        return 0;
    });
    ret.getSafeCCVersion.callsFake((ccId, nodeId, endpointIndex = 0) => {
        return (ret.getSupportedCCVersion(ccId, nodeId, endpointIndex)
            || getImplementedVersion(ccId));
    });
    return ret;
}
export function createTestNode(host, options) {
    const endpointCache = new Map();
    const securityClasses = new Map();
    const ret = {
        id: options.id,
        ...createTestEndpoint(host, {
            nodeId: options.id,
            index: 0,
            commandClasses: options.commandClasses,
        }),
        isListening: options.isListening ?? true,
        isFrequentListening: options.isFrequentListening ?? false,
        get canSleep() {
            if (ret.isListening == undefined)
                return undefined;
            if (ret.isFrequentListening == undefined)
                return undefined;
            return !ret.isListening && !ret.isFrequentListening;
        },
        setEndpoint: (endpoint) => {
            endpointCache.set(endpoint.index, createTestEndpoint(host, {
                nodeId: options.id,
                index: endpoint.index,
                commandClasses: endpoint.commandClasses,
            }));
        },
        getEndpoint: ((index) => {
            if (index === 0)
                return ret;
            if (!endpointCache.has(index)) {
                if (!options.endpoints?.[index]) {
                    return undefined;
                }
                ret.setEndpoint({
                    nodeId: options.id,
                    index,
                    commandClasses: options.endpoints[index].commandClasses,
                });
            }
            return endpointCache.get(index);
        }),
        getEndpointOrThrow(index) {
            const ep = ret.getEndpoint(index);
            if (!ep) {
                throw new ZWaveError(`Endpoint ${index} does not exist on Node ${ret.id}`, ZWaveErrorCodes.Controller_EndpointNotFound);
            }
            return ep;
        },
        // These are copied from Node.ts
        getHighestSecurityClass() {
            if (securityClasses.size === 0)
                return undefined;
            let missingSome = false;
            for (const secClass of securityClassOrder) {
                if (securityClasses.get(secClass) === true)
                    return secClass;
                if (!securityClasses.has(secClass)) {
                    missingSome = true;
                }
            }
            // If we don't have the info for every security class, we don't know the highest one yet
            return missingSome ? undefined : SecurityClass.None;
        },
        hasSecurityClass(securityClass) {
            return securityClasses.get(securityClass);
        },
        setSecurityClass(securityClass, granted) {
            securityClasses.set(securityClass, granted);
        },
        get isSecure() {
            const securityClass = ret.getHighestSecurityClass();
            if (securityClass == undefined)
                return NOT_KNOWN;
            if (securityClass === SecurityClass.None)
                return false;
            return true;
        },
    };
    endpointCache.set(0, ret);
    // If the number of endpoints are given, use them as the individual endpoint count
    if (options.endpoints) {
        nodeUtils.setIndividualEndpointCount(host, ret.id, Object.keys(options.endpoints).length);
        nodeUtils.setEndpointIndizes(host, ret.id, Object.keys(options.endpoints).map((index) => parseInt(index, 10)));
        nodeUtils.setAggregatedEndpointCount(host, ret.id, 0);
        nodeUtils.setMultiChannelInterviewComplete(host, ret.id, true);
    }
    return ret;
}
export function createTestEndpoint(host, options) {
    const ret = {
        virtual: false,
        nodeId: options.nodeId,
        index: options.index,
        supportsCC: (cc) => {
            const ccInfo = options.commandClasses?.[cc];
            if (!ccInfo)
                return false;
            return ccInfo.isSupported ?? true;
        },
        controlsCC: (cc) => {
            const ccInfo = options.commandClasses?.[cc];
            if (!ccInfo)
                return false;
            return ccInfo.isControlled ?? false;
        },
        isCCSecure: (cc) => {
            const ccInfo = options.commandClasses?.[cc];
            if (!ccInfo)
                return false;
            return ccInfo.secure ?? false;
        },
        getCCVersion: (cc) => {
            const ccInfo = options.commandClasses?.[cc];
            const defaultVersion = ccInfo?.isSupported
                ? getImplementedVersion(cc)
                : 0;
            return ccInfo?.version
                ?? host.getNode(options.nodeId)?.getCCVersion(cc)
                ?? defaultVersion;
        },
    };
    return ret;
}
//# sourceMappingURL=mocks.js.map