/**
 * ServiceChannel work order attachment helpers (OData).
 */

import FormData from 'form-data';
import { fetchWithRetry, scAxios } from './client.js';
import { logger } from '../../utils/logger.js';

const ATTACHMENT_SELECT =
  'Id,Name,Uri,Description,TimeStamp,IsInvoiceDigitalCopy,NoteId';

export function getContentTypeFromFilename(filename) {
  if (!filename) return 'application/octet-stream';
  const lower = filename.toLowerCase();
  if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
  if (lower.endsWith('.png')) return 'image/png';
  if (lower.endsWith('.gif')) return 'image/gif';
  if (lower.endsWith('.bmp')) return 'image/bmp';
  if (lower.endsWith('.webp')) return 'image/webp';
  if (lower.endsWith('.heic')) return 'image/heic';
  if (lower.endsWith('.heif')) return 'image/heif';
  if (lower.endsWith('.pdf')) return 'application/pdf';
  if (lower.endsWith('.txt')) return 'text/plain';
  if (lower.endsWith('.doc')) return 'application/msword';
  if (lower.endsWith('.docx')) {
    return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
  }
  if (lower.endsWith('.xls')) return 'application/vnd.ms-excel';
  if (lower.endsWith('.xlsx')) {
    return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
  }
  if (lower.endsWith('.ppt')) return 'application/vnd.ms-powerpoint';
  if (lower.endsWith('.pptx')) {
    return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
  }
  return 'application/octet-stream';
}

/**
 * List attachments for a work order.
 * @returns {Promise<Array<object>>}
 */
export async function listWorkOrderAttachments(workOrderId) {
  if (!workOrderId) return [];

  const start = Date.now();
  try {
    const res = await fetchWithRetry(
      `/odata/workorders(${workOrderId})/attachments`,
      {
        params: {
          $select: ATTACHMENT_SELECT,
          $orderby: 'TimeStamp desc',
        },
        timeout: 30000,
      }
    );

    const attachments = res.data?.value || [];
    logger(
      'sc:listAttachments',
      `Found ${attachments.length} for WO ${workOrderId} (${Date.now() - start}ms)`
    );
    return attachments;
  } catch (err) {
    logger('sc:listAttachments', `Failed for WO ${workOrderId}: ${err.message}`, 'warn');
    return [];
  }
}

/**
 * Fetch a single attachment by id.
 * @returns {Promise<object|null>}
 */
export async function getWorkOrderAttachment(workOrderId, attachmentId) {
  if (!workOrderId || attachmentId == null) return null;

  const start = Date.now();
  try {
    const res = await fetchWithRetry(
      `/odata/workorders(${workOrderId})/attachments`,
      {
        params: { $filter: `Id eq ${attachmentId}` },
        timeout: 30000,
      }
    );

    const att = res.data?.value?.[0] || null;
    if (att) {
      logger(
        'sc:getAttachment',
        `Fetched attachment ${attachmentId} for WO ${workOrderId} (${Date.now() - start}ms)`
      );
    }
    return att;
  } catch (err) {
    logger(
      'sc:getAttachment',
      `Failed ${attachmentId} for WO ${workOrderId}: ${err.message}`,
      'warn'
    );
    return null;
  }
}

/**
 * Download attachment bytes from a storage URI.
 * @returns {Promise<{ buffer: Buffer, contentType: string, fileName: string }>}
 */
export async function downloadAttachment(uri, fileName = 'attachment') {
  if (!uri) throw new Error('Missing attachment URI');

  const start = Date.now();
  const safeName = fileName || 'attachment';

  const fileRes = await fetch(uri);
  if (!fileRes.ok) {
    throw new Error(`Download failed: HTTP ${fileRes.status}`);
  }

  const headerType = fileRes.headers.get('content-type')?.split(';')[0]?.trim();
  const contentType = (headerType && headerType !== 'application/octet-stream')
    ? headerType
    : getContentTypeFromFilename(safeName);

  const buffer = Buffer.from(await fileRes.arrayBuffer());
  logger(
    'sc:downloadAttachment',
    `Downloaded ${safeName} (${buffer.length} bytes, ${Date.now() - start}ms)`
  );

  return { buffer, contentType, fileName: safeName };
}

function summarizeUploadError(err) {
  const data = err?.response?.data;
  if (data == null) return err.message || 'Upload failed';
  if (typeof data === 'string') return data;
  if (data.Message) return String(data.Message);
  try {
    return JSON.stringify(data);
  } catch {
    return err.message || 'Upload failed';
  }
}

/**
 * Upload a file to an existing work order.
 * POST /workorders/{workOrderId}/attachments (multipart/form-data).
 *
 * @returns {Promise<{ id: number|null, name: string, path: string|null }>}
 */
export async function uploadWorkOrderAttachment(workOrderId, buffer, fileName, contentType) {
  if (!workOrderId || !buffer?.length || !fileName) {
    throw new Error('workOrderId, buffer, and fileName are required');
  }

  const resolvedType = contentType || getContentTypeFromFilename(fileName);
  const form = new FormData();
  form.append('file', buffer, { filename: fileName, contentType: resolvedType });

  const start = Date.now();
  try {
    const res = await scAxios.post(`/workorders/${workOrderId}/attachments`, form, {
      headers: form.getHeaders(),
      maxBodyLength: Infinity,
      maxContentLength: Infinity,
      timeout: 120000,
    });

    const att = res.data?.Attachments?.[0] || {};
    logger(
      'sc:uploadAttachment',
      `Uploaded ${fileName} to WO ${workOrderId} (id=${att.Id ?? 'unknown'}, ${Date.now() - start}ms)`
    );

    return {
      id: att.Id ?? att.id ?? null,
      name: att.Name || fileName,
      path: att.Path ?? null,
    };
  } catch (err) {
    const msg = summarizeUploadError(err);
    logger('sc:uploadAttachment', `Failed ${fileName} for WO ${workOrderId}: ${msg}`, 'error');
    throw new Error(msg);
  }
}
