import { randomUUID } from "node:crypto"; import { getPool, withClient } from "../db/pool"; function nowIso() { return new Date().toISOString(); } function mapBinding(row) { return { id: row.id, device_id: row.device_id, merchant_id: row.merchant_id, outlet_id: row.outlet_id, terminal_id: row.terminal_id, active_flag: row.active_flag, bound_at: row.bound_at, unbound_at: row.unbound_at || undefined }; } export async function getActiveBindingByDevice(deviceId) { const { rows } = await getPool().query(`SELECT * FROM device_bindings WHERE device_id = $1 AND active_flag = TRUE ORDER BY bound_at DESC LIMIT 1`, [deviceId]); return rows[0] ? mapBinding(rows[0]) : null; } export async function getActiveBindingByTerminal(terminalId) { const { rows } = await getPool().query(`SELECT * FROM device_bindings WHERE terminal_id = $1 AND active_flag = TRUE ORDER BY bound_at DESC LIMIT 1`, [terminalId]); return rows[0] ? mapBinding(rows[0]) : null; } export async function getBindingsByDeviceId(deviceId) { const { rows } = await getPool().query(`SELECT * FROM device_bindings WHERE device_id = $1 ORDER BY bound_at DESC`, [deviceId]); return rows.map(mapBinding); } export async function bindDevice(payload) { const now = nowIso(); const result = await withClient(async (client) => { const existing = await client.query(`SELECT * FROM device_bindings WHERE device_id = $1 AND active_flag = TRUE ORDER BY bound_at DESC LIMIT 1`, [payload.device_id]); const same = existing.rows[0] ? mapBinding(existing.rows[0]) : null; if (same && same.merchant_id === payload.merchant_id && same.outlet_id === payload.outlet_id && same.terminal_id === payload.terminal_id) { return same; } await client.query("BEGIN"); try { if (existing.rows[0]) { await client.query(`UPDATE device_bindings SET active_flag = FALSE, unbound_at = $2 WHERE id = $1`, [same.id, now]); } const id = randomUUID(); const inserted = await client.query(`INSERT INTO device_bindings ( id, device_id, merchant_id, outlet_id, terminal_id, active_flag, bound_at ) VALUES ($1,$2,$3,$4,$5,TRUE,$6) RETURNING *`, [id, payload.device_id, payload.merchant_id, payload.outlet_id, payload.terminal_id, now]); await client.query("COMMIT"); return mapBinding(inserted.rows[0]); } catch (error) { await client.query("ROLLBACK"); throw error; } }); return result; } export async function unbindDevice(deviceId) { const now = nowIso(); const { rows } = await getPool().query(`UPDATE device_bindings SET active_flag = FALSE, unbound_at = $2 WHERE device_id = $1 AND active_flag = TRUE RETURNING *`, [deviceId, now]); return rows[0] ? mapBinding(rows[0]) : null; } export async function getBindingById(id) { const { rows } = await getPool().query("SELECT * FROM device_bindings WHERE id = $1", [id]); return rows[0] ? mapBinding(rows[0]) : null; } export function toBindingPayload(binding) { return { ...binding }; }