// ============================================================================
// UPDATED "Machine cycles" Function
// Location: flows.json, line 1150, node ID: 0d023d87a13bf56f
// Outputs: 3 (added third output for database state backup)
// ============================================================================

const current = Number(msg.payload) || 0;

// ============================================================================
// SECTION 1: TIME TRACKING (NEW - Must run BEFORE any early returns)
// ============================================================================
const now = Date.now();
const trackingEnabled = !!global.get("trackingEnabled");
const lastUpdate = global.get("lastUpdateTime") || now;
const deltaMs = now - lastUpdate;
const deltaSeconds = deltaMs / 1000;

// Sanity check: Protect against clock skew (negative delta or >5 min gap)
if (deltaSeconds < 0 || deltaSeconds > 300) {
    node.warn(`[TIME] Abnormal delta: ${deltaSeconds.toFixed(2)}s - clock skew or restart detected, resetting timer`);
    global.set("lastUpdateTime", now);
    // Don't accumulate time, but continue processing
} else {
    // Normal delta, accumulate time if tracking is enabled
    if (trackingEnabled) {
        // Initialize timing vars if they don't exist (handles restart scenario)
        if (!global.get("productionStartTime")) {
            node.warn("[TIME] Production start time missing, initializing now");
            global.set("productionStartTime", now);
            global.set("operatingTime", 0);
            global.set("downtime", 0);
        }

        // Accumulate operating time when machine is running (state 1)
        if (current === 1) {
            const opTime = global.get("operatingTime") || 0;
            global.set("operatingTime", opTime + deltaSeconds);
        }
        // Accumulate downtime when machine is stopped (state 0) while tracking
        else if (current === 0) {
            const downTime = global.get("downtime") || 0;
            global.set("downtime", downTime + deltaSeconds);
        }
    }
    global.set("lastUpdateTime", now);
}

// ============================================================================
// SECTION 2: EXISTING CYCLE COUNTING LOGIC (UNCHANGED)
// ============================================================================
let zeroStreak = flow.get("zeroStreak") || 0;
zeroStreak = current === 0 ? zeroStreak + 1 : 0;
flow.set("zeroStreak", zeroStreak);

const prev = flow.get("lastMachineState") ?? 0;
flow.set("lastMachineState", current);

global.set("machineOnline", true);   // force ONLINE for now

let productionRunning = !!global.get("productionStarted");
let stateChanged = false;

if (current === 1 && !productionRunning) {
    productionRunning = true;
    stateChanged = true;
} else if (current === 0 && zeroStreak >= 2 && productionRunning) {
    productionRunning = false;
    stateChanged = true;
}

global.set("productionStarted", productionRunning);

const stateMsg = stateChanged
    ? {
        _mode: "production-state",
        machineOnline: true,
        productionStarted: productionRunning
    }
    : null;

const activeOrder = global.get("activeWorkOrder");
const cavities = Number(global.get("moldActive") || 0);
if (!activeOrder || !activeOrder.id || cavities <= 0) {
    // We still want to pass along any state change even if there's no active WO.
    return [null, stateMsg, null];
}

// Check if tracking is enabled (START button clicked)
if (!trackingEnabled) {
    // Cycles are happening but we're not tracking them yet
    return [null, stateMsg, null];
}

// only count rising edges (0 -> 1) for production totals
if (prev === 1 || current !== 1) {
    return [null, stateMsg, null];
}

let cycles = Number(global.get("cycleCount") || 0) + 1;
global.set("cycleCount", cycles);

// Calculate good parts: total produced minus accumulated scrap
const scrapTotal = Number(activeOrder.scrap) || 0;
const totalProduced = cycles * cavities;
const produced = totalProduced - scrapTotal;
const target = Number(activeOrder.target) || 0;
const progress = target > 0 ? Math.min(100, Math.round((produced / target) * 100)) : 0;

activeOrder.good = produced;
activeOrder.progressPercent = progress;
activeOrder.lastUpdateIso = new Date().toISOString();
global.set("activeWorkOrder", activeOrder);

const promptIssued = global.get("scrapPromptIssuedFor") || null;

if (!promptIssued && target > 0 && produced >= target) {
    node.warn(`[DEBUG] TRIGGERING PROMPT!`);
    global.set("scrapPromptIssuedFor", activeOrder.id);
    msg._mode = "scrap-prompt";
    msg.scrapPrompt = {
        id: activeOrder.id,
        sku: activeOrder.sku || "",
        target,
        produced
    };
    return [null, msg, null]; // bypass the DB update on this cycle
}

// ============================================================================
// SECTION 3: DATABASE UPDATE MESSAGE (EXISTING)
// ============================================================================
const dbMsg = {
    _mode: "cycle",
    cycle: {
        id: activeOrder.id,
        sku: activeOrder.sku || "",
        target,
        good: produced,
        scrap: Number(activeOrder.scrap) || 0,
        cycleTime: Number(activeOrder.cycleTime || activeOrder.theoreticalCycleTime || 0),
        progressPercent: progress,
        lastUpdateIso: activeOrder.lastUpdateIso,
        machineOnline: true,
        productionStarted: productionRunning
    },
    topic: `
      UPDATE work_orders
      SET
        good_parts = ${produced},
        progress_percent = ${progress},
        updated_at = NOW()
      WHERE work_order_id = '${activeOrder.id}';
    `
};

// ============================================================================
// SECTION 4: STATE BACKUP TO DATABASE (NEW - Every 10th cycle to reduce load)
// ============================================================================
let cyclesSinceBackup = flow.get("cyclesSinceBackup") || 0;
cyclesSinceBackup++;
flow.set("cyclesSinceBackup", cyclesSinceBackup);

let stateBackupMsg = null;
if (cyclesSinceBackup >= 10) {
    // Reset counter
    flow.set("cyclesSinceBackup", 0);

    // Backup current state to database
    const productionStartTime = global.get("productionStartTime") || null;
    const operatingTime = global.get("operatingTime") || 0;
    const downtime = global.get("downtime") || 0;
    const lastUpdateTime = global.get("lastUpdateTime") || null;
    const scrapPromptIssuedFor = global.get("scrapPromptIssuedFor") || null;

    stateBackupMsg = {
        _mode: "state-backup",
        topic: `
          UPDATE session_state
          SET
            work_order_id = '${activeOrder.id}',
            cycle_count = ${cycles},
            production_start_time = ${productionStartTime},
            operating_time = ${operatingTime.toFixed(2)},
            downtime = ${downtime.toFixed(2)},
            last_update_time = ${lastUpdateTime},
            tracking_enabled = ${trackingEnabled ? 1 : 0},
            machine_state = ${current},
            scrap_prompt_issued_for = ${scrapPromptIssuedFor ? `'${scrapPromptIssuedFor}'` : 'NULL'}
          WHERE session_key = 'current_session';
        `
    };
}

// ============================================================================
// OUTPUTS:
// Output 1: Database update for work_orders table (existing)
// Output 2: State message to UI (existing)
// Output 3: State backup to session_state table (NEW)
// ============================================================================
return [dbMsg, stateMsg, stateBackupMsg];
