/**
 * Room Status Manager
 * 
 * Manages an independent PostgreSQL connection pool for
 * the room_status.room_status_moment snapshot table.
 * Provides batch upsert with JSONB merge.
 */
import pg from 'pg';
import { randomUUID } from 'crypto';

const { Pool } = pg;

export class RoomStatusManager {
    /**
     * @param {Object} dbConfig - roomStatusDb config from config.js
     * @param {Object} [options] - additional configuration like omitGuid
     */
    constructor(dbConfig, options = {}) {
        this.pool = new Pool({
            host: dbConfig.host,
            port: dbConfig.port,
            user: dbConfig.user,
            password: dbConfig.password,
            database: dbConfig.database,
            max: dbConfig.max,
            ssl: dbConfig.ssl
        });
        this.schema = dbConfig.schema;
        this.table = dbConfig.table;
        this.fullTableName = `${this.schema}.${this.table}`;
        this.omitGuid = options.omitGuid || false;
    }

    /**
     * Batch upsert status rows into room_status_moment.
     * Uses ON CONFLICT for atomic merge.
     * 
     * @param {Array<Object>} rows - Array of merged status objects
    *   Each: { hotel_id, room_id, device_id, ts_ms, ip, sys_lock_status, dev_loops, faulty_device_count }
     */
    async upsertBatch(rows) {
        if (!rows || rows.length === 0) return;
        await this._doUpsert(rows);
    }

    /**
     * Execute the actual upsert SQL for a batch of rows.
     */
    async _doUpsert(rows) {
        const values = [];
        const placeholders = [];

        for (let i = 0; i < rows.length; i++) {
            const row = rows[i];
            const paramsPerRow = this.omitGuid ? 9 : 10;
            const offset = i * paramsPerRow;

            if (this.omitGuid) {
                values.push(
                    row.ts_ms,                         // $1  
                    row.hotel_id,                      // $2
                    row.room_id,                       // $3
                    row.device_id,                     // $4
                    row.ip ?? null,                    // $5
                    row.sys_lock_status,               // $6
                    row.dev_loops ? JSON.stringify(row.dev_loops) : null,              // $7
                    row.faulty_device_count ? JSON.stringify(row.faulty_device_count) : null, // $8
                    1                                  // $9 online_status
                );
                const p = (n) => `$${offset + n}`;
                placeholders.push(`(${p(1)}, ${p(2)}, ${p(3)}, ${p(4)}, ${p(5)}, ${p(6)}, ${p(7)}::jsonb, ${p(8)}::jsonb, ${p(9)})`);
            } else {
                values.push(
                    row.guid || randomUUID(),          // $1
                    row.ts_ms,                         // $2  
                    row.hotel_id,                      // $3
                    row.room_id,                       // $4
                    row.device_id,                     // $5
                    row.ip ?? null,                    // $6
                    row.sys_lock_status,               // $7
                    row.dev_loops ? JSON.stringify(row.dev_loops) : null,              // $8
                    row.faulty_device_count ? JSON.stringify(row.faulty_device_count) : null,  // $9
                    1                                  // $10 online_status
                );
                const p = (n) => `$${offset + n}`;
                placeholders.push(`(${p(1)}::uuid, ${p(2)}, ${p(3)}, ${p(4)}, ${p(5)}, ${p(6)}, ${p(7)}, ${p(8)}::jsonb, ${p(9)}::jsonb, ${p(10)})`);
            }
        }

        const insertColumns = this.omitGuid
            ? 'ts_ms, hotel_id, room_id, device_id, ip, sys_lock_status, dev_loops, faulty_device_count, online_status'
            : 'guid, ts_ms, hotel_id, room_id, device_id, ip, sys_lock_status, dev_loops, faulty_device_count, online_status';

        const conflictTarget = this.omitGuid
            ? '(hotel_id, room_id)'
            : '(hotel_id, room_id, device_id)';

        const sql = `
      INSERT INTO ${this.fullTableName} (${insertColumns}) VALUES ${placeholders.join(', ')}
      ON CONFLICT ${conflictTarget}
      DO UPDATE SET
        ts_ms = EXCLUDED.ts_ms,
                ip = COALESCE(EXCLUDED.ip, ${this.fullTableName}.ip),
        online_status = 1,
        device_id = EXCLUDED.device_id,
        sys_lock_status = COALESCE(EXCLUDED.sys_lock_status, ${this.fullTableName}.sys_lock_status),
        dev_loops = CASE
          WHEN EXCLUDED.dev_loops IS NULL THEN ${this.fullTableName}.dev_loops
          ELSE COALESCE(${this.fullTableName}.dev_loops, '{}'::jsonb) || EXCLUDED.dev_loops
        END,
        faulty_device_count = COALESCE(EXCLUDED.faulty_device_count, ${this.fullTableName}.faulty_device_count)
    `;

        await this.pool.query(sql, values);
    }
    async testConnection() {
        try {
            await this.pool.query('SELECT 1');
            return true;
        } catch {
            return false;
        }
    }

    async close() {
        await this.pool.end();
    }
}
