// 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';

const REMOVE_OTHERS_AFTER_DAYS = 14;
const DELETE_AFTER_DAYS = 60;
const REMINDER_AFTER_DAYS = parseInt(process.env.SPACE_CLEANUP_REMINDER_DAYS || '60', 10);

/** COMPLETED + these extended values → auto-remove eligible. */
const AUTO_REMOVE_EXTENDED = new Set(['CONFIRMED', 'CANCELLED']);

function normalizePrimary(value) {
  return (value || '').trim().toUpperCase();
}

function normalizeExtended(value) {
  return (value || '').trim().toUpperCase();
}

function isNoChargeExtended(extendedRaw) {
  const extended = normalizeExtended(extendedRaw);
  return extended === 'NO CHARGE' || extended.includes('NO CHARGE');
}

/**
 * Classify a work order for space cleanup.
 * @returns {{ category: 'auto_remove'|'remind'|'skip', reason: string }}
 */
export function classifyWorkOrderCleanup(primaryRaw, extendedRaw) {
  const primary = normalizePrimary(primaryRaw);
  const extended = normalizeExtended(extendedRaw);

  if (primary !== 'COMPLETED') {
    return { category: 'skip', reason: 'not a COMPLETED work order' };
  }

  if (AUTO_REMOVE_EXTENDED.has(extended) || isNoChargeExtended(extendedRaw)) {
    return { category: 'auto_remove', reason: 'terminal COMPLETED extended status' };
  }

  const statusLabel = extended ? `COMPLETED / ${extended}` : 'COMPLETED';
  return {
    category: 'remind',
    reason: `${statusLabel} — awaiting Confirmed, Cancelled, or No Charge before removal`,
  };
}

function daysSince(dateStr) {
  return Math.floor((Date.now() - new Date(dateStr)) / (1000 * 3600 * 24));
}

function alreadyRemindedToday(lastReminderAt) {
  if (!lastReminderAt) return false;
  return new Date(lastReminderAt).toDateString() === new Date().toDateString();
}

function buildCloseoutReminderMessage({ woNumber, workOrderId, daysSinceUpdate, statusDisplay }) {
  const label = woNumber || workOrderId;
  const statusText = statusDisplay || 'COMPLETED';
  return (
    `**Close-out reminder:** Work order **${label}** is **${statusText}** in ServiceChannel ` +
    `and has been in that state for **${daysSinceUpdate} days**.\n\n` +
    `This space will not be removed until the work order reaches **Confirmed**, **Cancelled**, or **No Charge**. ` +
    `Please review in ServiceChannel and close out this Webex space when appropriate.`
  );
}

async function getLastReminder(db, workOrderId) {
  return new Promise((resolve, reject) => {
    db.get(
      'SELECT lastReminderAt FROM space_cleanup_reminders WHERE workOrderId = ?',
      [workOrderId],
      (err, row) => (err ? reject(err) : resolve(row?.lastReminderAt || null))
    );
  });
}

async function recordReminder(db, workOrderId) {
  const now = new Date().toISOString();
  return new Promise((resolve, reject) => {
    db.run(
      `INSERT INTO space_cleanup_reminders (workOrderId, lastReminderAt) VALUES (?, ?)
       ON CONFLICT(workOrderId) DO UPDATE SET lastReminderAt = excluded.lastReminderAt`,
      [workOrderId, now],
      (err) => (err ? reject(err) : resolve())
    );
  });
}

/**
 * Runs the space cleanup job.
 *
 * @param {boolean} dryRun
 * @param {object} [options]
 * @param {object} [options.db] - Optional sqlite3 database instance.
 * @param {boolean} [options.remindersOnly] - When true, only post close-out reminders (no prune/delete).
 */
export async function runSpaceCleanup(dryRun = true, options = {}) {
  const { db = defaultDb, remindersOnly = false } = options;

  const mode = dryRun ? '[DRY-RUN]' : '[LIVE]';
  const scope = remindersOnly ? 'reminders only' : 'full cleanup';

  console.log(
    `[SPACE-CLEANUP] ${mode} Starting job (${scope} | auto-remove ≥${REMOVE_OTHERS_AFTER_DAYS}d prune / ≥${DELETE_AFTER_DAYS}d delete | reminders ≥${REMINDER_AFTER_DAYS}d)...`
  );

  const results = [];
  let autoRemoveEligible = 0;
  let remindEligible = 0;
  let removedOthers = 0;
  let deleted = 0;
  let reminded = 0;
  let skipped = 0;
  let 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 = normalizePrimary(statusInfo.primaryStatus);
        const extended = normalizeExtended(statusInfo.extendedStatus);
        const statusDisplay = extended ? `${primary} / ${extended}` : primary;
        const classification = classifyWorkOrderCleanup(primary, extended);

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

        const ageDays = daysSince(statusInfo.updatedDate);

        if (classification.category === 'remind') {
          remindEligible++;

          if (ageDays < REMINDER_AFTER_DAYS) {
            results.push({
              woId: mapping.workOrderId,
              roomId: mapping.roomId,
              action: 'skipped',
              days: ageDays,
              status: statusDisplay,
              reason: `${statusDisplay} — ${ageDays} days (reminder at ${REMINDER_AFTER_DAYS})`,
            });
            skipped++;
            continue;
          }

          const lastReminderAt = await getLastReminder(db, mapping.workOrderId);
          if (alreadyRemindedToday(lastReminderAt)) {
            results.push({
              woId: mapping.workOrderId,
              roomId: mapping.roomId,
              action: 'skipped',
              days: ageDays,
              status: statusDisplay,
              reason: 'reminder already posted today',
            });
            skipped++;
            continue;
          }

          const reminderText = buildCloseoutReminderMessage({
            woNumber: statusInfo.woNumber,
            workOrderId: mapping.workOrderId,
            daysSinceUpdate: ageDays,
            statusDisplay,
          });

          if (dryRun) {
            results.push({
              woId: mapping.workOrderId,
              roomId: mapping.roomId,
              action: 'would_remind',
              days: ageDays,
              status: statusDisplay,
              reason: `${ageDays} days → post close-out reminder`,
            });
            reminded++;
            continue;
          }

          await botClient.sendMarkdown(mapping.roomId, reminderText);
          await recordReminder(db, mapping.workOrderId);
          logger(
            'SPACE-CLEANUP',
            `Posted close-out reminder in room ${mapping.roomId} for WO ${mapping.workOrderId} [${statusDisplay}]`
          );
          reminded++;
          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: 'remind',
            days: ageDays,
            status: statusDisplay,
            reason: `${ageDays} days → posted close-out reminder`,
          });
          continue;
        }

        // auto_remove
        if (remindersOnly) {
          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: 'skipped',
            days: ageDays,
            status: statusDisplay,
            reason: 'auto-remove skipped (reminders-only run)',
          });
          skipped++;
          continue;
        }

        autoRemoveEligible++;

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

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

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

        if (dryRun) {
          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: `would_${action}`,
            days: ageDays,
            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++;
          }

          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: ageDays,
            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,
      autoRemoveEligible,
      remindEligible,
      removedOthers,
      deleted,
      reminded,
      skipped,
      failed,
      dryRun,
      remindersOnly,
      reminderAfterDays: REMINDER_AFTER_DAYS,
    };
    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;

  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');
    _cachedBotPerson = { id: envPersonId, emails: [] };
  }
  return _cachedBotPerson;
}

async function removeAllOtherMembers(roomId) {
  try {
    const axiosInstance = botClient.axios;
    const bot = await getBotIdentity(axiosInstance);

    if (!bot.id && bot.emails.length === 0) {
      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();

      if (bot.id && m.personId === bot.id) continue;
      if (emailLc && bot.emails.includes(emailLc)) continue;
      if (emailLc.endsWith('@webex.bot')) continue;

      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');
  }
}
