import { randomUUID } from "node:crypto";
import { getPool } from "../db/pool";
function nowIso() {
    return new Date().toISOString();
}
function mapHeartbeat(row) {
    return {
        id: row.id,
        device_id: row.device_id,
        timestamp: row.timestamp,
        received_at: row.received_at,
        firmware_version: row.firmware_version || undefined,
        network_strength: row.network_strength,
        battery_level: row.battery_level,
        state: row.state || undefined
    };
}
export async function createDeviceHeartbeat(payload) {
    const now = nowIso();
    const id = randomUUID();
    const { rows } = await getPool().query(`INSERT INTO device_heartbeats (
      id,
      device_id,
      timestamp,
      received_at,
      firmware_version,
      network_strength,
      battery_level,
      state,
      payload_json
    ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
    RETURNING *`, [
        id,
        payload.device_id,
        payload.timestamp,
        now,
        payload.firmware_version,
        payload.network_strength,
        payload.battery_level,
        payload.state,
        payload.payload_json || {}
    ]);
    return mapHeartbeat(rows[0]);
}
export async function getLatestHeartbeatByDeviceId(deviceId) {
    const { rows } = await getPool().query(`SELECT * FROM device_heartbeats
      WHERE device_id = $1
      ORDER BY received_at DESC
      LIMIT 1`, [deviceId]);
    return rows[0] ? mapHeartbeat(rows[0]) : null;
}
export async function getHeartbeatCountForDeviceLastHours(deviceId, hours = 24) {
    const { rows } = await getPool().query(`SELECT COUNT(*)::INT AS cnt
      FROM device_heartbeats
     WHERE device_id = $1
       AND (NOW() AT TIME ZONE 'utc' - timestamp) <= ($2 || ' hours')::interval`, [deviceId, hours]);
    return Number(rows[0]?.cnt || 0);
}
export async function listHeartbeats(filter) {
    const clauses = [];
    const params = [];
    let i = 1;
    if (filter?.device_id) {
        clauses.push(`device_id = $${i++}`);
        params.push(filter.device_id);
    }
    if (filter?.state) {
        clauses.push(`state = $${i++}`);
        params.push(filter.state);
    }
    if (filter?.from) {
        clauses.push(`timestamp >= $${i++}`);
        params.push(filter.from);
    }
    if (filter?.to) {
        clauses.push(`timestamp <= $${i++}`);
        params.push(filter.to);
    }
    const whereSql = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
    const limitSql = filter?.limit ? `LIMIT ${Number(filter.limit)}` : "";
    const { rows } = await getPool().query(`SELECT * FROM device_heartbeats ${whereSql} ORDER BY received_at DESC ${limitSql}`, params);
    return rows.map(mapHeartbeat);
}
export function deriveDeviceStatus(input) {
    const now = Date.now();
    const lastSeen = Date.parse(input?.last_seen_at || "");
    if (!Number.isFinite(lastSeen)) {
        return "offline";
    }
    const ageSeconds = (now - lastSeen) / 1000;
    if (ageSeconds > 900) {
        return "offline";
    }
    if (ageSeconds > 90) {
        return "stale";
    }
    if ((typeof input?.network_strength === "number" && input.network_strength < 40) ||
        (typeof input?.battery_level === "number" && input.battery_level < 20)) {
        return "degraded";
    }
    return "online";
}
export function deriveDeviceHealthSummary(input) {
    const now = Date.now();
    const lastSeen = Date.parse(input?.last_seen_at || "");
    const reasons = [];
    if (!Number.isFinite(lastSeen)) {
        return {
            status: "offline",
            score: 0,
            age_seconds: null,
            reasons: ["no_heartbeat"]
        };
    }
    const ageSeconds = Math.max(0, Math.floor((now - lastSeen) / 1000));
    const networkStrength = typeof input?.network_strength === "number" ? input.network_strength : null;
    const batteryLevel = typeof input?.battery_level === "number" ? input.battery_level : null;
    if (ageSeconds > 900) {
        reasons.push("offline_threshold_exceeded");
    }
    else if (ageSeconds > 90) {
        reasons.push("stale_threshold_exceeded");
    }
    if (typeof networkStrength === "number" && networkStrength < 40) {
        reasons.push("low_signal");
    }
    if (typeof batteryLevel === "number" && batteryLevel < 20) {
        reasons.push("low_battery");
    }
    let status = "online";
    if (reasons.includes("offline_threshold_exceeded")) {
        status = "offline";
    }
    else if (reasons.includes("stale_threshold_exceeded")) {
        status = "stale";
    }
    else if (reasons.includes("low_signal") || reasons.includes("low_battery")) {
        status = "degraded";
    }
    let score = 100;
    if (ageSeconds > 900) {
        score -= 80;
    }
    else if (ageSeconds > 90) {
        score -= 35;
    }
    else if (ageSeconds > 60) {
        score -= 10;
    }
    if (typeof networkStrength === "number") {
        score -= Math.max(0, 40 - networkStrength);
    }
    if (typeof batteryLevel === "number") {
        score -= Math.max(0, 20 - batteryLevel);
    }
    return {
        status,
        score: Math.max(0, Math.min(100, Math.round(score))),
        age_seconds: ageSeconds,
        reasons
    };
}
