/**
 * src/services/attachmentService.js
 *
 * Auto-post ServiceChannel work order attachments to Webex spaces.
 */

import { logger } from '../utils/logger.js';
import invoiceApprovalService from './invoiceApprovalService.js';
import {
  listWorkOrderAttachments,
  downloadAttachment,
  getContentTypeFromFilename,
} from '../integrations/serviceChannel/attachments.js';
import {
  assertCollabSupportConfigured,
  fetchCollabJson,
} from '../integrations/collabSupport/client.js';
import {
  isWebexNativeUpload,
  prepareWebexUpload,
} from '../utils/prepareWebexAttachment.js';

function isAutoPostEnabled() {
  const raw = process.env.AUTO_POST_ATTACHMENTS;
  if (raw == null || raw === '') return true;
  return !/^(0|false|no|off)$/i.test(String(raw).trim());
}

function isInvoiceAttachment(att) {
  if (!att) return false;
  if (att.IsInvoiceDigitalCopy === true) return true;
  const name = String(att.Name || att.fileName || '').toUpperCase();
  return /INVOICE_/i.test(name) || name.endsWith('.PDF') && name.includes('INVOICE');
}

function normalizeAttachment(att) {
  return {
    id: att.Id ?? att.id ?? att.attachmentId ?? null,
    name: att.Name || att.fileName || `attachment_${att.Id ?? att.id ?? 'unknown'}`,
    uri: att.Uri || att.downloadUri || null,
    isInvoice: isInvoiceAttachment(att),
  };
}

function hasNumericId(id) {
  return id != null && /^\d+$/.test(String(id));
}

function buildCaption(att, woNumber) {
  const prefix = att.isInvoice ? 'Invoice' : 'Attachment';
  const woLabel = woNumber ? `WO **${woNumber}**` : 'work order';
  return `📎 ${prefix} from ${woLabel}: **${att.name}**`;
}

function buildLinkMarkdown(att, woNumber) {
  const prefix = att.isInvoice ? 'Invoice' : 'Attachment';
  const woLabel = woNumber ? `WO **${woNumber}**` : 'work order';
  return `📎 ${prefix} from ${woLabel}: [${att.name}](${att.uri})`;
}

function dbGetPosted(db, workOrderId, attachmentId) {
  return new Promise((resolve, reject) => {
    db.get(
      'SELECT 1 FROM posted_attachments WHERE workOrderId = ? AND attachmentId = ?',
      [workOrderId, attachmentId],
      (err, row) => (err ? reject(err) : resolve(!!row))
    );
  });
}

function dbMarkPosted(db, workOrderId, attachmentId) {
  return new Promise((resolve, reject) => {
    db.run(
      'INSERT OR IGNORE INTO posted_attachments (workOrderId, attachmentId, postedAt) VALUES (?, ?, ?)',
      [workOrderId, attachmentId, new Date().toISOString()],
      (err) => (err ? reject(err) : resolve())
    );
  });
}

async function postSingleAttachment(webex, roomId, att, woNumber) {
  const fileName = att.name;
  const contentType = getContentTypeFromFilename(fileName);
  const caption = buildCaption(att, woNumber);

  // Webex cannot ingest HEIC URLs; skip straight to download + convert.
  if (att.uri && isWebexNativeUpload(fileName)) {
    try {
      await webex.sendWithAttachment(roomId, null, fileName, contentType, caption, att.uri);
      logger('attachments', `Posted ${fileName} via URL for room ${roomId}`);
      return { ok: true, linkOnly: false };
    } catch (urlErr) {
      logger('attachments', `URL post failed for ${fileName}: ${urlErr.message}`, 'warn');
    }
  }

  if (att.uri) {
    try {
      const { buffer, contentType: dlType } = await downloadAttachment(att.uri, fileName);
      const prepared = await prepareWebexUpload(buffer, fileName, dlType || contentType);
      await webex.sendWithAttachment(
        roomId,
        prepared.buffer,
        prepared.fileName,
        prepared.contentType,
        caption
      );
      logger('attachments', `Posted ${prepared.fileName} via buffer for room ${roomId}`);
      return { ok: true, linkOnly: false };
    } catch (dlErr) {
      logger('attachments', `Buffer post failed for ${fileName}: ${dlErr.message}`, 'warn');
      if (/heic-convert|Cannot find package/i.test(dlErr.message)) {
        logger(
          'attachments',
          'HEIC conversion requires heic-convert in node_modules — rebuild the container: docker compose down -v && docker compose up --build',
          'error'
        );
      }
    }
  }

  if (att.uri) {
    await webex.sendMarkdown(roomId, buildLinkMarkdown(att, woNumber));
    logger('attachments', `Posted link fallback for ${fileName} in room ${roomId}`);
    return { ok: true, linkOnly: true };
  }

  logger('attachments', `No URI for attachment ${att.id} (${fileName})`, 'warn');
  return { ok: false, linkOnly: false };
}

async function fetchCollabAttachments(woNumber) {
  try {
    assertCollabSupportConfigured();
  } catch {
    return [];
  }

  try {
    const result = await fetchCollabJson(
      '/woAttachments',
      { woId: woNumber },
      { logTag: 'attachments:collab', timeoutMs: 20_000 }
    );
    if (!result.ok || !result.data?.success) return [];
    return (result.data.attachments || []).map((a) => normalizeAttachment({
      id: a.attachmentId || a.id,
      Name: a.fileName,
      Uri: a.downloadUri,
      fileName: a.fileName,
      downloadUri: a.downloadUri,
    }));
  } catch (err) {
    logger('attachments:collab', `CollabSupport fallback failed: ${err.message}`, 'warn');
    return [];
  }
}

/**
 * Post work order attachments to a Webex room.
 *
 * @param {object} options
 * @param {object} options.db - sqlite3 db instance
 * @param {object} options.webex - client with sendWithAttachment + sendMarkdown
 * @param {string} options.roomId
 * @param {number|string} options.workOrderId - SC internal WO id
 * @param {number[]|string[]|null} [options.attachmentIds] - if set, only post these ids
 * @param {string|number} [options.woNumber] - display WO number for captions
 * @param {boolean} [options.skipDedup=false] - for manual /woAttachments resync
 */
export async function postWorkOrderAttachments({
  db,
  webex,
  roomId,
  workOrderId,
  attachmentIds = null,
  woNumber = null,
  skipDedup = false,
}) {
  if (!isAutoPostEnabled() && !skipDedup) {
    logger('attachments', 'AUTO_POST_ATTACHMENTS is disabled — skipping');
    return { posted: 0, skipped: 0, links: 0 };
  }
  if (!db || !webex || !roomId || !workOrderId) {
    logger('attachments', 'Missing required params for postWorkOrderAttachments', 'warn');
    return { posted: 0, skipped: 0, links: 0 };
  }

  let raw = await listWorkOrderAttachments(workOrderId);
  if (!raw.length && woNumber) {
    raw = await fetchCollabAttachments(woNumber);
  }

  const normalized = raw.map(normalizeAttachment).filter((a) => a.uri);

  const idFilter = attachmentIds?.length
    ? new Set(attachmentIds.map((id) => String(id)))
    : null;

  let posted = 0;
  let skipped = 0;
  let links = 0;

  for (const att of normalized) {
    if (idFilter && att.id != null && !idFilter.has(String(att.id))) continue;
    if (idFilter && att.id == null) continue;

    if (!skipDedup && hasNumericId(att.id)) {
      try {
        const already = await dbGetPosted(db, workOrderId, att.id);
        if (already) {
          skipped++;
          continue;
        }
      } catch (err) {
        logger('attachments', `Dedup check failed for ${att.id}: ${err.message}`, 'warn');
      }
    }

    try {
      const result = await postSingleAttachment(webex, roomId, att, woNumber || workOrderId);
      if (result.ok) {
        if (result.linkOnly) links++;
        if (!skipDedup && hasNumericId(att.id)) {
          await dbMarkPosted(db, workOrderId, att.id);
        }
        posted++;

        if (att.isInvoice) {
          invoiceApprovalService.postInvoiceApprovalCard({
            db,
            webex,
            roomId,
            workOrderId,
            woNumber: woNumber || workOrderId,
          }).catch((err) => {
            logger('attachments', `Invoice approval card failed for WO ${workOrderId}: ${err.message}`, 'warn');
          });
        }
      }
    } catch (err) {
      logger('attachments', `Failed to post ${att.name} (${att.id}): ${err.message}`, 'warn');
    }
  }

  if (posted > 0 || skipped > 0 || links > 0) {
    logger(
      'attachments',
      `WO ${workOrderId}: posted=${posted}, links=${links}, skipped=${skipped}` +
        (idFilter ? ` (filter=${[...idFilter].join(',')})` : ' (all)')
    );
  }

  return { posted, skipped, links };
}

export default {
  postWorkOrderAttachments,
  isAutoPostEnabled,
};
