#!/usr/bin/env node
'use strict';

const fs = require('fs');

const CALLBACK_PATH = '/agent-invoke-callback';
const LEGACY_CALLBACK_PATH = '/ai-audit-callback';

function emit(stage, message, status = 'running', extra = {}) {
  process.stderr.write(JSON.stringify({
    type: 'process_event',
    stage,
    message,
    status,
    timestamp: new Date().toISOString(),
    ...extra,
  }) + '\n');
}

function readInput() {
  const raw = String(process.argv[2] || process.env.SKILL_INPUT || process.env.AIFLOW_SKILL_INPUT || fs.readFileSync(0, 'utf8')).trim();
  return raw ? JSON.parse(raw) : {};
}

function boolValue(value, fallback) {
  if (value === undefined || value === null || value === '') return fallback;
  if (typeof value === 'boolean') return value;
  if (typeof value === 'number') return value !== 0;
  return ['1', 'true', 'yes', 'y', 'on'].includes(String(value).toLowerCase());
}

function normalizeStatus(status, fallback = 'failed') {
  const value = String(status || '').trim().replace(/[-\s]/g, '_').toLowerCase();
  const map = {
    approved: 'approved',
    approve: 'approved',
    pass: 'approved',
    passed: 'approved',
    success: 'approved',
    ok: 'approved',
    rejected: 'rejected',
    reject: 'rejected',
    deny: 'rejected',
    denied: 'rejected',
    refuse: 'rejected',
    refused: 'rejected',
    manual_review: 'manual_review',
    manualreview: 'manual_review',
    manual: 'manual_review',
    human_review: 'manual_review',
    human: 'manual_review',
    need_manual_review: 'manual_review',
    failed: 'failed',
    fail: 'failed',
    failure: 'failed',
    error: 'failed',
    exception: 'failed',
    timeout: 'failed',
  };
  return map[value] || fallback;
}

function firstNonEmpty(...values) {
  for (const value of values) {
    if (value !== undefined && value !== null && value !== '') return value;
  }
  return undefined;
}

function queryValueFromUrl(url, key) {
  try {
    return new URL(url).searchParams.get(key) || '';
  } catch {
    const match = String(url || '').match(new RegExp(`[?&]${key}=([^&#]+)`));
    return match ? decodeURIComponent(match[1]) : '';
  }
}

function normalizeCallbackUrl(callbackUrl, orderNo) {
  if (!callbackUrl) throw new Error('callbackUrl is required');

  let url;
  try {
    url = new URL(String(callbackUrl));
  } catch {
    throw new Error('callbackUrl must be an absolute URL');
  }

  if (url.pathname.includes(LEGACY_CALLBACK_PATH)) {
    url.pathname = url.pathname.replace(LEGACY_CALLBACK_PATH, CALLBACK_PATH);
  }

  if (!url.pathname.includes(CALLBACK_PATH)) {
    const normalizedPath = url.pathname.replace(/\/+$/, '');
    url.pathname = `${normalizedPath}${CALLBACK_PATH}`;
  }

  if (orderNo && !url.searchParams.get('orderNo')) {
    url.searchParams.set('orderNo', String(orderNo));
  }
  url.searchParams.delete('token');

  return url.toString();
}

function buildBody(input) {
  const auditResult = input.auditResult && typeof input.auditResult === 'object' ? input.auditResult : {};
  const orderNo = firstNonEmpty(input.orderNo, auditResult.orderNo, auditResult.bizNo, auditResult.repairNum, queryValueFromUrl(input.callbackUrl, 'orderNo'));
  if (!orderNo) throw new Error('orderNo is required');

  const status = normalizeStatus(firstNonEmpty(
    input.status,
    input.auditStatus,
    input.conclusion,
    auditResult.status,
    auditResult.auditStatus,
    auditResult.conclusion,
  ));

  const reason = firstNonEmpty(
    input.reason,
    input.errorMessage,
    input.error,
    auditResult.reason,
    auditResult.message,
    auditResult.errorMessage,
    status === 'failed' ? 'AI审核失败，未返回明确失败原因' : '',
  );

  const body = {
    orderNo: String(orderNo),
    status,
    reason: String(reason || ''),
    reportMarkdown: String(firstNonEmpty(input.reportMarkdown, input.report, auditResult.reportMarkdown, auditResult.report, '') || ''),
  };

  if (input.auditResult !== undefined) body.auditResult = input.auditResult;
  if (input.extra && typeof input.extra === 'object' && !Array.isArray(input.extra)) Object.assign(body, input.extra);

  body.orderNo = String(orderNo);
  body.status = normalizeStatus(body.status);
  body.reason = String(body.reason || (body.status === 'failed' ? 'AI审核失败，未返回明确失败原因' : ''));
  body.reportMarkdown = String(body.reportMarkdown || '');

  return body;
}

async function postJson(url, bodyText, headers, timeoutMs, retries) {
  let lastErr;
  for (let attempt = 0; attempt <= retries; attempt += 1) {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), timeoutMs);
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers,
        body: bodyText,
        signal: controller.signal,
      });
      const text = await response.text();
      let parsed = text;
      try {
        parsed = text ? JSON.parse(text) : {};
      } catch {
        parsed = text;
      }
      if (!response.ok) {
        const err = new Error(`HTTP ${response.status}: ${typeof parsed === 'string' ? parsed.slice(0, 300) : JSON.stringify(parsed).slice(0, 300)}`);
        err.statusCode = response.status;
        err.response = parsed;
        throw err;
      }
      return { statusCode: response.status, response: parsed };
    } catch (err) {
      lastErr = err;
      if (attempt < retries) await new Promise(resolve => setTimeout(resolve, 300 * (attempt + 1)));
    } finally {
      clearTimeout(timer);
    }
  }
  throw lastErr;
}

async function run(input, env) {
  const body = buildBody(input);
  const callbackUrl = normalizeCallbackUrl(input.callbackUrl, body.orderNo);
  const timeoutMs = Number(input.timeoutMs || env.TYCM_CALLBACK_TIMEOUT_MS || 15000);
  const retries = Number(input.retries ?? env.TYCM_CALLBACK_RETRIES ?? 1);
  const bodyText = JSON.stringify(body);
  const headers = {
    'Content-Type': 'application/json',
  };

  const request = {
    url: callbackUrl,
    authMode: 'none',
    headers,
    body,
  };

  if (boolValue(input.dryRun, false)) {
    emit('dry-run', 'TYCM agent-invoke-callback dryRun完成', 'completed');
    return { success: true, dryRun: true, request };
  }

  emit('request', 'POST回推TYCM审核结果到agent-invoke-callback');
  const response = await postJson(callbackUrl, bodyText, headers, timeoutMs, retries);
  emit('complete', 'TYCM agent-invoke-callback完成', 'completed', { statusCode: response.statusCode });
  return {
    success: true,
    dryRun: false,
    ...response,
    request,
  };
}

(async () => {
  try {
    const input = readInput();
    process.stdout.write(JSON.stringify(await run(input, process.env)));
  } catch (err) {
    process.stdout.write(JSON.stringify({
      success: false,
      error: err && err.message ? err.message : String(err),
      statusCode: err && err.statusCode ? err.statusCode : undefined,
      response: err && err.response ? err.response : undefined,
    }));
  }
})();
