/**
 * Approval Step — send approval request, pause pipeline until callback.
 * Config: { channel_id: 1, message: "...", options: ["Approve","Reject","Escalate"], timeout_min: 60 }
 */

import { registerStepExecutor } from '../pipeline-engine';
import { postgresClient } from '../postgres-client';
import { PipelineStep, PipelineContext, StepExecutorResult, NotificationChannel } from '../../types/pipeline';

async function executeApproval(
  step: PipelineStep,
  context: PipelineContext,
  executionId: number
): Promise<StepExecutorResult> {
  const channelId = Number(step.config.channel_id);
  const message = step.config.message || 'Approval required';
  const options = step.config.options || ['Approve', 'Reject'];
  const timeoutMin = Number(step.config.timeout_min) || 60;

  const expiresAt = new Date(Date.now() + timeoutMin * 60 * 1000);

  // Create approval request record
  const result = await postgresClient.query<{ id: number }>(
    `INSERT INTO approval_requests (execution_id, step_order, channel_id, message, options, status, expires_at)
     VALUES ($1, $2, $3, $4, $5, 'pending', $6)
     RETURNING id`,
    [executionId, step.step_order, channelId || null, message, JSON.stringify(options), expiresAt]
  );

  const approvalId = result.rows[0].id;
  const callbackUrl = `${process.env.WEBHOOK_BASE_URL || ''}/api/pipelines/approval/${approvalId}`;

  console.log(`[PIPELINE:approval] Created approval #${approvalId}, callback: ${callbackUrl}`);

  // Send notification with approval buttons if channel is configured
  if (channelId) {
    const chResult = await postgresClient.query<NotificationChannel>(
      `SELECT * FROM notification_channels WHERE id = $1 AND is_active = true`,
      [channelId]
    );

    if (chResult.rows.length > 0) {
      const channel = chResult.rows[0];
      await sendApprovalNotification(channel, message, options, approvalId, callbackUrl, context);
    }
  }

  // Return waiting — pipeline will pause here
  return {
    success: true,
    waiting: true,
    output: { approval_id: approvalId, callback_url: callbackUrl },
  };
}

async function sendApprovalNotification(
  channel: NotificationChannel,
  message: string,
  options: string[],
  approvalId: number,
  callbackUrl: string,
  context: PipelineContext
): Promise<void> {
  try {
    if (channel.channel_type === 'teams') {
      const actions = options.map(opt => ({
        type: 'Action.OpenUrl',
        title: opt,
        url: `${callbackUrl}?response=${encodeURIComponent(opt)}`,
      }));

      const card = {
        type: 'message',
        attachments: [{
          contentType: 'application/vnd.microsoft.card.adaptive',
          content: {
            type: 'AdaptiveCard',
            $schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
            version: '1.4',
            body: [
              { type: 'TextBlock', text: 'Approval Required', weight: 'bolder', size: 'medium' },
              { type: 'TextBlock', text: message, wrap: true },
              { type: 'TextBlock', text: `Approval #${approvalId}`, size: 'small', isSubtle: true },
            ],
            actions,
          },
        }],
      };

      await fetch(channel.config.webhook_url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(card),
      });
    } else if (channel.channel_type === 'telegram') {
      const keyboard = {
        inline_keyboard: [options.map(opt => ({
          text: opt,
          callback_data: JSON.stringify({ approval_id: approvalId, response: opt }),
        }))],
      };

      await fetch(`https://api.telegram.org/bot${channel.config.bot_token}/sendMessage`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          chat_id: channel.config.chat_id,
          text: `🔔 *Approval Required*\n\n${message}\n\n_Approval #${approvalId}_`,
          parse_mode: 'Markdown',
          reply_markup: keyboard,
        }),
      });
    } else if (channel.channel_type === 'ntfy') {
      // Personal channels forced to company server + token (UAT-FIX-01).
      // Global rows retain their config-driven behavior.
      const isPersonal = !!channel.owner_user_id;
      const serverUrl = isPersonal
        ? (process.env.NTFY_BASE_URL || 'https://ntfy.wulfconsulting.cloud')
        : (channel.config.server_url || 'https://ntfy.sh');
      const headers: Record<string, string> = {
        'Title': 'Approval Required',
        'Priority': 'high',
        'Tags': 'warning',
        'Actions': options.map(opt =>
          `http, ${opt}, ${callbackUrl}?response=${encodeURIComponent(opt)}, method=POST`
        ).join('; '),
      };
      if (isPersonal) {
        const token = process.env.NTFY_PULSE_TOKEN;
        if (token) headers['Authorization'] = `Bearer ${token}`;
        // If token missing, send unauthenticated — approval is best-effort and
        // the parent try/catch logs failures. Loud failure would block the
        // whole approval step for one missing env var.
      } else if (channel.config.auth_token) {
        headers['Authorization'] = `Bearer ${channel.config.auth_token}`;
      }
      await fetch(`${serverUrl}/${channel.config.topic}`, {
        method: 'POST',
        headers,
        body: message,
      });
    }
  } catch (err) {
    console.error(`[PIPELINE:approval] Failed to send notification:`, err);
  }
}

registerStepExecutor('approval', executeApproval);
