"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSWVZFRawZclCommand = exports.shiftUtcSecondsByOffsetMonths = exports.formatUtcSecondsToIsoWithOffset = exports.getRuntimeLocalOffsetSeconds = exports.parseIsoWithOffsetToUtcSeconds = exports.deviceLocal2000ToUTCSeconds = exports.utcToDeviceLocal2000Seconds = exports.YEAR_2000_IN_UTC = void 0;
/**
 * Unix timestamp in seconds for `2000-01-01T00:00:00Z`.
 * Sonoff irrigation devices encode local datetimes relative to this base.
 */
exports.YEAR_2000_IN_UTC = Math.floor(Date.UTC(2000, 0, 1) / 1000);
/**
 * Convert Unix UTC seconds to device local-time seconds (2000 base).
 */
const utcToDeviceLocal2000Seconds = (utcSeconds, offsetSeconds) => {
    return utcSeconds + offsetSeconds - exports.YEAR_2000_IN_UTC;
};
exports.utcToDeviceLocal2000Seconds = utcToDeviceLocal2000Seconds;
/**
 * Convert device local-time seconds (2000 base) back to Unix UTC seconds.
 */
const deviceLocal2000ToUTCSeconds = (deviceSeconds, offsetSeconds) => {
    return deviceSeconds - offsetSeconds + exports.YEAR_2000_IN_UTC;
};
exports.deviceLocal2000ToUTCSeconds = deviceLocal2000ToUTCSeconds;
/**
 * Parse ISO 8601 datetime (must include `Z` or `±HH:mm`) to Unix UTC seconds.
 * Returns `undefined` when format is invalid or parsed value is negative.
 */
const parseIsoWithOffsetToUtcSeconds = (value) => {
    if (!/(Z|[+-]\d{2}:\d{2})$/.test(value)) {
        return;
    }
    const timeMs = new Date(value).getTime();
    if (Number.isNaN(timeMs)) {
        return;
    }
    const seconds = Math.floor(timeMs / 1000);
    if (seconds < 0) {
        return;
    }
    return seconds;
};
exports.parseIsoWithOffsetToUtcSeconds = parseIsoWithOffsetToUtcSeconds;
/**
 * Get runtime local timezone offset in seconds for the specified UTC timestamp.
 */
const getRuntimeLocalOffsetSeconds = (utcSeconds) => {
    const date = new Date(utcSeconds * 1000);
    return -date.getTimezoneOffset() * 60;
};
exports.getRuntimeLocalOffsetSeconds = getRuntimeLocalOffsetSeconds;
/**
 * Format Unix UTC seconds to ISO 8601 with the specified timezone offset.
 * Falls back to the runtime local timezone offset when not provided.
 */
const formatUtcSecondsToIsoWithOffset = (utcSeconds, offsetSeconds) => {
    const resolvedOffsetSeconds = typeof offsetSeconds === "number" && Number.isFinite(offsetSeconds) ? offsetSeconds : (0, exports.getRuntimeLocalOffsetSeconds)(utcSeconds);
    const sign = resolvedOffsetSeconds >= 0 ? "+" : "-";
    const offsetMinutesAbs = Math.abs(Math.floor(resolvedOffsetSeconds / 60));
    const offsetHours = Math.floor(offsetMinutesAbs / 60);
    const offsetMinutes = offsetMinutesAbs % 60;
    const localMs = utcSeconds * 1000 + resolvedOffsetSeconds * 1000;
    const localDate = new Date(localMs);
    return (`${localDate.getUTCFullYear()}-${String(localDate.getUTCMonth() + 1).padStart(2, "0")}-${String(localDate.getUTCDate()).padStart(2, "0")}T` +
        `${String(localDate.getUTCHours()).padStart(2, "0")}:${String(localDate.getUTCMinutes()).padStart(2, "0")}:${String(localDate.getUTCSeconds()).padStart(2, "0")}` +
        `${sign}${String(offsetHours).padStart(2, "0")}:${String(offsetMinutes).padStart(2, "0")}`);
};
exports.formatUtcSecondsToIsoWithOffset = formatUtcSecondsToIsoWithOffset;
/**
 * Shift UTC seconds by local calendar months under a fixed UTC -> local offset.
 */
const shiftUtcSecondsByOffsetMonths = (utcSeconds, monthDelta, offsetSeconds = 0) => {
    const localMs = utcSeconds * 1000 + offsetSeconds * 1000;
    const localDate = new Date(localMs);
    const shiftedLocalMs = Date.UTC(localDate.getUTCFullYear(), localDate.getUTCMonth() + monthDelta, localDate.getUTCDate(), localDate.getUTCHours(), localDate.getUTCMinutes(), localDate.getUTCSeconds(), localDate.getUTCMilliseconds());
    return Math.floor((shiftedLocalMs - offsetSeconds * 1000) / 1000);
};
exports.shiftUtcSecondsByOffsetMonths = shiftUtcSecondsByOffsetMonths;
/**
 * Extract the ZCL command id and payload from a raw SWV-ZN/ZF frame.
 * Handles both standard and manufacturer-specific headers.
 */
const parseSWVZFRawZclCommand = (buffer) => {
    if (buffer.length < 3) {
        return;
    }
    const frameControl = buffer[0];
    const hasManufacturerCode = (frameControl & 0b100) !== 0;
    const zclHeaderLength = hasManufacturerCode ? 5 : 3;
    if (buffer.length < zclHeaderLength) {
        return;
    }
    return {
        commandId: buffer[zclHeaderLength - 1],
        payload: buffer.subarray(zclHeaderLength),
    };
};
exports.parseSWVZFRawZclCommand = parseSWVZFRawZclCommand;
//# sourceMappingURL=sonoff.js.map