/**
 * Реестр инструментов ИИ-чата: описание для модели (OpenAI tools) и выполнение через self-request с JWT.
 *
 * КАК ДОБАВИТЬ НОВЫЙ ИНСТРУМЕНТ:
 * 1. В TOOLS_FOR_OPENAI — добавить объект { type: 'function', function: { name, description, parameters } }.
 *    parameters — JSON Schema (properties, required, additionalProperties: false). Описание (description)
 *    помогает ИИ задавать наводящие вопросы и правильно заполнять аргументы.
 * 2. В TOOL_EXECUTORS — добавить запись: method ('GET'|'POST'|'PUT'), pathTemplate (с :param для path),
 *    опционально queryParams: { argKey: queryKey }, для POST/PUT — bodyMapping: { argKey: destKey } или _createdBy: true.
 * 3. runTool подставит path, query string и body, для запросов от имени пользователя вызовет наш API с JWT.
 *
 * Модули для расширения: финансы (payment-invoices, calendar), заявки (applications), офис (meetings, documents),
 * HR (employees, vacations), юр. отдел (contracts, court-cases), развитие (pipeline, OSS).
 */

const axios = require('axios');

/** Текст «что умеет» — возвращается по команде get_capabilities */
const CAPABILITIES_TEXT = `Я помощник в системе управления МКД. Вот что я умею:

**Дома и объекты**
• Показать список всех домов (МКД) — адреса, id, основные данные.
• Показать данные по одному дому по его id (паспорт, лицевые счета и т.д.).

**Участки**
• Показать список участков (районов обслуживания) — название, руководитель.

**Сотрудники**
• Показать список сотрудников (id и имя) — для поиска исполнителя или контакта.

**Финансы — счета на оплату**
• Показать список счетов на оплату (с пагинацией: страница и количество на странице).
• Создать счёт на оплату: контрагент, сумма, назначение (мероприятие/дом/участок/прочее), формат оплаты (наличные/безнал), список услуг (название и сумма). Могу задать уточняющие вопросы, если чего-то не хватает.

**Заявки (диспетчерская)**
• Создать заявку: обязательно адрес и описание; по желанию — заявитель, квартира, срок выполнения.

Просто напиши, что нужно: например «покажи дома», «создай счёт на 10 000 для ООО Рога», «создай заявку по адресу ул. Ленина 5 — течёт кран». Если данных не хватает, я спрошу.`;

/** Описания инструментов в формате OpenAI Chat Completions (tools) */
const TOOLS_FOR_OPENAI = [
  {
    type: 'function',
    function: {
      name: 'get_capabilities',
      description: 'Вернуть подробное описание всех возможностей помощника в программе МКД. Вызывай, когда пользователь спрашивает «что ты умеешь», «расскажи о возможностях», «какие команды», «помощь», «что можешь» и т.п. Аргументов нет.',
      parameters: { type: 'object', properties: {}, additionalProperties: false }
    }
  },
  {
    type: 'function',
    function: {
      name: 'list_buildings',
      description: 'Получить список всех домов (МКД). Возвращает массив домов с id, адресом и основными данными.',
      parameters: { type: 'object', properties: {}, additionalProperties: false }
    }
  },
  {
    type: 'function',
    function: {
      name: 'get_building',
      description: 'Получить данные одного дома по его id (например id из списка домов).',
      parameters: {
        type: 'object',
        properties: {
          id: { type: 'string', description: 'Идентификатор дома (id)' }
        },
        required: ['id'],
        additionalProperties: false
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'list_districts',
      description: 'Получить список участков (районов обслуживания) с id, названием и руководителем.',
      parameters: { type: 'object', properties: {}, additionalProperties: false }
    }
  },
  {
    type: 'function',
    function: {
      name: 'list_employees',
      description: 'Получить список сотрудников (id, имя). Используй для выбора исполнителя или поиска по имени.',
      parameters: { type: 'object', properties: {}, additionalProperties: false }
    }
  },
  {
    type: 'function',
    function: {
      name: 'list_payment_invoices',
      description: 'Получить список счетов на оплату. Можно указать limit и page для пагинации.',
      parameters: {
        type: 'object',
        properties: {
          limit: { type: 'number', description: 'Количество записей на странице (по умолчанию 10)' },
          page: { type: 'number', description: 'Номер страницы (по умолчанию 1)' }
        },
        additionalProperties: false
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'create_payment_invoice',
      description: 'Создать счёт на оплату. Нужны: контрагент (contractorName), сумма (totalAmount), назначение (purposeType: event, building, district, other), формат оплаты (paymentFormat: cash, non_cash), список услуг (serviceItems: массив объектов { name, amount }). Опционально: purposeDescription, contractorInn, notes.',
      parameters: {
        type: 'object',
        properties: {
          contractorName: { type: 'string', description: 'Название контрагента (ИП/ООО)' },
          totalAmount: { type: 'number', description: 'Общая сумма в рублях' },
          purposeType: { type: 'string', enum: ['event', 'building', 'district', 'other'], description: 'Тип назначения платежа' },
          paymentFormat: { type: 'string', enum: ['cash', 'non_cash'], description: 'Формат оплаты' },
          serviceItems: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                name: { type: 'string' },
                amount: { type: 'number' }
              },
              required: ['name', 'amount']
            },
            description: 'Список услуг: каждый элемент { name, amount }'
          },
          purposeDescription: { type: 'string', description: 'Текстовое описание назначения' },
          contractorInn: { type: 'string', description: 'ИНН контрагента' },
          notes: { type: 'string', description: 'Заметки' }
        },
        required: ['contractorName', 'totalAmount', 'purposeType', 'paymentFormat', 'serviceItems'],
        additionalProperties: false
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'create_application',
      description: 'Создать заявку (диспетчерская). Обязательно: адрес (address), описание (description). Опционально: contactName, apartment, deadlineAt (дата в ISO).',
      parameters: {
        type: 'object',
        properties: {
          address: { type: 'string', description: 'Адрес объекта' },
          description: { type: 'string', description: 'Описание заявки' },
          contactName: { type: 'string', description: 'ФИО заявителя' },
          apartment: { type: 'string', description: 'Квартира/помещение' },
          deadlineAt: { type: 'string', description: 'Срок выполнения (ISO дата)' }
        },
        required: ['address', 'description'],
        additionalProperties: false
      }
    }
  }
];

/**
 * Исполнение инструмента: method, path (шаблон с :param), опционально queryParams, bodyMapping.
 * queryParams: объект { argKey: queryKey } — добавляет query string из args.
 * bodyMapping: для POST/PUT — подстановка body; _createdBy: true — подставить user.userId в createdBy.
 */
const TOOL_EXECUTORS = {
  list_buildings: {
    method: 'GET',
    pathTemplate: '/buildings',
    queryParams: null,
    bodyMapping: null
  },
  get_building: {
    method: 'GET',
    pathTemplate: '/buildings/:id',
    queryParams: null,
    bodyMapping: null
  },
  list_districts: {
    method: 'GET',
    pathTemplate: '/districts',
    queryParams: null,
    bodyMapping: null
  },
  list_employees: {
    method: 'GET',
    pathTemplate: '/employees/list',
    queryParams: null,
    bodyMapping: null
  },
  list_payment_invoices: {
    method: 'GET',
    pathTemplate: '/finance/payment-invoices',
    queryParams: { limit: 'limit', page: 'page' },
    bodyMapping: null
  },
  create_payment_invoice: {
    method: 'POST',
    pathTemplate: '/finance/payment-invoices',
    queryParams: null,
    bodyMapping: {
      _createdBy: true,
      contractorName: 'contractorName',
      totalAmount: 'totalAmount',
      purposeType: 'purposeType',
      paymentFormat: 'paymentFormat',
      serviceItems: 'serviceItems',
      purposeDescription: 'purposeDescription',
      contractorInn: 'contractorInn',
      notes: 'notes'
    }
  },
  create_application: {
    method: 'POST',
    pathTemplate: '/applications',
    queryParams: null,
    bodyMapping: {
      address: 'address',
      description: 'description',
      contactName: 'contactName',
      apartment: 'apartment',
      deadlineAt: 'deadlineAt'
    }
  }
};

/**
 * Подставить параметры в path (например :id -> args.id)
 */
function buildPath(pathTemplate, args) {
  let p = pathTemplate;
  const re = /:(\w+)/g;
  let m;
  while ((m = re.exec(pathTemplate)) !== null) {
    const key = m[1];
    const val = args && args[key];
    if (val === undefined || val === null) {
      throw new Error(`Параметр "${key}" обязателен для пути ${pathTemplate}`);
    }
    p = p.replace(new RegExp(':' + key + '(?=/|$)'), encodeURIComponent(String(val)));
  }
  return p;
}

/**
 * Выполнить инструмент от имени пользователя: внутренний HTTP-запрос к нашему API с JWT.
 * @param {string} toolName
 * @param {object} args — аргументы от ИИ
 * @param {object} user — { userId, employeeId, role } (req.user)
 * @param {object} context — { baseUrl, apiPrefix, getTokenForUser }
 * @returns {Promise<{ success: boolean, data?: any, error?: string }>}
 */
async function runTool(toolName, args, user, context) {
  if (toolName === 'get_capabilities') {
    return { success: true, data: { text: CAPABILITIES_TEXT } };
  }
  const { baseUrl, apiPrefix, getTokenForUser } = context;
  const executor = TOOL_EXECUTORS[toolName];
  if (!executor) {
    return { success: false, error: `Неизвестный инструмент: ${toolName}` };
  }
  const path = buildPath(executor.pathTemplate, args || {});
  let url = `${baseUrl}${apiPrefix}${path}`;
  if (executor.queryParams && args) {
    const q = [];
    for (const [argKey, queryKey] of Object.entries(executor.queryParams)) {
      if (args[argKey] !== undefined && args[argKey] !== null) {
        q.push(`${encodeURIComponent(queryKey)}=${encodeURIComponent(String(args[argKey]))}`);
      }
    }
    if (q.length) url += '?' + q.join('&');
  }
  const token = getTokenForUser(user);
  const headers = {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json'
  };
  let body = undefined;
  if (executor.bodyMapping && (executor.method === 'POST' || executor.method === 'PUT')) {
    body = {};
    for (const [argKey, destKey] of Object.entries(executor.bodyMapping)) {
      if (argKey === '_createdBy' && destKey === true) {
        body.createdBy = user.userId;
        continue;
      }
      if (args && argKey in args) {
        body[destKey] = args[argKey];
      }
    }
  }
  try {
    const res = await axios({
      method: executor.method,
      url,
      headers,
      data: body,
      timeout: 30000,
      validateStatus: () => true
    });
    if (res.status >= 200 && res.status < 300) {
      return { success: true, data: res.data };
    }
    const errText = res.data && (res.data.error || res.data.message) ? String(res.data.error || res.data.message) : res.statusText;
    return { success: false, error: `HTTP ${res.status}: ${errText}` };
  } catch (err) {
    const msg = err.response ? `HTTP ${err.response.status}` : err.message || 'Ошибка запроса';
    return { success: false, error: msg };
  }
}

module.exports = {
  getToolsSchema: () => TOOLS_FOR_OPENAI,
  runTool,
  TOOL_EXECUTORS
};
