// src/services/spaceCleanupService.js
import { getWorkOrderStatus } from '../integrations/serviceChannel/client.js';
import botClient from '../integrations/webex/botClient.js';
import { logger } from '../utils/logger.js';
import defaultDb from '../db/mappings.js';

/**
 * Runs the space cleanup job.
 *
 * @param {boolean} dryRun
 * @param {object} [options]
 * @param {object} [options.db] - Optional sqlite3 database instance.
 *   When provided, this allows the caller (e.g. the main app) to ensure we are
 *   always using the exact same database connection that the production bot is using.
 */
export async function runSpaceCleanup(dryRun = true, options = {}) {
  const { db = defaultDb } = options;

  const mode = dryRun ? '[DRY-RUN]' : '[LIVE]';
  
  const REMOVE_OTHERS_AFTER_DAYS = 14;   // "archive-like" step
  const DELETE_AFTER_DAYS = 60;

  console.log(`[SPACE-CLEANUP] ${mode} Starting job (Remove others ≥${REMOVE_OTHERS_AFTER_DAYS}d | Delete ≥${DELETE_AFTER_DAYS}d)...`);

  const results = [];
  let qualifying = 0, removedOthers = 0, deleted = 0, skipped = 0, failed = 0;

  try {
    const mappings = await new Promise((resolve, reject) => {
      db.all('SELECT workOrderId, roomId FROM mappings', [], (err, rows) => {
        if (err) reject(err);
        else resolve(rows || []);
      });
    });

    for (const mapping of mappings) {
      try {
        const statusInfo = await getWorkOrderStatus(mapping.workOrderId);
        if (!statusInfo) {
          results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action: 'skipped', status: 'UNKNOWN', reason: 'status fetch failed' });
          failed++;
          continue;
        }

        const primary = statusInfo.primaryStatus?.toUpperCase() || 'UNKNOWN';
        const extended = statusInfo.extendedStatus?.toUpperCase() || '';
        const statusDisplay = extended ? `${primary} (${extended})` : primary;

        if (primary !== 'INVOICED' && primary !== 'COMPLETED') {
          results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action: 'skipped', status: statusDisplay, reason: 'not qualifying' });
          skipped++;
          continue;
        }

        qualifying++;

        const daysSinceUpdate = Math.floor((Date.now() - new Date(statusInfo.updatedDate)) / (1000 * 3600 * 24));

        let action = 'skipped';
        let reason = '';

        if (daysSinceUpdate >= DELETE_AFTER_DAYS) {
          action = 'delete';
          reason = `${daysSinceUpdate} days → delete room`;
        } else if (daysSinceUpdate >= REMOVE_OTHERS_AFTER_DAYS) {
          action = 'remove_others';
          reason = `${daysSinceUpdate} days → remove other members`;
        } else {
          reason = `only ${daysSinceUpdate} days old`;
        }

        if (action === 'skipped') {
          skipped++;
          results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action: 'skipped', days: daysSinceUpdate, status: statusDisplay, reason });
          continue;
        }

        if (dryRun) {
          results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action: `would_${action}`, days: daysSinceUpdate, status: statusDisplay, reason });
        } else {
          if (action === 'remove_others') {
            await removeAllOtherMembers(mapping.roomId);
            logger('SPACE-CLEANUP', `Removed all other members from room ${mapping.roomId} for WO ${mapping.workOrderId} [${statusDisplay}]`);
            removedOthers++;
          } else if (action === 'delete') {
            await botClient.deleteRoom(mapping.roomId);
            deleted++;
          }

          // Clean up mapping
          await new Promise((resolve, reject) => {
            db.run('DELETE FROM mappings WHERE workOrderId = ?', [mapping.workOrderId], (err) => {
              err ? reject(err) : resolve();
            });
          });

          results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action, days: daysSinceUpdate, status: statusDisplay, reason });
        }
      } catch (err) {
        results.push({ woId: mapping.workOrderId, roomId: mapping.roomId, action: 'failed', status: 'ERROR', reason: err.message });
        failed++;
        logger('SPACE-CLEANUP', `Failed for WO ${mapping.workOrderId}: ${err.message}`);
      }
    }

    const summary = { totalChecked: mappings.length, qualifying, removedOthers, deleted, skipped, failed, dryRun };
    console.log(`[SPACE-CLEANUP] ${mode} Job completed:`, summary);
    logger('SPACE-CLEANUP', `${mode} Job completed: ${JSON.stringify(summary)}`);

    return { summary, results, mode };

  } catch (err) {
    console.error(`[SPACE-CLEANUP] Critical error:`, err.message);
    return { error: err.message };
  }
}

// Cache the bot's own identity so we never accidentally kick ourselves out of
// a room during "remove other members". We resolve it once from the Webex API,
// which is authoritative regardless of what env vars are set.
let _cachedBotPerson = null;
async function getBotIdentity(axiosInstance) {
  if (_cachedBotPerson) return _cachedBotPerson;

  // Prefer env-provided ID if set; still fetch email so the email-based skip
  // works too even when the person ID is stale.
  const envPersonId = process.env.WEBEX_BOT_PERSON_ID || null;

  try {
    const { data } = await axiosInstance.get('/people/me');
    _cachedBotPerson = {
      id: data?.id || envPersonId,
      emails: (data?.emails || []).map(e => e.toLowerCase()),
    };
  } catch (err) {
    logger('SPACE-CLEANUP', `Failed to resolve bot identity via /people/me: ${err.message}`, 'warn');
    // Fall back to env-only. Emails list stays empty — the caller will still
    // skip anything matching the well-known bot email suffix below.
    _cachedBotPerson = { id: envPersonId, emails: [] };
  }
  return _cachedBotPerson;
}

// Helper: Remove everyone except the bot itself (best-effort)
async function removeAllOtherMembers(roomId) {
  try {
    const axiosInstance = botClient.axios;
    const bot = await getBotIdentity(axiosInstance);

    if (!bot.id && bot.emails.length === 0) {
      // We couldn't figure out who "we" are. Removing everyone in this state
      // would evict the bot from its own room, orphaning it. Refuse.
      logger('SPACE-CLEANUP', `Refusing to remove members from ${roomId}: bot identity unresolved (set WEBEX_BOT_PERSON_ID or verify /people/me works)`, 'error');
      return;
    }

    const { data } = await axiosInstance.get('/memberships', { params: { roomId, max: 200 } });
    const memberships = data.items || [];

    for (const m of memberships) {
      const emailLc = (m.personEmail || '').toLowerCase();

      // Skip the bot's own membership by any signal we can get.
      if (bot.id && m.personId === bot.id) continue;
      if (emailLc && bot.emails.includes(emailLc)) continue;
      if (emailLc.endsWith('@webex.bot')) continue; // catches bot mail regardless of local part

      try {
        await axiosInstance.delete(`/memberships/${m.id}`);
        logger('SPACE-CLEANUP', `Removed ${m.personEmail || m.personId} from ${roomId}`);
      } catch (e) {
        logger('SPACE-CLEANUP', `Failed to remove member ${m.id}: ${e.message}`, 'warn');
      }
    }
  } catch (err) {
    logger('SPACE-CLEANUP', `removeAllOtherMembers failed for ${roomId}: ${err.message}`, 'error');
  }
}