// 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';
import { getCompletedOperationsRoomId, parseServChanRoomTitle } from '../utils/servchanRoomTitle.js';
import { getWoCloseout } from './woCloseoutService.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);
const CLEANUP_PROVIDER_NAME = (process.env.SPACE_CLEANUP_PROVIDER_NAME || 'Pro-Motion Technology Group, LLC').trim();
const CLEANUP_PROVIDER_ID = parseInt(process.env.SPACE_CLEANUP_PROVIDER_ID || '2000002215', 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');
}

export function matchesCleanupProvider(providerName, providerId = null) {
  const hasNameFilter = Boolean(CLEANUP_PROVIDER_NAME);
  const hasIdFilter = Number.isFinite(CLEANUP_PROVIDER_ID) && CLEANUP_PROVIDER_ID > 0;
  if (!hasNameFilter && !hasIdFilter) return true;

  if (hasIdFilter && providerId != null && Number(providerId) === CLEANUP_PROVIDER_ID) {
    return true;
  }

  if (hasNameFilter) {
    return (providerName || '').trim().toLowerCase() === CLEANUP_PROVIDER_NAME.toLowerCase();
  }

  return false;
}

/**
 * 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 alreadyPostedToday(lastPostedAt) {
  if (!lastPostedAt) return false;
  return new Date(lastPostedAt).toDateString() === new Date().toDateString();
}

function formatDigestDate() {
  return new Date().toLocaleDateString('en-US', {
    weekday: 'short',
    month: 'short',
    day: 'numeric',
    year: 'numeric',
  });
}

function buildConsolidatedDigestMessage(digestRows) {
  const dateLabel = formatDigestDate();
  const count = digestRows.length;
  const noun = count === 1 ? 'work order is' : 'work orders are';

  let md = `**Daily close-out report** — ${dateLabel}\n\n`;
  md += `${count} ${noun} COMPLETED (non-terminal) for ${REMINDER_AFTER_DAYS}+ days:\n\n`;

  for (const row of digestRows) {
    const header = row.headerLine || `WO-${row.woNumber || row.workOrderId}`;
    let line = `- **${header}** — ${row.statusDisplay}`;
    if (row.isInvoiced) {
      line += row.invoiceNumber ? ` — invoiced #${row.invoiceNumber}` : ' — invoiced';
    }
    line += ` — ${row.ageDays} days\n`;
    md += line;
  }

  md +=
    '\nTerminal statuses (Confirmed, Cancelled, No Charge) trigger auto-remove. ' +
    'After an invoice is posted, use `/confirmed` in the WO space when resolution is verified.';

  return md;
}

async function getLastDigestPostedAt(db) {
  return new Promise((resolve, reject) => {
    db.get(
      'SELECT lastPostedAt FROM space_cleanup_digest WHERE id = 1',
      [],
      (err, row) => (err ? reject(err) : resolve(row?.lastPostedAt || null))
    );
  });
}

async function recordDigestPosted(db) {
  const now = new Date().toISOString();
  return new Promise((resolve, reject) => {
    db.run(
      `INSERT INTO space_cleanup_digest (id, lastPostedAt) VALUES (1, ?)
       ON CONFLICT(id) DO UPDATE SET lastPostedAt = excluded.lastPostedAt`,
      [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 consolidated digest (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 ? 'digest only' : 'full cleanup';

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

  const results = [];
  const digestRows = [];
  let autoRemoveEligible = 0;
  let remindEligible = 0;
  let removedOthers = 0;
  let deleted = 0;
  let digestPosted = 0;
  let skipped = 0;
  let providerFiltered = 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;
        }

        if (!matchesCleanupProvider(statusInfo.providerName, statusInfo.providerId)) {
          const providerLabel = statusInfo.providerName
            ? `${statusInfo.providerName}${statusInfo.providerId ? ` (${statusInfo.providerId})` : ''}`
            : (statusInfo.providerId ? `ID ${statusInfo.providerId}` : 'UNKNOWN PROVIDER');
          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: 'skipped',
            status: providerLabel,
            reason: `provider excluded (cleanup limited to ${CLEANUP_PROVIDER_NAME}${Number.isFinite(CLEANUP_PROVIDER_ID) ? ` / ID ${CLEANUP_PROVIDER_ID}` : ''})`,
          });
          providerFiltered++;
          skipped++;
          continue;
        }

        const primary = normalizePrimary(statusInfo.primaryStatus);
        const extended = normalizeExtended(statusInfo.extendedStatus);
        const statusDisplay = extended ? `${primary} / ${extended}` : primary;
        let classification = classifyWorkOrderCleanup(primary, extended);

        const closeout = await getWoCloseout(db, mapping.workOrderId);
        if (closeout && primary === 'COMPLETED') {
          classification = {
            category: 'auto_remove',
            reason: closeout.scStatusUpdated
              ? 'ServChan close-out recorded (SC CONFIRMED)'
              : 'ServChan close-out recorded (SC note only)',
          };
        }

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

        const ageDays = closeout?.confirmedAt
          ? daysSince(closeout.confirmedAt)
          : 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 (digest at ${REMINDER_AFTER_DAYS})`,
            });
            skipped++;
            continue;
          }

          let headerLine = null;
          try {
            const room = await botClient.getRoom(mapping.roomId);
            const parsed = parseServChanRoomTitle(room?.title);
            headerLine = parsed?.headerLine || room?.title || null;
          } catch (err) {
            logger(
              'SPACE-CLEANUP',
              `Could not fetch room title for ${mapping.roomId}: ${err.message}`,
              'warn'
            );
          }

          digestRows.push({
            workOrderId: mapping.workOrderId,
            woNumber: statusInfo.woNumber,
            statusDisplay,
            ageDays,
            roomId: mapping.roomId,
            headerLine,
            isInvoiced: statusInfo.isInvoiced === true,
            invoiceNumber: statusInfo.invoiceNumber ?? null,
          });

          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: dryRun ? 'would_post_digest' : 'pending_digest',
            days: ageDays,
            status: statusDisplay,
            reason: `${ageDays} days → include in consolidated digest`,
          });
          continue;
        }

        // auto_remove
        if (remindersOnly) {
          results.push({
            woId: mapping.workOrderId,
            roomId: mapping.roomId,
            action: 'skipped',
            days: ageDays,
            status: statusDisplay,
            reason: 'auto-remove skipped (digest-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}`);
      }
    }

    let digestWouldPost = false;
    let digestSkippedReason = null;

    if (digestRows.length > 0) {
      const opsRoomId = getCompletedOperationsRoomId();

      if (!opsRoomId) {
        digestSkippedReason = 'COMPLETED_OPERATIONS_ROOM_ID not configured';
        logger('SPACE-CLEANUP', digestSkippedReason, 'warn');
        for (const r of results) {
          if (r.action === 'would_post_digest' || r.action === 'pending_digest') {
            r.action = 'skipped';
            r.reason = digestSkippedReason;
            skipped++;
          }
        }
      } else {
        const lastDigestAt = await getLastDigestPostedAt(db);
        if (alreadyPostedToday(lastDigestAt)) {
          digestSkippedReason = 'consolidated digest already posted today';
          for (const r of results) {
            if (r.action === 'would_post_digest' || r.action === 'pending_digest') {
              r.action = 'skipped';
              r.reason = digestSkippedReason;
              skipped++;
            }
          }
        } else {
          digestWouldPost = true;

          if (!dryRun) {
            const digestMarkdown = buildConsolidatedDigestMessage(digestRows);
            await botClient.sendMarkdown(opsRoomId, digestMarkdown);
            await recordDigestPosted(db);
            digestPosted = 1;
            logger(
              'SPACE-CLEANUP',
              `Posted consolidated digest (${digestRows.length} WOs) to ops room ${opsRoomId}`
            );

            for (const r of results) {
              if (r.action === 'pending_digest') {
                r.action = 'post_digest';
                r.reason = `${r.days} days → included in consolidated digest`;
              }
            }
          }
        }
      }
    }

    const summary = {
      totalChecked: mappings.length,
      autoRemoveEligible,
      remindEligible,
      digestEligible: digestRows.length,
      digestPosted,
      digestWouldPost: dryRun && digestWouldPost && digestRows.length > 0,
      digestSkippedReason,
      providerFiltered,
      cleanupProvider: CLEANUP_PROVIDER_NAME || null,
      cleanupProviderId: Number.isFinite(CLEANUP_PROVIDER_ID) ? CLEANUP_PROVIDER_ID : null,
      removedOthers,
      deleted,
      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 };
  }
}

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