/**
 * botClient.js
 * Consolidated Webex client for standard bot operations.
 *
 * This is the primary client for normal ServChan bot activities:
 * - Creating rooms
 * - Posting messages
 * - Adding members
 * - Basic room helpers (getWOByRoom, getStoreByRoom)
 *
 * For privileged operations (DECT, telephony, etc.), use adminClient.js instead.
 */

import axios from 'axios';
import { logger } from '../../utils/logger.js';
import FormData from 'form-data';

class ServChanBotClient {
  static #instance = null;

  constructor() {
    if (ServChanBotClient.#instance) {
      return ServChanBotClient.#instance;
    }

    const token = process.env.WEBEX_BOT_TOKEN;
    const baseURL = process.env.WEBEX_BASE_URL || 'https://webexapis.com/v1';

    if (!token) {
      logger('webex:botClient', 'WEBEX_BOT_TOKEN missing – bot will not work', 'error');
      throw new Error('Missing WEBEX_BOT_TOKEN');
    }

    this.axios = axios.create({
      baseURL,
      timeout: 15000,
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
    });

    this.axios.interceptors.response.use(
      (res) => res,
      (err) => {
        const msg = err.response
          ? `${err.response.status} - ${JSON.stringify(err.response.data)}`
          : err.message;
        logger('webex:botClient', `API error: ${msg}`, 'error');
        return Promise.reject(err);
      }
    );

    ServChanBotClient.#instance = this;
    logger('webex:botClient', 'Initialized (bot token)');
  }

  // ──────────────────────────────────────────────
  // Core messaging & room operations
  // ──────────────────────────────────────────────

  async createRoom(title, teamId = null) {
    const payload = { title, type: 'group' };
    if (teamId) payload.teamId = teamId;

    const res = await this.axios.post('/rooms', payload);
    logger('webex:bot:createRoom', `Created ${res.data.id}`);
    return res.data;
  }

  async addMember(roomId, personEmail) {
    const res = await this.axios.post('/memberships', { roomId, personEmail });
    logger('webex:bot:addMember', `Added ${personEmail}`);
    return res.data;
  }

  async sendMarkdown(roomId, markdown, textFallback = null) {
    if (!roomId || !markdown) {
      logger('webex:bot:sendMarkdown', 'Missing roomId or markdown', 'warn');
      return null;
    }

    const payload = { roomId, markdown };
    if (textFallback) payload.text = textFallback;

    const res = await this.axios.post('/messages', payload);
    return res.data;
  }

  async sendWithAttachment(roomId, buffer, fileName, contentType = 'application/octet-stream', text = 'Attached file', fileUrl = null) {
    if (!roomId || !fileName) {
      logger('webex:bot:attachment', 'Missing required params', 'warn');
      return null;
    }

    if (fileUrl) {
      const payload = { roomId, markdown: text, files: [fileUrl] };
      const res = await this.axios.post('/messages', payload);
      return res.data;
    }

    if (buffer) {
      const form = new FormData();
      form.append('roomId', roomId);
      form.append('markdown', text);
      form.append('files', buffer, { filename: fileName, contentType });

      const res = await this.axios.post('/messages', form, {
        headers: form.getHeaders(),
      });
      return res.data;
    }

    throw new Error('Either buffer or fileUrl must be provided');
  }

  async deleteRoom(roomId) {
    if (!roomId) throw new Error('roomId is required');
    await this.axios.delete(`/rooms/${roomId}`);
    logger('webex:bot:deleteRoom', `Deleted ${roomId}`);
  }

  // ──────────────────────────────────────────────
  // Helpers used by commands
  // ──────────────────────────────────────────────

  async getRoom(roomId) {
    const res = await this.axios.get(`/rooms/${roomId}`);
    return res.data;
  }

  async getWOByRoom(roomId) {
    try {
      const room = await this.getRoom(roomId);
      return this.#extractWoNumber(room?.title);
    } catch (err) {
      logger('webex:bot:getWOByRoom', `Failed for ${roomId}: ${err.message}`, 'warn');
      return null;
    }
  }

  async getStoreByRoom(roomId) {
    try {
      const room = await this.getRoom(roomId);
      return this.#extractStoreNumber(room?.title);
    } catch (err) {
      logger('webex:bot:getStoreByRoom', `Failed for ${roomId}: ${err.message}`, 'warn');
      return null;
    }
  }

  #extractWoNumber(str) {
    if (!str) return null;
    const m = str.match(/WO[-–—]?\s*(\d+)/i);
    return m ? m[1] : null;
  }

  #extractStoreNumber(str) {
    if (!str || typeof str !== 'string') return null;

    const storeRegex = /Store\s*[#:=-]?\s*(\d{3,5})/i;
    let m = str.match(storeRegex);
    if (m) return m[1];

    const fallback = /WO[-–—]?\s*\d+\s*[\|]\s*(\d{3,5})/i;
    m = str.match(fallback);
    if (m) return m[1];

    const numbers = [...str.matchAll(/\b(\d{4,5})\b/g)].map(x => x[1]);
    for (const n of numbers) {
      if (n.length >= 6) continue;
      return n;
    }
    return null;
  }

  // ──────────────────────────────────────────────
  // Adaptive Card + person helpers (for approval flow)
  // ──────────────────────────────────────────────

  /**
   * Send an Adaptive Card (v1.3) message.
   * card = plain JS object matching Adaptive Card schema.
   * The Webex /messages endpoint accepts attachments with contentType for cards.
   */
  async sendAdaptiveCard(roomId, card, fallbackText = "This message contains an interactive card. Please view in a supported Webex client.") {
    if (!roomId || !card) {
      logger("webex:bot:sendAdaptiveCard", "Missing roomId or card", "warn");
      return null;
    }
    const payload = {
      roomId,
      text: fallbackText,
      attachments: [
        {
          contentType: "application/vnd.microsoft.card.adaptive",
          content: card,
        },
      ],
    };
    const res = await this.axios.post("/messages", payload);
    return res.data;
  }

  /** Delete a message posted by this bot (e.g. remove an approval card after submit). */
  async deleteMessage(messageId) {
    if (!messageId) {
      logger('webex:bot:deleteMessage', 'Missing messageId', 'warn');
      return null;
    }
    await this.axios.delete(`/messages/${encodeURIComponent(messageId)}`);
    logger('webex:bot:deleteMessage', `Deleted message ${messageId}`);
    return true;
  }

  /**
   * Fetch person details (for approver attribution from attachmentAction.personId).
   */
  async getPersonDetails(personId) {
    if (!personId) return { displayName: "Unknown User", emails: [] };
    try {
      const res = await this.axios.get("/people/" + encodeURIComponent(personId));
      return res.data;
    } catch (err) {
      logger("webex:bot:getPersonDetails", "Failed for " + personId + ": " + err.message, "warn");
      return { displayName: personId, emails: [] };
    }
  }
}

export default new ServChanBotClient();
