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

const fs = require('fs');

const DEFAULT_CONFIG = {
  allowCompanyVehicle: false,
  maxPurchaseAgeDays: 365,
  plusCMaxVehicleAgeYears: 5,
  plusCKeywords: ['plusc', '代步权益'],
  manualReviewOnMissingCriticalFields: true,
  allowedVehicleTypeKeywords: ['乘用车', '小型轿车', '轿车', '客车', 'SUV', '新能源', '越野', '多用途乘用车'],
  rejectVehicleTypeKeywords: ['货车', '牵引', '挂车', '专项', '工程', '营运', '出租', '公交', '客运', '危险品'],
};

const COMPANY_SUFFIX_RE = /(公司|有限|集团|厂|店|中心|企业|合作社|机关|单位|学校|医院|政府|委员会|事务所|营业部|分公司|4S店)/;

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 = fs.readFileSync(0, 'utf8').trim();
  return raw ? JSON.parse(raw) : {};
}

function firstValue(obj, keys) {
  if (!obj || typeof obj !== 'object') return '';
  for (const key of keys) {
    const value = obj[key];
    if (value !== undefined && value !== null && String(value).trim() !== '' && String(value).trim() !== 'null') {
      return value;
    }
  }
  return '';
}

function str(value) {
  if (value === undefined || value === null) return '';
  return String(value).trim();
}

function normName(value) {
  return str(value).replace(/\s+/g, '').replace(/[·.．]/g, '');
}

function normId(value) {
  return str(value).replace(/\s+/g, '').toUpperCase();
}

function normVin(value) {
  return str(value).replace(/\s+/g, '').toUpperCase();
}

function normLoose(value) {
  return str(value).replace(/\s+/g, '').toUpperCase();
}

function fieldsOf(result) {
  if (!result || typeof result !== 'object') return {};
  if (result.fields && typeof result.fields === 'object') return result.fields;
  return result;
}

function parseDate(value) {
  if (value === undefined || value === null || value === '') return null;
  if (typeof value === 'number') {
    const timestamp = value > 1000000000 && value < 100000000000 ? value * 1000 : value;
    const date = new Date(timestamp);
    return Number.isNaN(date.getTime()) ? null : date;
  }
  const text = str(value);
  if (!text || text === '长期') return null;
  if (/^\d+$/.test(text)) {
    const n = Number(text);
    const timestamp = n > 1000000000 && n < 100000000000 ? n * 1000 : n;
    const date = new Date(timestamp);
    if (!Number.isNaN(date.getTime()) && n > 1000000000) return date;
  }
  const normalized = text
    .replace(/[年月/.]/g, '-')
    .replace(/日/g, '')
    .replace(/T.*$/, '')
    .trim();
  const compact = normalized.match(/^(\d{4})(\d{2})(\d{2})$/);
  const finalText = compact ? `${compact[1]}-${compact[2]}-${compact[3]}` : normalized;
  const dateOnly = finalText.match(/^(\d{4})-(\d{1,2})-(\d{1,2})/);
  const date = dateOnly
    ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))
    : new Date(finalText);
  return Number.isNaN(date.getTime()) ? null : date;
}

function formatDate(date) {
  if (!date) return '';
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
}

function daysBetween(start, end) {
  return Math.floor((end.getTime() - start.getTime()) / 86400000);
}

function fullMonthsBetween(start, end) {
  let months = (end.getFullYear() - start.getFullYear()) * 12 + (end.getMonth() - start.getMonth());
  if (end.getDate() < start.getDate()) months -= 1;
  return Math.max(0, months);
}

function addYearsClamped(date, years) {
  const targetYear = date.getFullYear() + years;
  const targetMonth = date.getMonth();
  const day = Math.min(date.getDate(), new Date(targetYear, targetMonth + 1, 0).getDate());
  return new Date(targetYear, targetMonth, day, 0, 0, 0, 0);
}

function isDateAfterLimitYears(start, end, years) {
  return end.getTime() > addYearsClamped(start, years).getTime();
}

function normalizeKeywordText(value) {
  return str(value).normalize('NFKC').replace(/\s+/g, '').toLowerCase();
}

function isPackageTypePlusC(value) {
  return normalizeKeywordText(value) === '3';
}

function isPlusCOrder(order, cfg) {
  if (!order || typeof order !== 'object') return false;
  if (isPackageTypePlusC(order.packageType)) return true;
  const text = normalizeKeywordText([
    order.productName,
    order.packageName,
    order.packageTypeName,
    order.goodsName,
    order.product,
    order.productTitle,
    order.productNo,
  ].filter(Boolean).join(' '));
  const keywords = Array.isArray(cfg.plusCKeywords) && cfg.plusCKeywords.length
    ? cfg.plusCKeywords
    : DEFAULT_CONFIG.plusCKeywords;
  return keywords.some(keyword => text.includes(normalizeKeywordText(keyword)));
}

function resolveVehicleAgeStartDate(n) {
  const order = n.order || {};
  const candidates = [
    ['firstRegistrationDate', firstValue(order, ['firstRegistrationDate', 'registrationDate', 'registerDate', 'vehicleRegisterDate', 'licenseRegisterDate'])],
    ['orderPurchaseDate', firstValue(order, ['carPurchaseTime', 'purchaseTime', 'purchaseDate'])],
    ['invoiceIssueDate', firstValue(n.invoiceFields, ['issueDate', '开票日期', 'date'])],
  ];
  for (const [source, value] of candidates) {
    const date = parseDate(value);
    if (date) return { source, date };
  }
  return { source: '', date: null };
}

function containsAny(text, keywords) {
  const value = str(text).toUpperCase();
  return keywords.some(k => value.includes(String(k).toUpperCase()));
}

function isMissingCritical(status, reason) {
  return {
    id: '',
    name: '',
    status,
    reason,
    evidence: {},
  };
}

function rule(id, name, status, reason, evidence = {}) {
  return { id, name, status, reason, evidence };
}

function compareRequired(left, right) {
  if (!left || !right) return 'MISSING';
  return left === right ? 'MATCH' : 'MISMATCH';
}

function compareLoose(left, right) {
  if (!left || !right) return 'MISSING';
  const a = normLoose(left);
  const b = normLoose(right);
  if (a === b) return 'MATCH';
  if (a.includes(b) || b.includes(a)) return 'PARTIAL';
  return 'MISMATCH';
}

function normalizeInputs(input) {
  const order = input.order || {};
  const idFields = fieldsOf(input.idCardOcr);
  const invoiceFields = fieldsOf(input.vehicleInvoiceOcr);
  return {
    order,
    idFields,
    invoiceFields,
    orderName: normName(firstValue(order, ['cardName', 'ownerName', 'customerName', 'name', 'buyerName'])),
    orderIdNumber: normId(firstValue(order, ['cardNumber', 'idNumber', 'idCardNo', 'certificateNo', 'cardNo'])),
    orderVin: normVin(firstValue(order, ['carFrame', 'vin', 'frameNo', 'vehicleVin'])),
    orderEngineNo: normLoose(firstValue(order, ['engineNo', 'engineNumber'])),
    orderModel: str(firstValue(order, ['modelName', 'carModel', 'vehicleModel', 'productName'])),
    orderPurchaseDate: parseDate(firstValue(order, ['carPurchaseTime', 'purchaseTime', 'purchaseDate'])),
    orderCreateDate: parseDate(firstValue(order, ['createTime', 'createdAt', 'createDate'])) || new Date(),
    idName: normName(firstValue(idFields, ['name', '姓名'])),
    idNumber: normId(firstValue(idFields, ['idNumber', 'cardNumber', '公民身份号码', '身份证号'])),
    invoiceBuyerName: normName(firstValue(invoiceFields, ['buyerName', '购买方名称', '购买方'])),
    invoiceVin: normVin(firstValue(invoiceFields, ['vin', '车辆识别代号/车架号码', '车架号码', 'VIN'])),
    invoiceEngineNo: normLoose(firstValue(invoiceFields, ['engineNo', '发动机号码', '发动机号'])),
    invoiceIssueDate: parseDate(firstValue(invoiceFields, ['issueDate', '开票日期', 'date'])),
    invoiceVehicleType: str(firstValue(invoiceFields, ['vehicleType', '车辆类型'])),
    invoiceBrandModel: str(firstValue(invoiceFields, ['brandModel', '厂牌型号', '品牌型号'])),
    invoiceSellerName: str(firstValue(invoiceFields, ['sellerName', '销货单位名称', '销售方名称'])),
  };
}

function auditPublicVehicle(n, cfg) {
  const carHost = n.order.carHost;
  const hostText = str(carHost);
  const companyNames = [n.invoiceBuyerName, n.orderName].filter(Boolean).filter(v => COMPANY_SUFFIX_RE.test(v));
  const publicHost = carHost !== undefined && carHost !== null && hostText !== '' && !['0', '个人', 'PERSONAL', 'false'].includes(hostText);
  if (cfg.allowCompanyVehicle) {
    return rule('R1_PUBLIC_VEHICLE', '公户车检查', 'PASS', '配置允许公户车', { carHost, companyNames });
  }
  if (publicHost || companyNames.length) {
    return rule('R1_PUBLIC_VEHICLE', '公户车检查', 'REJECT', '发现公户车或企业购买方特征', { carHost, companyNames });
  }
  return rule('R1_PUBLIC_VEHICLE', '公户车检查', 'PASS', '未发现公户车特征', { carHost, buyerName: n.invoiceBuyerName });
}

function auditBuyerName(n, cfg) {
  const cmp = compareRequired(n.orderName, n.invoiceBuyerName);
  const evidence = { orderName: n.orderName, invoiceBuyerName: n.invoiceBuyerName };
  if (cmp === 'MATCH') return rule('R2_BUYER_NAME', '购买方名称比对', 'PASS', '订单客户姓名与发票购买方一致', evidence);
  if (cmp === 'MISSING') {
    return rule('R2_BUYER_NAME', '购买方名称比对', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', '订单客户姓名或发票购买方缺失', evidence);
  }
  return rule('R2_BUYER_NAME', '购买方名称比对', 'MANUAL_REVIEW', '订单客户姓名与发票购买方不一致，需人工核验是否代购或录入错误', evidence);
}

function auditPurchaseAge(n, cfg) {
  const purchaseDate = n.invoiceIssueDate || n.orderPurchaseDate;
  const refDate = n.orderCreateDate || new Date();
  const evidence = {
    invoiceIssueDate: formatDate(n.invoiceIssueDate),
    orderPurchaseDate: formatDate(n.orderPurchaseDate),
    referenceDate: formatDate(refDate),
    maxPurchaseAgeDays: cfg.maxPurchaseAgeDays,
  };
  if (isPlusCOrder(n.order, cfg)) {
    return rule('R3_PURCHASE_AGE', '购车时间超期', 'PASS', 'Plus C/代步权益产品按5年车龄限制执行，不适用新车购车时间阈值', evidence);
  }
  if (!purchaseDate) {
    return rule('R3_PURCHASE_AGE', '购车时间超期', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', '发票开票日期和订单购车时间均缺失', evidence);
  }
  const ageDays = daysBetween(purchaseDate, refDate);
  evidence.ageDays = ageDays;
  if (ageDays < -7) return rule('R3_PURCHASE_AGE', '购车时间超期', 'MANUAL_REVIEW', '购车日期晚于订单创建日期，需核验日期来源', evidence);
  if (ageDays > cfg.maxPurchaseAgeDays) return rule('R3_PURCHASE_AGE', '购车时间超期', 'MANUAL_REVIEW', '购车日期超过配置阈值', evidence);
  return rule('R3_PURCHASE_AGE', '购车时间超期', 'PASS', '购车日期在允许范围内', evidence);
}

function auditIdCard(n, cfg) {
  const nameCmp = compareRequired(n.orderName, n.idName);
  const idCmp = compareRequired(n.orderIdNumber, n.idNumber);
  const evidence = {
    orderName: n.orderName,
    ocrName: n.idName,
    orderIdNumber: n.orderIdNumber,
    ocrIdNumber: n.idNumber,
  };
  if (nameCmp === 'MATCH' && idCmp === 'MATCH') return rule('R4_ID_CARD', '身份证比对', 'PASS', '订单姓名和身份证号与身份证OCR一致', evidence);
  if (nameCmp === 'MISSING' || idCmp === 'MISSING') {
    return rule('R4_ID_CARD', '身份证比对', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', '订单或身份证OCR关键字段缺失', evidence);
  }
  return rule('R4_ID_CARD', '身份证比对', 'REJECT', '订单姓名或身份证号与身份证OCR不一致', evidence);
}

function auditInvoice(n, cfg) {
  const vinCmp = compareRequired(n.orderVin, n.invoiceVin);
  const engineCmp = compareLoose(n.orderEngineNo, n.invoiceEngineNo);
  const evidence = {
    orderVin: n.orderVin,
    invoiceVin: n.invoiceVin,
    orderEngineNo: n.orderEngineNo,
    invoiceEngineNo: n.invoiceEngineNo,
    orderModel: n.orderModel,
    invoiceBrandModel: n.invoiceBrandModel,
    sellerName: n.invoiceSellerName,
  };
  if (vinCmp === 'MISMATCH') return rule('R5_INVOICE', '发票比对', 'REJECT', '订单VIN/车架号与发票不一致', evidence);
  if (vinCmp === 'MISSING') return rule('R5_INVOICE', '发票比对', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', '订单VIN或发票VIN缺失', evidence);
  if (engineCmp === 'MISMATCH') return rule('R5_INVOICE', '发票比对', 'MANUAL_REVIEW', 'VIN一致，但发动机号不一致，需人工核验', evidence);
  return rule('R5_INVOICE', '发票比对', 'PASS', '订单VIN与发票一致，车型营销名/厂牌公告型号差异不单独作为冲突', evidence);
}

function auditVehicleType(n, cfg) {
  const text = `${n.invoiceVehicleType} ${n.invoiceBrandModel} ${n.orderModel}`;
  const evidence = { vehicleType: n.invoiceVehicleType, brandModel: n.invoiceBrandModel, orderModel: n.orderModel };
  if (!str(text)) return rule('R6_VEHICLE_TYPE', '车辆类型判断', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', '车辆类型和车型字段缺失', evidence);
  if (containsAny(text, cfg.rejectVehicleTypeKeywords)) return rule('R6_VEHICLE_TYPE', '车辆类型判断', 'REJECT', '车辆类型命中非目标车型或营运特征', evidence);
  if (containsAny(text, cfg.allowedVehicleTypeKeywords)) return rule('R6_VEHICLE_TYPE', '车辆类型判断', 'PASS', '车辆类型符合新车投保审核范围', evidence);
  return rule('R6_VEHICLE_TYPE', '车辆类型判断', 'MANUAL_REVIEW', '车辆类型未命中明确允许范围，需人工判断', evidence);
}

function auditPlusCVehicleAge(n, cfg) {
  const plusC = isPlusCOrder(n.order, cfg);
  const refDate = n.orderCreateDate || new Date();
  const start = resolveVehicleAgeStartDate(n);
  const maxYears = Number(cfg.plusCMaxVehicleAgeYears || DEFAULT_CONFIG.plusCMaxVehicleAgeYears);
  const effectiveMaxYears = Number.isFinite(maxYears) && maxYears > 0
    ? maxYears
    : DEFAULT_CONFIG.plusCMaxVehicleAgeYears;
  const evidence = {
    productName: firstValue(n.order, ['productName', 'packageName', 'goodsName']),
    packageType: n.order.packageType,
    isPlusC: plusC,
    vehicleAgeStartSource: start.source,
    vehicleAgeStartDate: formatDate(start.date),
    referenceDate: formatDate(refDate),
    plusCMaxVehicleAgeYears: effectiveMaxYears,
  };
  if (!plusC) {
    return rule('R7_PLUS_C_VEHICLE_AGE', 'PlusC车龄限制', 'PASS', '非Plus C/代步权益产品，不适用5年车龄限制', evidence);
  }
  if (!start.date) {
    return rule('R7_PLUS_C_VEHICLE_AGE', 'PlusC车龄限制', cfg.manualReviewOnMissingCriticalFields ? 'MANUAL_REVIEW' : 'REJECT', 'Plus C/代步权益产品缺少车龄起算日期，无法判断是否超过5年', evidence);
  }
  const ageDays = daysBetween(start.date, refDate);
  evidence.vehicleAgeDays = ageDays;
  if (ageDays < -7) {
    return rule('R7_PLUS_C_VEHICLE_AGE', 'PlusC车龄限制', 'MANUAL_REVIEW', '车龄起算日期晚于审核基准日期，需人工核验日期来源', evidence);
  }
  evidence.vehicleAgeMonths = fullMonthsBetween(start.date, refDate);
  evidence.vehicleAgeYears = Number((Math.max(0, ageDays) / 365.25).toFixed(3));
  if (isDateAfterLimitYears(start.date, refDate, effectiveMaxYears)) {
    return rule('R7_PLUS_C_VEHICLE_AGE', 'PlusC车龄限制', 'REJECT', 'Plus C/代步权益产品不允许车龄超过5年的车辆购买', evidence);
  }
  return rule('R7_PLUS_C_VEHICLE_AGE', 'PlusC车龄限制', 'PASS', 'Plus C/代步权益产品车龄未超过5年', evidence);
}

function finalStatus(rules) {
  if (rules.some(r => r.status === 'REJECT')) return 'REJECT';
  if (rules.some(r => r.status === 'MANUAL_REVIEW')) return 'MANUAL_REVIEW';
  return 'PASS';
}

function reasonFor(status, rules) {
  if (status === 'PASS') return `${rules.length}项规则均通过`;
  const failed = rules.filter(r => r.status !== 'PASS');
  return failed.map(r => `${r.name}: ${r.reason}`).join('；');
}

function scoreFor(rules) {
  const score = rules.reduce((current, item) => {
    if (item.status === 'REJECT') return current - 35;
    if (item.status === 'MANUAL_REVIEW') return current - 15;
    return current;
  }, 100);
  return Math.max(0, score);
}

function run(input) {
  if (!input.order) throw new Error('order is required');
  if (!input.idCardOcr) throw new Error('idCardOcr is required');
  if (!input.vehicleInvoiceOcr) throw new Error('vehicleInvoiceOcr is required');
  const cfg = { ...DEFAULT_CONFIG, ...(input.config || {}) };
  emit('normalize', '归一化订单、身份证OCR和发票OCR字段');
  const normalized = normalizeInputs(input);
  emit('rules', '执行7条投保审核规则');
  const rules = [
    auditPublicVehicle(normalized, cfg),
    auditBuyerName(normalized, cfg),
    auditPurchaseAge(normalized, cfg),
    auditIdCard(normalized, cfg),
    auditInvoice(normalized, cfg),
    auditVehicleType(normalized, cfg),
    auditPlusCVehicleAge(normalized, cfg),
  ];
  const status = finalStatus(rules);
  const score = scoreFor(rules);
  const reason = reasonFor(status, rules);
  emit('complete', `审核完成: ${status}`, 'completed', { auditStatus: status, percent: 100 });
  return {
    success: true,
    status,
    score,
    reason,
    rules,
    summary: status === 'PASS' ? '审核通过' : status === 'REJECT' ? '审核驳回' : '转人工审核',
    normalized: {
      orderNo: firstValue(normalized.order, ['orderNo', 'orderNum']),
      orderName: normalized.orderName,
      orderIdNumber: normalized.orderIdNumber,
      orderVin: normalized.orderVin,
      idName: normalized.idName,
      idNumber: normalized.idNumber,
      invoiceBuyerName: normalized.invoiceBuyerName,
      invoiceVin: normalized.invoiceVin,
      invoiceIssueDate: formatDate(normalized.invoiceIssueDate),
      orderPurchaseDate: formatDate(normalized.orderPurchaseDate),
      vehicleType: normalized.invoiceVehicleType,
      brandModel: normalized.invoiceBrandModel,
    },
    config: cfg,
  };
}

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