const fs = require('fs')
const path = require('path')
const http = require('http')
const https = require('https')

const ROOT = path.resolve(__dirname, '..')
const DOCS_DIR = path.join(ROOT, 'docs')
const ENV_FILE = path.join(__dirname, '.env.local')
const KNOWLEDGE_FILE = path.join(DOCS_DIR, 'wc-spc-knowledge-base.json')

function loadLocalEnv() {
  if (!fs.existsSync(ENV_FILE)) return
  fs.readFileSync(ENV_FILE, 'utf8').split(/\r?\n/).forEach(line => {
    const text = line.trim()
    if (!text || text[0] === '#') return
    const index = text.indexOf('=')
    if (index <= 0) return
    const key = text.slice(0, index).trim()
    const value = text.slice(index + 1).trim().replace(/^["']|["']$/g, '')
    if (key && process.env[key] == null) process.env[key] = value
  })
}

loadLocalEnv()

const PORT = Number(process.env.AI_MCP_PORT || 3100)
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || ''
const DEEPSEEK_BASE_URL = (process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com').replace(/\/$/, '')
const DEEPSEEK_MODEL = process.env.DEEPSEEK_MODEL || 'deepseek-v4-pro'
const MES_BACKEND_URL = process.env.MES_BACKEND_URL || 'http://127.0.0.1:10050/submit/MESCommonBase.ashx'
const SPC_BACKEND_URL = (process.env.SPC_BACKEND_URL || 'http://localhost:57966').replace(/\/$/, '')
const ALLOW_BACKEND_CALLS = process.env.AI_MCP_ALLOW_BACKEND_CALLS === 'true'
const HOME_PAGE_URL = process.env.AI_HOME_PAGE_URL || 'http://127.0.0.1:1997/#/dashboard'
const HOME_PAGE_PATH = '/dashboard'
const QUALITY_DATA_DEFAULT_LIMIT = 50

function readJson(file, fallback) {
  try {
    return JSON.parse(fs.readFileSync(file, 'utf8'))
  } catch (error) {
    return fallback
  }
}

function emptyKnowledgeBase() {
  return {
    generatedAt: null,
    modules: [],
    apiMappings: [],
    sqlTables: [],
    qualityModules: [],
    qualityDataQueries: [],
    spcCharts: [],
    queryHints: []
  }
}

let knowledgeBase = readJson(KNOWLEDGE_FILE, emptyKnowledgeBase())

const QUALITY_DATA_QUERY_PROFILE = {
  key: 'assembly-quality-data',
  title: '质量数据查询',
  routePath: '/QualityAssurance/AssemblyQualitydataQuery',
  view: 'src\\views\\QualityAssurance\\AssemblyQualitydataQuery\\index.vue',
  module: 'QualityAssurance',
  method: 'selectDate',
  type: '11',
  nameOrSql: '质量数据_发动机质量数据_各个工位_视图_发动机型号_综合查询',
  exportType: '2001',
  exportNameOrSql: '质量数据查询_综合查询新',
  description: '按时间、工位、工件编号、订货号、订货号ID、机型号、工单ID、工单号、是否合格、测量位置、测量项目查询发动机各工位质量数据；无筛选条件时按全时间范围返回最新 50 条。',
  defaultStrategy: '无查询条件时使用 1900-01-01 00:00:00 至 2099-12-31 23:59:59、全部筛选_ischeck=0、PageCurrent=1、PageSize=50，并按生产日期等时间字段倒序展示最新 50 条。',
  latestSortFields: ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间'],
  parameters: [
    { name: '开始时间', arg: 'startTime', defaultValue: '1900-01-01 00:00:00', required: true },
    { name: '结束时间', arg: 'endTime', defaultValue: '2099-12-31 23:59:59', required: true },
    { name: '工位号_ischeck', arg: 'stationNumber', checkFor: '工位号' },
    { name: '工位号', arg: 'stationNumber' },
    { name: 'PageCurrent', arg: 'pageCurrent', defaultValue: 1 },
    { name: 'PageSize', arg: 'pageSize', defaultValue: 50 },
    { name: 'PageCount', defaultValue: '1111', type: 'int', output: '1' },
    { name: 'ItemCount', defaultValue: '1111', type: 'int', output: '1' },
    { name: '工件编号_ischeck', arg: 'engineId', checkFor: '工件编号' },
    { name: '工件编号', arg: 'engineId' },
    { name: '订货号_ischeck', arg: 'orderCode', checkFor: '订货号' },
    { name: '订货号', arg: 'orderCode' },
    { name: '订货号ID_ischeck', arg: 'orderCodeId', checkFor: '订货号ID' },
    { name: '订货号ID', arg: 'orderCodeId' },
    { name: '机型号_ischeck', arg: 'engineType', checkFor: '机型号' },
    { name: '机型号', arg: 'engineType' },
    { name: '工单ID_ischeck', arg: 'workOrderId', checkFor: '工单ID' },
    { name: '工单ID', arg: 'workOrderId' },
    { name: '工单号_ischeck', arg: 'workOrderCode', checkFor: '工单号' },
    { name: '工单号', arg: 'workOrderCode' },
    { name: '是否合格', arg: 'isOk', defaultValue: '0' },
    { name: '测量位置_ischeck', arg: 'testPosition', checkFor: '测量位置' },
    { name: '测量位置', arg: 'testPosition' },
    { name: '测量项目_ischeck', arg: 'testItem', checkFor: '测量项目' },
    { name: '测量项目', arg: 'testItem' }
  ],
  tableFields: [
    { label: '工位名称', prop: '工序名称' },
    { label: '工件编号', prop: '发动机号' },
    { label: '机型号', prop: '机型号' },
    { label: '订货号', prop: '订货号' },
    { label: '订货号ID', prop: '订货号ID' },
    { label: '工单ID', prop: '工单ID' },
    { label: '工单号', prop: '工单号' },
    { label: '测量位置', prop: '测量位置' },
    { label: '测量项目', prop: '测量项目' },
    { label: '测量值', prop: '测量值' },
    { label: '理论值', prop: '理论值' },
    { label: '上限值', prop: '上限值' },
    { label: '下限值', prop: '下限值' },
    { label: '测量单位', prop: '测量单位' },
    { label: '生产日期', prop: '生产日期' },
    { label: '合格标志', prop: '合格标志' }
  ]
}

function lower(value) {
  return String(value || '').toLowerCase()
}

function unique(values) {
  return Array.from(new Set(values.filter(Boolean)))
}

function escapeHtml(value) {
  return String(value == null ? '' : value)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

function table(headers, rows) {
  const dataRows = Array.isArray(rows) ? rows : []
  let html = '<table class="ai-result-table"><thead><tr>'
  headers.forEach(header => {
    html += '<th>' + escapeHtml(header.label) + '</th>'
  })
  html += '</tr></thead><tbody>'
  if (!dataRows.length) {
    html += '<tr><td colspan="' + headers.length + '">无匹配数据</td></tr>'
  } else {
    dataRows.forEach(row => {
      html += '<tr>'
      headers.forEach(header => {
        const value = row && row[header.key]
        html += '<td>' + escapeHtml(Array.isArray(value) ? value.join(', ') : value) + '</td>'
      })
      html += '</tr>'
    })
  }
  html += '</tbody></table>'
  return html
}

function isNgValue(value) {
  const text = String(value == null ? '' : value).trim()
  return /不合格|NG|false/i.test(text) || text === '0'
}

function firstValue(row, keys) {
  for (let i = 0; i < keys.length; i++) {
    const value = row && row[keys[i]]
    if (value != null && value !== '') return value
  }
  return ''
}

function detailTable(headers, rows) {
  const dataRows = Array.isArray(rows) ? rows : []
  let html = '<div class="ai-detail-table-wrap"><table class="ai-detail-table"><thead><tr>'
  headers.forEach(header => {
    html += '<th>' + escapeHtml(header.label) + '</th>'
  })
  html += '</tr></thead><tbody>'
  if (!dataRows.length) {
    html += '<tr><td colspan="' + headers.length + '">无数据</td></tr>'
  } else {
    dataRows.forEach(row => {
      html += '<tr>'
      headers.forEach(header => {
        const value = row && row[header.key]
        const className = header.status ? (isNgValue(value) ? ' class="ai-status-ng"' : '') : ''
        html += '<td' + className + '>' + escapeHtml(Array.isArray(value) ? value.join(', ') : value) + '</td>'
      })
      html += '</tr>'
    })
  }
  html += '</tbody></table></div>'
  return html
}

function normalizeRows(rows, limit) {
  if (!Array.isArray(rows)) return []
  return rows.slice(0, limit || 30).map(row => {
    if (!row || typeof row !== 'object') return { value: row }
    return row
  })
}

function htmlForRows(title, rows, limit) {
  const normalized = normalizeRows(rows, limit || 30)
  const keys = unique(normalized.flatMap(row => Object.keys(row))).slice(0, 12)
  if (!keys.length) return '<h4>' + escapeHtml(title) + '</h4>' + table([{ key: 'message', label: '结果' }], [{ message: '无数据' }])
  return '<h4>' + escapeHtml(title) + '</h4>' + table(keys.map(key => ({ key, label: key })), normalized)
}

function qualityDetailRows(rows) {
  return normalizeRows(rows, 50).map(row => ({
    productionDate: firstValue(row, ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间']),
    stationName: firstValue(row, ['工序名称', '工位名称', '工位号', '指南工位号']),
    workpieceNo: firstValue(row, ['发动机号', '工件编号', '工件号', '总成号']),
    measureItem: firstValue(row, ['测量项目', '测量内容', '项目']),
    measurePosition: firstValue(row, ['测量位置', '位置']),
    measureValue: firstValue(row, ['测量值', '实际值', '值']),
    upperLimit: firstValue(row, ['上限值', '上限']),
    lowerLimit: firstValue(row, ['下限值', '下限']),
    passFlag: firstValue(row, ['合格标志', '是否合格', '合格'])
  }))
}

function htmlForQualityDetailRows(rows) {
  return '<h4>质量数据明细（部分，按生产日期倒序，最多50条）</h4>' + detailTable([
    { key: 'productionDate', label: '生产日期' },
    { key: 'stationName', label: '工位名称' },
    { key: 'workpieceNo', label: '工件编号' },
    { key: 'measureItem', label: '测量项目' },
    { key: 'measurePosition', label: '测量位置' },
    { key: 'measureValue', label: '测量值' },
    { key: 'upperLimit', label: '上限值' },
    { key: 'lowerLimit', label: '下限值' },
    { key: 'passFlag', label: '合格标志', status: true }
  ], qualityDetailRows(rows))
}

function tokens(query) {
  const source = lower(query)
  const parts = source
    .replace(/[^\w\u4e00-\u9fa5]+/g, ' ')
    .split(/\s+/)
    .map(item => item.trim())
    .filter(item => item && item.length > 1)
  const grams = []
  parts.forEach(part => {
    if (/[\u4e00-\u9fa5]/.test(part) && part.length > 2) {
      for (let size = 2; size <= Math.min(4, part.length); size++) {
        for (let index = 0; index <= part.length - size; index++) {
          grams.push(part.substr(index, size))
        }
      }
    }
  })
  return unique(parts.concat(grams)).filter(item => ['页面', '接口', '数据', '查询', '功能', '模块'].indexOf(item) === -1)
}

function scoreText(text, query) {
  const haystack = lower(text)
  const compactHaystack = haystack.replace(/\s+/g, '')
  const q = lower(query)
  const compactQuery = q.replace(/\s+/g, '')
  if (!q) return 1
  let score = compactQuery && compactHaystack.indexOf(compactQuery) > -1 ? 50 : 0
  tokens(query).forEach(part => {
    if (haystack.indexOf(part) > -1) score += part.length
  })
  return score
}

function pick(rows, query, fields, limit) {
  return (rows || [])
    .map(row => {
      const text = fields.map(field => {
        const value = row[field]
        return Array.isArray(value) ? value.join(' ') : value
      }).join(' ')
      return { row, score: scoreText(text, query) }
    })
    .filter(item => item.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, limit || 20)
    .map(item => item.row)
}

function routeCandidates(question, limit) {
  const query = String(question || '').replace(/打开|跳转|进入|导航|前往|去到|查看|页面|模块/g, ' ')
  return (knowledgeBase.modules || [])
    .filter(row => row.routePath)
    .map(row => {
      const text = [row.module, row.title, row.view, row.routePath, row.domains, row.functionNames, row.keywords].join(' ')
      let score = scoreText(text, query || question)
      const compactQuestion = String(question || '').replace(/\s+/g, '').toLowerCase()
      const compactPath = String(row.routePath || '').replace(/[\/_\s]+/g, '').toLowerCase()
      const compactTitle = String(row.title || '').replace(/[\/_\s]+/g, '').toLowerCase()
      if (compactQuestion.indexOf('spc分析') > -1 && compactPath.indexOf('searchdataspcanalysis') > -1) score += 100
      if (compactQuestion.indexOf('spc') > -1 && compactTitle.indexOf('spc') > -1) score += 30
      if (compactQuestion.indexOf('质量andon') > -1 && compactPath.indexOf('qualityandon') > -1) score += 40
      if (compactQuestion.indexOf('统计') === -1 && compactQuestion.indexOf('质量andon') > -1 && compactPath.indexOf('qualityandonstatistics') > -1) score -= 25
      if (compactQuestion.indexOf('统计') > -1 && compactPath.indexOf('statistics') > -1) score += 40
      return Object.assign({ score }, row)
    })
    .filter(row => row.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, limit || 8)
}

function moduleFeatureRows() {
  if (Array.isArray(knowledgeBase.moduleFeatureProfiles) && knowledgeBase.moduleFeatureProfiles.length) {
    return knowledgeBase.moduleFeatureProfiles
  }
  return (knowledgeBase.modules || []).map(row => Object.assign({
    view: row.view,
    module: row.module,
    title: row.title,
    routePath: row.routePath,
    filters: [],
    buttons: [],
    tableFields: [],
    lifecycle: [],
    methods: [],
    backendCalls: row.calls || [],
    actions: [],
    riskLevel: row.queryable ? '包含查询入口' : '未识别到数据调用',
    description: row.title || row.view,
    keywords: row.keywords || []
  }, row.featureProfile || {}))
}

function moduleFeatureCandidates(question, limit) {
  const q = String(question || '')
  return moduleFeatureRows()
    .map(row => {
      const text = [
        row.module,
        row.title,
        row.view,
        row.routePath,
        row.description,
        row.actions,
        (row.filters || []).map(item => [item.label, item.model, item.control].join(' ')).join(' '),
        (row.buttons || []).map(item => [item.label, item.method, item.action].join(' ')).join(' '),
        (row.tableFields || []).map(item => [item.label, item.prop].join(' ')).join(' '),
        (row.backendCalls || []).map(item => item.nameOrSql || item.NameOrSql).join(' '),
        row.keywords
      ].join(' ')
      let score = scoreText(text, question)
      if (/统计/.test(q) && /Statistics/i.test(row.view + ' ' + row.routePath + ' ' + row.title)) score += 40
      if (!/统计/.test(q) && /(页面|查询页面|模块|功能|有什么)/.test(q) && /Statistics/i.test(row.view + ' ' + row.routePath + ' ' + row.title)) score -= 25
      if (/质量.*Andon|Andon.*质量/i.test(q) && /qualityAndon\\index\.vue/i.test(row.view)) score += 40
      return Object.assign({ score }, row)
    })
    .filter(row => row.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, limit || 8)
}

function isFeatureQuestion(question) {
  const text = String(question || '')
  return /(功能|有什么|有哪些|介绍|说明|页面|模块|按钮|字段|表格|筛选|条件|接口|入口|怎么用|如何使用|对应.*代码|功能代码|详细解析)/i.test(text) &&
    !/(查数据|查询数据|数据库|明细数据|最近\s*\d+\s*天.*数据|按.*查询.*数据|统计数据|实际数据|返回数据)/i.test(text)
}

function isNavigationQuestion(question) {
  return /(打开|跳转|进入|导航|前往|去到|切换到)/.test(String(question || '')) || /查看.*页面/.test(String(question || ''))
}

function isHomeNavigationQuestion(question) {
  const text = String(question || '').replace(/\s+/g, '')
  return /(打开|跳转|进入|导航|前往|去到|切换到|返回|回到).*(主页|首页|主页面|仪表盘|dashboard)/i.test(text) ||
    /(主页|首页|主页面|仪表盘|dashboard).*(打开|跳转|进入|导航|前往|去到|切换到)/i.test(text)
}

function navigationResponse(question) {
  if (!isNavigationQuestion(question)) return null
  if (isHomeNavigationQuestion(question)) {
    const target = {
      title: '主页',
      routePath: HOME_PAGE_PATH,
      url: HOME_PAGE_URL,
      view: 'dashboard'
    }
    return {
      html: '<h4>页面导航</h4>' + table([
        { key: 'title', label: '页面' },
        { key: 'routePath', label: '路由' },
        { key: 'url', label: '目标地址' }
      ], [target]),
      actions: [{ type: 'navigate', path: HOME_PAGE_PATH, url: HOME_PAGE_URL, title: target.title, view: target.view }]
    }
  }
  const candidates = routeCandidates(question, 8)
  if (!candidates.length) {
    return { html: table([{ key: '提示', label: '提示' }], [{ 提示: '未找到匹配页面，请换一个页面名或模块名。' }]), actions: [] }
  }
  const target = candidates[0]
  return {
    html: '<h4>页面导航</h4>' + table([
      { key: 'title', label: '页面' },
      { key: 'routePath', label: '路由' },
      { key: 'view', label: '源码文件' }
    ], [target]),
    actions: [{ type: 'navigate', path: target.routePath, title: target.title, view: target.view }]
  }
}

function isQualityQuestion(question) {
  return /质量|SPC|巡检|条码|Andon|安灯|测量|工位|过程能力|趋势|直方|正态|排列|X-R|XR|X-S|XS/i.test(String(question || ''))
}

function listQualityModules(limit) {
  return (knowledgeBase.qualityModules || []).slice(0, limit || 50).map(row => ({
    title: row.Title,
    routePath: row.RoutePath,
    domains: row.Domains,
    queryable: row.Queryable,
    view: row.View
  }))
}

function qualitySearchResponse(question) {
  const rows = pick(knowledgeBase.qualityModules, question, ['Title', 'View', 'Domains', 'QueryNames'], 12)
  return '<h4>质量管理匹配模块</h4>' + table([
    { key: 'Title', label: '页面' },
    { key: 'RoutePath', label: '路由' },
    { key: 'Domains', label: '业务域' },
    { key: 'Queryable', label: '可查询' }
  ], rows)
}

function isSimpleLookupQuestion(question) {
  const text = String(question || '')
  return /^(项目|功能|模块|页面|查询质量数据|质量数据查询|质量Andon|Andon)/i.test(text) || /查看.*页面/.test(text)
}

function featureSummaryRows(profile) {
  return [
    { name: '页面', value: profile.title || '' },
    { name: '路由', value: profile.routePath || '' },
    { name: '源码', value: profile.view || '' },
    { name: '一级模块', value: profile.module || '' },
    { name: '功能概要', value: profile.description || '' },
    { name: '读写风险', value: profile.riskLevel || '' }
  ]
}

function qualityDataProfileRows(profile) {
  return [
    { name: '功能名称', value: profile.title || '' },
    { name: '页面路由', value: profile.routePath || '' },
    { name: '源码文件', value: profile.view || '' },
    { name: '默认查询入口', value: profile.nameOrSql || '' },
    { name: '导出入口', value: profile.exportNameOrSql || '' },
    { name: '说明', value: profile.description || '' }
  ]
}

function qualityDataParameterRows(profile, args) {
  const inputs = buildStructuredQualityDataParams(args || {}, profile)
  return inputs.map(item => ({
    name: item.name,
    value: item.value,
    output: item.output || '',
    type: item.type || ''
  }))
}

function describeQualityDataQuery(profile, args) {
  let html = '<h4>质量数据查询功能</h4>' + table([
    { key: 'name', label: '项目' },
    { key: 'value', label: '内容' }
  ], qualityDataProfileRows(profile))
  html += '<h4>查询参数</h4>' + table([
    { key: 'name', label: '参数' },
    { key: 'value', label: '值' },
    { key: 'type', label: '类型' },
    { key: 'output', label: '输出' }
  ], qualityDataParameterRows(profile, args))
  html += '<h4>结果字段</h4>' + table([
    { key: 'label', label: '字段' },
    { key: 'prop', label: '数据字段' },
    { key: 'width', label: '宽度' }
  ], profile.tableFields || [])
  html += '<h4>查询入口</h4>' + table([
    { key: 'type', label: '类型' },
    { key: 'nameOrSql', label: '入口名称' },
    { key: 'role', label: '角色' }
  ], [{
    type: profile.type,
    nameOrSql: profile.nameOrSql,
    role: '页面已有查询方法 selectDate() 的主查询入口'
  }])
  html += '<h4>导出入口</h4>' + table([
    { key: 'exportType', label: '类型' },
    { key: 'exportNameOrSql', label: '导出入口' }
  ], [{
    exportType: profile.exportType,
    exportNameOrSql: profile.exportNameOrSql
  }])
  return html
}

function describeModuleFeatures(question, limit) {
  const candidates = moduleFeatureCandidates(question || '', limit || 5)
  if (!candidates.length) {
    return '<h4>模块功能说明</h4>' + table([{ key: 'message', label: '提示' }], [{ message: '未找到匹配模块，请换一个页面名、路由或业务关键词。' }])
  }
  const target = candidates[0]
  let html = '<h4>模块功能说明</h4>' + table([
    { key: 'name', label: '项目' },
    { key: 'value', label: '内容' }
  ], featureSummaryRows(target))
  html += '<h4>筛选条件</h4>' + table([
    { key: 'label', label: '条件' },
    { key: 'model', label: '绑定字段' },
    { key: 'control', label: '控件' },
    { key: 'type', label: '类型' }
  ], target.filters || [])
  html += '<h4>页面操作</h4>' + table([
    { key: 'label', label: '按钮/操作' },
    { key: 'action', label: '动作' },
    { key: 'method', label: '方法' },
    { key: 'icon', label: '图标' }
  ], target.buttons || [])
  html += '<h4>表格字段</h4>' + table([
    { key: 'label', label: '显示字段' },
    { key: 'prop', label: '数据字段' },
    { key: 'width', label: '宽度' }
  ], target.tableFields || [])
  html += '<h4>自动加载</h4>' + table([
    { key: 'hook', label: '生命周期' },
    { key: 'calls', label: '调用方法' }
  ], target.lifecycle || [])
  html += '<h4>后端数据入口</h4>' + table([
    { key: 'source', label: '来源' },
    { key: 'type', label: '类型' },
    { key: 'action', label: '动作' },
    { key: 'domain', label: '业务域' },
    { key: 'nameOrSql', label: '查询入口/SQL' },
    { key: 'role', label: '说明' }
  ], target.backendCalls || [])
  if (candidates.length > 1) {
    html += '<h4>其他匹配模块</h4>' + table([
      { key: 'title', label: '页面' },
      { key: 'routePath', label: '路由' },
      { key: 'riskLevel', label: '读写风险' }
    ], candidates.slice(1, 6))
  }
  return html
}

function searchModuleFeatures(question, limit) {
  const rows = moduleFeatureCandidates(question || '', limit || 20).map(row => ({
    title: row.title,
    routePath: row.routePath,
    module: row.module,
    actions: (row.actions || []).join('、'),
    filters: (row.filters || []).map(item => item.label).slice(0, 6).join('、'),
    buttons: (row.buttons || []).map(item => item.label).slice(0, 6).join('、'),
    riskLevel: row.riskLevel
  }))
  return '<h4>模块功能匹配结果</h4>' + table([
    { key: 'title', label: '页面' },
    { key: 'routePath', label: '路由' },
    { key: 'module', label: '一级模块' },
    { key: 'actions', label: '动作' },
    { key: 'filters', label: '筛选条件' },
    { key: 'buttons', label: '页面按钮' },
    { key: 'riskLevel', label: '读写风险' }
  ], rows)
}

function spcChartType(question) {
  const text = String(question || '')
  const rules = [
    { type: 'xr', re: /X-R|XR|极差|均值.*极差/i },
    { type: 'xs', re: /X-S|XS|标准差|均值.*标准差/i },
    { type: 'pareto', re: /排列|帕累托|pareto/i },
    { type: 'processCapability', re: /过程能力|CPK|CP|能力/i },
    { type: 'normalDistribution', re: /正态|分布|normal/i },
    { type: 'histogram', re: /直方|频数|histogram/i },
    { type: 'sampleTrend', re: /样本趋势|样本/i },
    { type: 'basicTrend', re: /基本趋势|趋势|折线/i }
  ]
  const match = rules.find(rule => rule.re.test(text))
  return match ? match.type : ''
}

function parseDateRange(question) {
  const text = String(question || '')
  const explicit = text.match(/(\d{4}[-/]\d{1,2}[-/]\d{1,2}).{0,8}(\d{4}[-/]\d{1,2}[-/]\d{1,2})/)
  if (explicit) return { startTime: explicit[1].replace(/\//g, '-'), endTime: explicit[2].replace(/\//g, '-') }
  const today = new Date()
  const end = today.toISOString().slice(0, 10)
  const daysMatch = text.match(/最近\s*(\d+)\s*天/)
  const days = daysMatch ? Math.min(Math.max(Number(daysMatch[1]), 1), 90) : 7
  const startDate = new Date(today.getTime() - (days - 1) * 24 * 60 * 60 * 1000)
  return { startTime: startDate.toISOString().slice(0, 10), endTime: end }
}

function parseQuotedOrNamed(question, names) {
  const text = String(question || '')
  const quoted = text.match(/[“"']([^“”"']+)[”"']/)
  if (quoted) return quoted[1]
  for (let i = 0; i < names.length; i++) {
    const name = names[i]
    const re = new RegExp(name + '\\s*[:：]?\\s*([^，,。\\s]+)')
    const match = text.match(re)
    if (match) return match[1]
  }
  return ''
}

function parseSpcParams(question) {
  const dateRange = parseDateRange(question)
  const sampleSize = (String(question).match(/样本容量\s*[:：]?\s*(\d+)/) || [])[1] || '5'
  const sampleNumber = (String(question).match(/样本(?:个数|数量)\s*[:：]?\s*(\d+)/) || [])[1] || '20'
  const usl = (String(question).match(/USL|上限|上规格限/i) || {}).index != null ? ((String(question).match(/(?:USL|上限|上规格限)\s*[:：]?\s*(-?\d+(?:\.\d+)?)/i) || [])[1] || '') : ''
  const lsl = (String(question).match(/LSL|下限|下规格限/i) || {}).index != null ? ((String(question).match(/(?:LSL|下限|下规格限)\s*[:：]?\s*(-?\d+(?:\.\d+)?)/i) || [])[1] || '') : ''
  return Object.assign({
    opName: parseQuotedOrNamed(question, ['工位', '工位号', '工位名称']),
    model: parseQuotedOrNamed(question, ['机型', '型号', '零件号']),
    measureName: parseQuotedOrNamed(question, ['测量位置', '位置']),
    measureContent: parseQuotedOrNamed(question, ['测量项目', '测量内容', '项目']),
    sampleSize,
    sampleNumber,
    usl,
    lsl,
    pageSize: 20,
    pageCurrent: 1
  }, dateRange)
}

function missingSpcParams(params) {
  const missing = []
  if (!params.opName) missing.push('工位')
  if (!params.measureName) missing.push('测量位置')
  if (!params.measureContent) missing.push('测量项目')
  return missing
}

function buildUrl(endpoint, type, params) {
  const query = {
    type,
    OPNameCheck: params.opName ? 'true' : 'false',
    OPName: params.opName || '',
    shaftNameCheck: params.measureName ? 'true' : 'false',
    shaftName: params.measureName || '',
    EngineTypeIDCheck: params.model ? 'true' : 'false',
    EngineTypeID: params.model || '',
    itemNameCheck: params.measureContent ? 'true' : 'false',
    itemName: params.measureContent || '',
    startTime: params.startTime,
    endTime: params.endTime,
    Yangben: params.sampleSize || '5',
    Num: params.sampleNumber || '20'
  }
  if (params.usl) query.Usl = params.usl
  if (params.lsl) query.Lsl = params.lsl
  if (type === 'GetDataTableByConditent') {
    query.page = params.pageCurrent || 1
    query.rows = params.pageSize || 20
  }
  const qs = Object.keys(query).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(query[key])).join('&')
  return SPC_BACKEND_URL + '/submit/' + endpoint + '?' + qs
}

function getJson(url) {
  return new Promise((resolve, reject) => {
    const target = new URL(url)
    const client = target.protocol === 'https:' ? https : http
    const req = client.request({
      method: 'POST',
      hostname: target.hostname,
      port: target.port || (target.protocol === 'https:' ? 443 : 80),
      path: target.pathname + target.search
    }, res => {
      let text = ''
      res.setEncoding('utf8')
      res.on('data', chunk => { text += chunk })
      res.on('end', () => {
        try {
          resolve(JSON.parse(text))
        } catch (error) {
          resolve(text)
        }
      })
    })
    req.on('error', reject)
    req.end()
  })
}

function numericValuesFromRows(rows) {
  return normalizeRows(rows, 1000).map(row => {
    const keys = Object.keys(row)
    const key = keys.find(item => /测量值|value|Value|数值/i.test(item)) || keys.find(item => !Number.isNaN(Number(row[item])))
    return key ? Number(row[key]) : NaN
  }).filter(value => !Number.isNaN(value))
}

function summaryForValues(values) {
  if (!values.length) return []
  const sum = values.reduce((acc, item) => acc + item, 0)
  const avg = sum / values.length
  const variance = values.reduce((acc, item) => acc + Math.pow(item - avg, 2), 0) / values.length
  return [
    { name: '样本数', value: values.length },
    { name: '最小值', value: Math.min.apply(null, values).toFixed(4) },
    { name: '最大值', value: Math.max.apply(null, values).toFixed(4) },
    { name: '平均值', value: avg.toFixed(4) },
    { name: '标准差', value: Math.sqrt(variance).toFixed(4) }
  ]
}

function simpleLineChart(title, values) {
  return {
    id: 'chart_' + Date.now() + '_' + Math.floor(Math.random() * 10000),
    title,
    option: {
      title: { text: title, left: 'center' },
      tooltip: { trigger: 'axis' },
      grid: { left: 45, right: 20, top: 55, bottom: 35 },
      xAxis: { type: 'category', data: values.map((_, index) => String(index + 1)) },
      yAxis: { type: 'value', scale: true },
      series: [{ name: '测量值', type: 'line', smooth: true, data: values }]
    }
  }
}

function simpleBarChart(title, values) {
  const bins = {}
  values.forEach(value => {
    const key = value.toFixed(2)
    bins[key] = (bins[key] || 0) + 1
  })
  const keys = Object.keys(bins).sort((a, b) => Number(a) - Number(b))
  return {
    id: 'chart_' + Date.now() + '_' + Math.floor(Math.random() * 10000),
    title,
    option: {
      title: { text: title, left: 'center' },
      tooltip: { trigger: 'axis' },
      grid: { left: 45, right: 20, top: 55, bottom: 45 },
      xAxis: { type: 'category', data: keys },
      yAxis: { type: 'value' },
      series: [{ name: '频数', type: 'bar', data: keys.map(key => bins[key]) }]
    }
  }
}

async function querySpcChart(args) {
  const chartType = args.chartType || spcChartType(args.query || '') || 'basicTrend'
  const params = Object.assign(parseSpcParams(args.query || ''), args)
  const missing = missingSpcParams(params)
  if (missing.length) {
    return {
      html: '<h4>SPC 查询条件不足</h4>' + table([
        { key: 'field', label: '缺少条件' },
        { key: 'example', label: '示例' }
      ], missing.map(field => ({ field, example: field + '：请在问题中明确指定' }))),
      charts: [],
      data: { missing, params }
    }
  }
  const chartMap = {
    basicTrend: { endpoint: 'QualityData_TrendPictureBasic.ashx', type: 'GetBaseTrendData', title: '基本趋势图' },
    sampleTrend: { endpoint: 'QualityData_TrendPicture.ashx', type: 'GetBaseTrendData', title: '样本趋势图' },
    histogram: { endpoint: 'QualityData_Histogram.ashx', type: 'GetDataTableByConditent', title: '直方图' },
    normalDistribution: { endpoint: 'QualityData_NormalDistribution.ashx', type: 'GetBaseTrendData', title: '正态分布图' },
    processCapability: { endpoint: 'QualityData_NormalDistribution.ashx', type: 'GetBaseTrendData', title: '过程能力分析' },
    pareto: { endpoint: 'QualityData_Pareto.ashx', type: 'GetChart', title: '排列图' },
    xr: { endpoint: 'QualityData_XR.ashx', type: 'GetBaseTrendData', title: 'X-R 控制图' },
    xs: { endpoint: 'QualityData_XS.ashx', type: 'GetBaseTrendData', title: 'X-S 控制图' }
  }
  const meta = chartMap[chartType] || chartMap.basicTrend
  const url = buildUrl(meta.endpoint, meta.type, params)
  let raw
  try {
    raw = await getJson(url)
  } catch (error) {
    return {
      html: '<h4>SPC 接口调用失败</h4>' + table([
        { key: 'name', label: '项目' },
        { key: 'value', label: '内容' }
      ], [
        { name: '错误', value: error.message },
        { name: '接口', value: url }
      ]),
      charts: [],
      data: { error: error.message, params, url }
    }
  }
  const rows = Array.isArray(raw) ? raw : (raw && Array.isArray(raw.rows) ? raw.rows : [])
  const values = numericValuesFromRows(rows)
  const charts = values.length ? [
    /histogram|normalDistribution|processCapability|pareto/.test(chartType) ? simpleBarChart(meta.title, values) : simpleLineChart(meta.title, values)
  ] : []
  const conditionRows = [
    { name: '图表类型', value: meta.title },
    { name: '开始时间', value: params.startTime },
    { name: '结束时间', value: params.endTime },
    { name: '工位', value: params.opName },
    { name: '测量位置', value: params.measureName },
    { name: '测量项目', value: params.measureContent },
    { name: '样本容量', value: params.sampleSize },
    { name: '样本个数', value: params.sampleNumber }
  ]
  const summaryRows = summaryForValues(values)
  const sourceRows = [{ endpoint: meta.endpoint, type: meta.type, url }]
  let html = '<h4>SPC 查询条件</h4>' + table([{ key: 'name', label: '条件' }, { key: 'value', label: '值' }], conditionRows)
  html += '<h4>SPC 统计摘要</h4>' + table([{ key: 'name', label: '指标' }, { key: 'value', label: '值' }], summaryRows)
  html += htmlForRows('明细数据', rows, 20)
  html += '<h4>数据来源</h4>' + table([{ key: 'endpoint', label: '接口' }, { key: 'type', label: '类型' }, { key: 'url', label: '调用地址' }], sourceRows)
  if (!rows.length && typeof raw === 'string') {
    html += '<h4>原始返回</h4><pre>' + escapeHtml(raw.slice(0, 1000)) + '</pre>'
  }
  return { html, charts, data: { params, raw, rows, values, url } }
}

function postJson(url, payload, headers) {
  return new Promise((resolve, reject) => {
    const target = new URL(url)
    const client = target.protocol === 'https:' ? https : http
    const body = typeof payload === 'string' ? payload : JSON.stringify(payload)
    const bodyBuffer = Buffer.from(body, 'utf8')
    const req = client.request({
      method: 'POST',
      hostname: target.hostname,
      port: target.port || (target.protocol === 'https:' ? 443 : 80),
      path: target.pathname + target.search,
      headers: Object.assign({ 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': bodyBuffer.length }, headers || {})
    }, res => {
      let text = ''
      res.setEncoding('utf8')
      res.on('data', chunk => { text += chunk })
      res.on('end', () => {
        try {
          resolve(JSON.parse(text))
        } catch (error) {
          resolve({ raw: text, statusCode: res.statusCode })
        }
      })
    })
    req.on('error', reject)
    req.write(bodyBuffer)
    req.end()
  })
}

function isQualityDataQuestion(question) {
  const text = String(question || '')
  if (isFeatureQuestion(text)) return false
  if (/质量数据/.test(text) && /(查|查询|查找|获取|显示|展示|列出|返回|最近|最新|明细|数据)/.test(text)) return true
  return /(查询质量数据|质量数据查询|发动机质量数据|工件质量数据|测量值查询)/i.test(text) ||
    /(查数据|查询数据|明细数据|实际数据|返回数据|最近\s*\d+\s*天.*数据|按.*查询.*数据|统计数据)/i.test(text) ||
    /(查询|查看|统计|列出).*(质量|Andon|安灯|巡检|条码|测量|工位).*(数据|明细|记录|最近|今天|昨天)/i.test(text) ||
    /ANDON_ANDON信息_备份_质量Andon综合查询/i.test(text)
}

function qualityDataQueryProfiles() {
  return [QUALITY_DATA_QUERY_PROFILE]
}

function findStructuredQualityDataProfile(args) {
  const queryText = [args.query, args.module, args.nameOrSql].filter(Boolean).join(' ')
  const exactName = String(args.nameOrSql || '').trim()
  return qualityDataQueryProfiles()
    .map(profile => {
      let score = 0
      if (exactName && profile.nameOrSql === exactName) score += 1000
      const text = [profile.title, profile.routePath, profile.view, profile.nameOrSql, profile.description, profile.defaultUseWhen, profile.keywords].join(' ')
      score += scoreText(text, queryText)
      if (/(查询质量数据|质量数据查询|发动机质量数据|工件质量数据|测量值查询)/i.test(queryText)) score += 300
      return Object.assign({ score }, profile)
    })
    .filter(profile => profile.score > 0)
    .sort((a, b) => b.score - a.score)[0]
}

function createDataQueries() {
  return (knowledgeBase.modules || []).flatMap(module => {
    return (module.primaryQueries || [])
      .filter(query => query && query.kind === 'create-data' && query.nameOrSql)
      .map(query => Object.assign({
        moduleTitle: module.title,
        routePath: module.routePath,
        moduleName: module.module,
        qualityRelated: module.qualityRelated
      }, query))
  })
}

function findQualityDataQuery(args) {
  const exactName = String(args.nameOrSql || '').trim()
  const structured = exactName && exactName !== QUALITY_DATA_QUERY_PROFILE.nameOrSql
    ? null
    : QUALITY_DATA_QUERY_PROFILE
  if (structured) {
    return Object.assign({
      view: structured.view,
      moduleTitle: structured.title,
      routePath: structured.routePath,
      moduleName: structured.module,
      queryTables: [],
      backendQueryName: structured.nameOrSql,
      structuredProfile: structured
    }, structured)
  }
  const queryText = [args.query, args.module, args.nameOrSql].filter(Boolean).join(' ')
  const rows = createDataQueries().filter(row => {
    if (exactName) return row.nameOrSql === exactName
    const text = [row.moduleTitle, row.routePath, row.view, row.domain, row.nameOrSql].join(' ')
    return row.qualityRelated || scoreText(text, queryText) > 0
  })
  return rows
    .map(row => {
      const text = [row.moduleTitle, row.routePath, row.view, row.domain, row.nameOrSql].join(' ')
      let score = exactName && row.nameOrSql === exactName ? 1000 : scoreText(text, queryText)
      if (/质量.*Andon|Andon.*质量|ANDON_ANDON信息/i.test(queryText) && /ANDON_ANDON信息_备份_质量Andon综合查询_分页/i.test(row.nameOrSql)) score += 300
      if (String(row.type) === '11') score += 30
      if (String(row.type) === '2001' || /导出|下载|excel/i.test(row.nameOrSql)) score -= 80
      return Object.assign({ score }, row)
    })
    .filter(row => row.score > 0)
    .sort((a, b) => b.score - a.score)[0]
}

function normalizeMesParams(params) {
  if (Array.isArray(params)) {
    return params.map(item => {
      if (Array.isArray(item)) return { name: item[0], value: item[1], type: item[2], output: item[3] }
      return item
    }).filter(item => item && item.name)
  }
  if (params && typeof params === 'object') {
    return Object.keys(params).map(key => ({ name: key, value: params[key] }))
  }
  return []
}

function buildQualityAndonParams(args) {
  const range = Object.assign(parseDateRange(args.query || ''), {
    startTime: args.startTime,
    endTime: args.endTime
  })
  const stationNumber = args.stationNumber || args.opName || parseQuotedOrNamed(args.query || '', ['工位号', '工位', '工位名称'])
  const pageCurrent = Math.max(Number(args.pageCurrent || 1), 1)
  const pageSize = Math.min(Math.max(Number(args.pageSize || args.limit || 30), 1), 100)
  return [
    { name: '开始时间', value: range.startTime },
    { name: '结束时间', value: range.endTime },
    { name: '工位号_ischeck', value: stationNumber ? 1 : 0 },
    { name: '工位号', value: stationNumber || '' },
    { name: 'PageCurrent', value: pageCurrent },
    { name: 'PageSize', value: pageSize },
    { name: 'PageCount', value: '1111', type: 'int', output: '1' },
    { name: 'ItemCount', value: '1111', type: 'int', output: '1' }
  ]
}

function withDateTime(value, endOfDay) {
  if (!value) return ''
  const text = String(value)
  if (/\d{1,2}:\d{2}/.test(text)) return text
  return text + (endOfDay ? ' 23:59:59' : ' 00:00:00')
}

function normalizePassFlag(value) {
  const text = String(value == null ? '' : value).trim()
  if (!text || text === '全部') return '0'
  if (/不合格|NG|false/i.test(text)) return '2'
  if (/合格|OK|true/i.test(text)) return '1'
  return text
}

function parsePassFlagFromQuery(query) {
  const text = String(query || '')
  if (/不合格|NG/i.test(text)) return '2'
  if (/全部/.test(text)) return '0'
  if (/合格|OK/i.test(text)) return '1'
  return ''
}

function pickArg(args, names) {
  for (let i = 0; i < names.length; i++) {
    const value = args[names[i]]
    if (value != null && value !== '') return value
  }
  return ''
}

function hasExplicitDateRange(args) {
  const text = String(args.query || '')
  return Boolean(args.startTime || args.endTime || /(\d{4}[-/]\d{1,2}[-/]\d{1,2})|今天|昨日|昨天|最近\s*\d+\s*天/.test(text))
}

function buildStructuredQualityDataParams(args, profile) {
  const range = hasExplicitDateRange(args)
    ? Object.assign(parseDateRange(args.query || ''), { startTime: args.startTime, endTime: args.endTime })
    : { startTime: '1900-01-01', endTime: '2099-12-31' }
  const values = {
    startTime: withDateTime(range.startTime, false),
    endTime: withDateTime(range.endTime, true),
    stationNumber: pickArg(args, ['stationNumber', 'opName']) || parseQuotedOrNamed(args.query || '', ['工位号', '工位']),
    engineId: pickArg(args, ['engineId', 'workpieceNo']) || parseQuotedOrNamed(args.query || '', ['工件编号', '工件号', '发动机号', '总成号']),
    orderCode: pickArg(args, ['orderCode']) || parseQuotedOrNamed(args.query || '', ['订货号']),
    orderCodeId: pickArg(args, ['orderCodeId']) || parseQuotedOrNamed(args.query || '', ['订货号ID']),
    engineType: pickArg(args, ['engineType', 'model']) || parseQuotedOrNamed(args.query || '', ['机型号', '机型', '型号']),
    workOrderId: pickArg(args, ['workOrderId']) || parseQuotedOrNamed(args.query || '', ['工单ID']),
    workOrderCode: pickArg(args, ['workOrderCode']) || parseQuotedOrNamed(args.query || '', ['工单号']),
    isOk: normalizePassFlag(pickArg(args, ['isOk', 'qualified', 'passFlag']) || parseQuotedOrNamed(args.query || '', ['是否合格', '合格标志']) || parsePassFlagFromQuery(args.query || '')),
    testPosition: pickArg(args, ['testPosition', 'measurePosition', 'measureName']) || parseQuotedOrNamed(args.query || '', ['测量位置', '位置']),
    testItem: pickArg(args, ['testItem', 'measureItem', 'measureContent']) || parseQuotedOrNamed(args.query || '', ['测量项目', '测量内容', '项目']),
    pageCurrent: Math.max(Number(args.pageCurrent || 1), 1),
    pageSize: Math.min(Math.max(Number(args.pageSize || args.limit || 50), 1), 50)
  }
  return (profile.parameters || []).map(param => {
    if (param.output) return { name: param.name, value: param.defaultValue, type: param.type, output: param.output }
    if (param.checkFor) {
      const value = values[param.arg]
      return { name: param.name, value: value ? 1 : 0 }
    }
    return { name: param.name, value: values[param.arg] != null && values[param.arg] !== '' ? values[param.arg] : (param.defaultValue == null ? '' : param.defaultValue) }
  })
}

function buildMesPayload(query, args) {
  const providedParams = normalizeMesParams(args.params)
  const params = providedParams.length
    ? providedParams
    : (query.structuredProfile ? buildStructuredQualityDataParams(args, query.structuredProfile)
      : (/ANDON_ANDON信息_备份_质量Andon综合查询/i.test(query.nameOrSql) ? buildQualityAndonParams(args) : []))
  return {
    type: String(args.type || query.type || '11'),
    name: query.nameOrSql,
    param: JSON.stringify(params),
    pageSize: args.pageSize,
    pageList: args.pageList,
    UserID: args.userId || 'AI',
    ModularID: query.routePath || query.view || '/AI'
  }
}

function parsePossibleJson(value) {
  if (typeof value !== 'string') return value
  try {
    return JSON.parse(value)
  } catch (error) {
    return value
  }
}

function unwrapMesResponse(response) {
  let body = response
  if (body && typeof body === 'object' && typeof body.raw === 'string') body = parsePossibleJson(body.raw)
  if (typeof body === 'string') body = parsePossibleJson(body)
  if (body && typeof body === 'object' && typeof body.d === 'string') body = parsePossibleJson(body.d)
  if (body && typeof body === 'object' && typeof body.data === 'string') body.data = parsePossibleJson(body.data)
  const data = body && body.data && typeof body.data === 'object' ? body.data : body
  const rows = Array.isArray(data) ? data
    : (data && Array.isArray(data.result)) ? data.result
      : (data && Array.isArray(data.rows)) ? data.rows
        : (data && Array.isArray(data.data)) ? data.data
          : []
  return {
    body,
    data,
    rows,
    output: data && Array.isArray(data.output) ? data.output : []
  }
}

function parseDateValue(value) {
  if (value == null || value === '') return 0
  const text = String(value).replace(/\//g, '-')
  const time = Date.parse(text)
  return Number.isNaN(time) ? 0 : time
}

function isQualityChartQuestion(question) {
  const text = String(question || '')
  return isQualityDataQuestion(text) && qualityChartTypes(text).length > 0
}

function qualityChartTypes(question) {
  const text = String(question || '')
  const types = []
  const has = words => words.some(word => text.toLowerCase().indexOf(word.toLowerCase()) > -1)
  if (has(['\u6837\u672c\u8d8b\u52bf\u56fe'])) types.push('sampleTrend')
  if (has(['\u76f4\u65b9\u56fe'])) types.push('histogram')
  if (has(['\u5de5\u5e8f\u80fd\u529b\u5206\u6790\u56fe'])) types.push('processCapability')
  if (has(['\u6392\u5217\u56fe', 'Pareto'])) types.push('pareto')
  if (has(['\u5747\u503c\u6781\u5dee\u56fe'])) types.push('xr')
  if (has(['\u5747\u503c\u6807\u51c6\u5dee\u56fe'])) types.push('xs')
  if (has(['SPC\u57fa\u672c\u8d8b\u52bf\u56fe', '\u57fa\u672c\u8d8b\u52bf\u56fe'])) types.push('basicTrend')
  return unique(types)
}

function numericQualityValue(row) {
  const value = firstValue(row, ['测量值', '实际值', 'value', 'Value', '数值'])
  const number = Number(value)
  return Number.isNaN(number) ? NaN : number
}

function constantLineValue(rows, keys) {
  for (let i = 0; i < rows.length; i++) {
    const value = firstValue(rows[i], keys)
    const number = Number(value)
    if (!Number.isNaN(number)) return number
  }
  return NaN
}

function uniqueRowValues(rows, keys) {
  return unique((rows || []).map(row => firstValue(row, keys)).filter(Boolean))
}

function qualityChartPoints(rows) {
  const sortedRows = normalizeRows(rows, QUALITY_DATA_DEFAULT_LIMIT).slice().sort((a, b) => {
    const diff = parseDateValue(firstValue(a, ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间'])) -
      parseDateValue(firstValue(b, ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间']))
    return diff || 0
  })
  return sortedRows.map((row, index) => ({
    row,
    value: numericQualityValue(row),
    label: firstValue(row, ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间']) || String(index + 1)
  })).filter(point => !Number.isNaN(point.value))
}

function qualityChartTitle(baseTitle, points) {
  const rows = points.map(point => point.row)
  const stationNames = uniqueRowValues(rows, ['工序名称', '工位名称', '工位号', '指南工位号'])
  const positions = uniqueRowValues(rows, ['测量位置', '位置'])
  return baseTitle + ' - ' +
    (stationNames.length === 1 ? stationNames[0] : '多工位') +
    ' / ' +
    (positions.length === 1 ? positions[0] : '多测量位置')
}

function mixedMeasurePosition(points) {
  return uniqueRowValues(points.map(point => point.row), ['测量位置', '位置']).length > 1
}

function qualitySpecMarkLines(points) {
  const rows = points.map(point => point.row)
  const markLineData = []
  const upper = constantLineValue(rows, ['上限值', '上限'])
  const lower = constantLineValue(rows, ['下限值', '下限'])
  const theory = constantLineValue(rows, ['理论值', '目标值'])
  if (!Number.isNaN(upper)) markLineData.push({ name: '上限值', yAxis: upper })
  if (!Number.isNaN(lower)) markLineData.push({ name: '下限值', yAxis: lower })
  if (!Number.isNaN(theory)) markLineData.push({ name: '理论值', yAxis: theory })
  return markLineData
}

function polishChart(chart, palette) {
  if (!chart || !chart.option) return chart
  const colors = palette || ['#2563eb', '#16a34a', '#f97316', '#dc2626', '#7c3aed']
  chart.option.color = colors
  chart.option.backgroundColor = '#ffffff'
  chart.option.title = Object.assign({
    left: 'center',
    top: 10,
    textStyle: { color: '#1f2937', fontSize: 14, fontWeight: 600 }
  }, chart.option.title || {})
  chart.option.tooltip = Object.assign({
    trigger: 'axis',
    backgroundColor: 'rgba(17, 24, 39, 0.9)',
    borderWidth: 0,
    textStyle: { color: '#fff', fontSize: 12 },
    axisPointer: { type: 'line', lineStyle: { color: '#94a3b8', width: 1, type: 'dashed' } }
  }, chart.option.tooltip || {})
  chart.option.grid = Object.assign({ left: 54, right: 28, top: 72, bottom: 48, containLabel: true }, chart.option.grid || {})
  const axisStyle = {
    axisLine: { lineStyle: { color: '#cbd5e1' } },
    axisTick: { lineStyle: { color: '#cbd5e1' } },
    axisLabel: { color: '#475569', fontSize: 11 },
    splitLine: { lineStyle: { color: '#eef2f7', type: 'dashed' } }
  }
  if (Array.isArray(chart.option.xAxis)) {
    chart.option.xAxis = chart.option.xAxis.map(axis => Object.assign({}, axisStyle, axis))
  } else {
    chart.option.xAxis = Object.assign({}, axisStyle, chart.option.xAxis || {})
  }
  if (Array.isArray(chart.option.yAxis)) {
    chart.option.yAxis = chart.option.yAxis.map(axis => Object.assign({}, axisStyle, axis))
  } else {
    chart.option.yAxis = Object.assign({}, axisStyle, chart.option.yAxis || {})
  }
  chart.option.legend = Object.assign({
    top: 36,
    textStyle: { color: '#475569', fontSize: 11 },
    itemWidth: 12,
    itemHeight: 8
  }, chart.option.legend || {})
  if (Array.isArray(chart.option.series)) {
    chart.option.series = chart.option.series.map((series, index) => {
      const next = Object.assign({
        symbol: series.type === 'line' ? 'circle' : undefined,
        symbolSize: series.type === 'line' ? 5 : undefined,
        lineStyle: series.type === 'line' ? { width: 2 } : undefined,
        itemStyle: { borderRadius: series.type === 'bar' ? [4, 4, 0, 0] : 0 }
      }, series)
      if (next.markLine) {
        next.markLine = Object.assign({
          symbol: 'none',
          label: { color: '#64748b', fontSize: 11 },
          lineStyle: { color: colors[(index + 2) % colors.length], width: 1.5, type: 'dashed' }
        }, next.markLine)
      }
      return next
    })
  }
  return chart
}

function lineQualityChart(type, title, points, data, seriesName) {
  const markLineData = qualitySpecMarkLines(points)
  return polishChart({
    id: 'chart_' + type + '_' + Date.now() + '_' + Math.floor(Math.random() * 10000),
    title,
    mixedMeasurePosition: mixedMeasurePosition(points),
    option: {
      title: { text: title, left: 'center' },
      tooltip: { trigger: 'axis' },
      grid: { left: 50, right: 24, top: 62, bottom: 48 },
      xAxis: { type: 'category', data: points.map(point => point.label), axisLabel: { rotate: 35 } },
      yAxis: { type: 'value', scale: true },
      series: [{ name: seriesName || '测量值', type: 'line', smooth: true, data, markLine: markLineData.length ? { symbol: 'none', data: markLineData } : undefined }]
    }
  })
}

function histogramQualityChart(points) {
  const values = points.map(point => point.value)
  const min = Math.min.apply(null, values)
  const max = Math.max.apply(null, values)
  const binCount = Math.min(10, Math.max(4, Math.ceil(Math.sqrt(values.length))))
  const width = max === min ? 1 : (max - min) / binCount
  const bins = Array.from({ length: binCount }, (_, index) => ({ label: (min + index * width).toFixed(2) + '-' + (min + (index + 1) * width).toFixed(2), count: 0 }))
  values.forEach(value => {
    const index = max === min ? 0 : Math.min(Math.floor((value - min) / width), binCount - 1)
    bins[index].count += 1
  })
  const title = qualityChartTitle('直方图', points)
  return polishChart({ id: 'chart_histogram_' + Date.now() + '_' + Math.floor(Math.random() * 10000), title, mixedMeasurePosition: mixedMeasurePosition(points), option: { title: { text: title, left: 'center' }, tooltip: { trigger: 'axis' }, grid: { left: 48, right: 24, top: 62, bottom: 58 }, xAxis: { type: 'category', data: bins.map(bin => bin.label), axisLabel: { rotate: 35 } }, yAxis: { type: 'value' }, series: [{ name: '频数', type: 'bar', data: bins.map(bin => bin.count) }] } }, ['#2563eb'])
}

function processCapabilityQualityChart(points) {
  const values = points.map(point => point.value)
  const summary = summaryForValues(values)
  const upper = constantLineValue(points.map(point => point.row), ['上限值', '上限'])
  const lower = constantLineValue(points.map(point => point.row), ['下限值', '下限'])
  const avg = values.reduce((acc, item) => acc + item, 0) / values.length
  const variance = values.reduce((acc, item) => acc + Math.pow(item - avg, 2), 0) / values.length
  const sigma = Math.sqrt(variance)
  const cp = !Number.isNaN(upper) && !Number.isNaN(lower) && sigma > 0 ? (upper - lower) / (6 * sigma) : NaN
  const cpk = !Number.isNaN(upper) && !Number.isNaN(lower) && sigma > 0 ? Math.min((upper - avg) / (3 * sigma), (avg - lower) / (3 * sigma)) : NaN
  const title = qualityChartTitle('工序能力分析图', points)
  return polishChart({ id: 'chart_processCapability_' + Date.now() + '_' + Math.floor(Math.random() * 10000), title, mixedMeasurePosition: mixedMeasurePosition(points), option: { title: { text: title, left: 'center' }, tooltip: { trigger: 'axis' }, grid: { left: 60, right: 24, top: 72, bottom: 42 }, xAxis: { type: 'category', data: summary.map(item => item.name).concat(['CP', 'CPK']) }, yAxis: { type: 'value', scale: true }, series: [{ name: '能力指标', type: 'bar', data: summary.map(item => Number(item.value)).concat([Number.isNaN(cp) ? 0 : Number(cp.toFixed(4)), Number.isNaN(cpk) ? 0 : Number(cpk.toFixed(4))]) }] } }, ['#16a34a'])
}

function paretoQualityChart(points) {
  const counts = {}
  points.forEach(point => {
    const key = firstValue(point.row, ['合格标志', '是否合格', '测量项目', '测量位置']) || '数据项'
    counts[key] = (counts[key] || 0) + 1
  })
  const items = Object.keys(counts).map(name => ({ name, count: counts[name] })).sort((a, b) => b.count - a.count)
  const total = items.reduce((acc, item) => acc + item.count, 0) || 1
  let cumulative = 0
  const line = items.map(item => { cumulative += item.count; return Number((cumulative / total * 100).toFixed(2)) })
  const title = qualityChartTitle('排列图', points)
  return polishChart({ id: 'chart_pareto_' + Date.now() + '_' + Math.floor(Math.random() * 10000), title, mixedMeasurePosition: mixedMeasurePosition(points), option: { title: { text: title, left: 'center' }, tooltip: { trigger: 'axis' }, legend: { top: 30 }, grid: { left: 48, right: 48, top: 76, bottom: 50 }, xAxis: { type: 'category', data: items.map(item => item.name), axisLabel: { rotate: 25 } }, yAxis: [{ type: 'value' }, { type: 'value', min: 0, max: 100, axisLabel: { formatter: '{value}%' } }], series: [{ name: '数量', type: 'bar', data: items.map(item => item.count) }, { name: '累计占比', type: 'line', yAxisIndex: 1, data: line }] } }, ['#2563eb', '#f97316'])
}

function groupedValues(values, size, mapper) {
  const result = []
  for (let i = 0; i < values.length; i += size) {
    const group = values.slice(i, i + size)
    if (group.length) result.push(mapper(group))
  }
  return result
}

function controlQualityChart(type, points) {
  const values = points.map(point => point.value)
  const size = 5
  const labels = groupedValues(values, size, (_, index) => index).map((_, index) => '组' + (index + 1))
  const averages = groupedValues(values, size, group => group.reduce((acc, item) => acc + item, 0) / group.length)
  const secondary = groupedValues(values, size, group => {
    if (type === 'xr') return Math.max.apply(null, group) - Math.min.apply(null, group)
    const avg = group.reduce((acc, item) => acc + item, 0) / group.length
    return Math.sqrt(group.reduce((acc, item) => acc + Math.pow(item - avg, 2), 0) / group.length)
  })
  const title = qualityChartTitle(type === 'xr' ? '均值极差图' : '均值标准差图', points)
  return polishChart({ id: 'chart_' + type + '_' + Date.now() + '_' + Math.floor(Math.random() * 10000), title, mixedMeasurePosition: mixedMeasurePosition(points), option: { title: { text: title, left: 'center' }, tooltip: { trigger: 'axis' }, legend: { top: 30 }, grid: { left: 50, right: 44, top: 76, bottom: 42 }, xAxis: { type: 'category', data: labels }, yAxis: [{ type: 'value', scale: true }, { type: 'value', scale: true }], series: [{ name: '均值', type: 'line', smooth: true, data: averages }, { name: type === 'xr' ? '极差' : '标准差', type: 'line', smooth: true, yAxisIndex: 1, data: secondary }] } }, ['#2563eb', '#dc2626'])
}

function qualityDataCharts(rows, question) {
  const points = qualityChartPoints(rows)
  if (!points.length) return []
  return qualityChartTypes(question).map(type => {
    if (type === 'histogram') return histogramQualityChart(points)
    if (type === 'processCapability') return processCapabilityQualityChart(points)
    if (type === 'pareto') return paretoQualityChart(points)
    if (type === 'xr') return controlQualityChart('xr', points)
    if (type === 'xs') return controlQualityChart('xs', points)
    if (type === 'sampleTrend') return lineQualityChart('sampleTrend', qualityChartTitle('样本趋势图', points), points, points.map(point => point.value), '测量值')
    return lineQualityChart('basicTrend', qualityChartTitle('SPC基本趋势图', points), points, points.map(point => point.value), '测量值')
  }).filter(Boolean)
}

function latestQualityRows(rows, profile, limit) {
  const source = Array.isArray(rows) ? rows.slice() : []
  const maxRows = Math.min(Math.max(Number(limit || QUALITY_DATA_DEFAULT_LIMIT), 1), QUALITY_DATA_DEFAULT_LIMIT)
  const fields = (profile && profile.latestSortFields && profile.latestSortFields.length)
    ? profile.latestSortFields
    : ['生产日期', '操作时间', '完成时间', '到达时间', '离开时间', '时间']
  const sortField = fields.find(field => source.some(row => row && row[field]))
  if (sortField) {
    source.sort((a, b) => parseDateValue(b && b[sortField]) - parseDateValue(a && a[sortField]))
  }
  return source.slice(0, maxRows)
}

async function queryQualityData(args) {
  const queryLimit = Math.min(Math.max(Number((args && (args.limit || args.pageSize)) || QUALITY_DATA_DEFAULT_LIMIT), 1), QUALITY_DATA_DEFAULT_LIMIT)
  const selected = findQualityDataQuery(args || {})
  if (!selected) {
    const rows = pick(knowledgeBase.qualityModules, args.query || '', ['Title', 'View', 'Domains', 'QueryNames'], 10)
    return {
      html: '<h4>未定位到质量查询入口</h4>' + table([
        { key: 'Title', label: '页面' },
        { key: 'RoutePath', label: '路由' },
        { key: 'QueryNames', label: '可用查询入口' }
      ], rows),
      data: { matched: false, candidates: rows }
    }
  }
  const payload = buildMesPayload(selected, args || {})
  const sourceRows = [{
    module: selected.moduleTitle,
    routePath: selected.routePath,
    type: payload.type,
    name: payload.name,
    note: 'MESCommonBase 查询入口，复用页面已有查询能力'
  }]
  let response
  try {
    response = await postJson(MES_BACKEND_URL, JSON.stringify(payload))
  } catch (error) {
    return {
      html: '<h4>质量数据查询入口已定位</h4>' + table([
        { key: 'module', label: '页面' },
        { key: 'routePath', label: '路由' },
        { key: 'type', label: '类型' },
        { key: 'name', label: '查询入口' },
        { key: 'note', label: '说明' }
      ], sourceRows) + '<h4>接口调用失败</h4>' + table([
        { key: 'name', label: '项目' },
        { key: 'value', label: '内容' }
      ], [
        { name: '错误', value: error.message },
        { name: '接口', value: MES_BACKEND_URL }
      ]),
      data: { matched: true, error: error.message, payload, query: selected }
    }
  }
  const parsed = unwrapMesResponse(response)
  const displayRows = selected.structuredProfile
    ? latestQualityRows(parsed.rows, selected.structuredProfile, queryLimit)
    : normalizeRows(parsed.rows, queryLimit)
  const paramRows = parsePossibleJson(payload.param)
  let html = '<h4>质量数据查询入口</h4>' + table([
    { key: 'module', label: '页面' },
    { key: 'routePath', label: '路由' },
    { key: 'type', label: '类型' },
    { key: 'name', label: '查询入口' },
    { key: 'note', label: '说明' }
  ], sourceRows)
  html += '<h4>查询条件</h4>' + table([
    { key: 'name', label: '参数' },
    { key: 'value', label: '值' },
    { key: 'output', label: '输出参数' }
  ], paramRows)
  html += htmlForQualityDetailRows(displayRows)
  if (parsed.output.length) html += '<h4>输出参数</h4>' + htmlForRows('分页信息', parsed.output, 5)
  if (!parsed.rows.length && typeof (response && response.raw) === 'string') {
    html += '<h4>原始返回</h4><pre>' + escapeHtml(response.raw.slice(0, 1000)) + '</pre>'
  }
  return { html, data: { matched: true, query: selected, payload, response, rows: displayRows, rawRows: parsed.rows, output: parsed.output } }
}

async function askDeepSeek(question, toolHtml, context, options) {
  const fallback = toolHtml || '<p>DeepSeek 未配置或暂不可用。</p>'
  if (!DEEPSEEK_API_KEY) return fallback
  const opts = options || {}
  const mergedContext = opts.useProjectContext
    ? Object.assign({
      matchedModules: routeCandidates(question, 8),
      qualityModules: pick(knowledgeBase.qualityModules, question, ['Title', 'View', 'Domains', 'QueryNames'], 8),
      moduleFeatures: moduleFeatureCandidates(question, 8),
      qualityDataQueries: qualityDataQueryProfiles(),
      spcCharts: pick(knowledgeBase.spcCharts, question, ['chartType', 'title', 'apiFile', 'endpoint', 'functions'], 8)
    }, context || {})
    : (context || {})
  const system = [
    '你是 WC-SPC MES-Manager_View 网页内置的 DeepSeekV4Pro AI 助手，回答以 DeepSeek 推理和表达为主。',
    '使用中文回答，优先输出规范 HTML 表格；必要时可补充简短段落。',
    '如果提供了工具调用结果，必须以工具结果为事实依据组织回答，不要编造工具结果中没有的数据。',
    '如果没有工具调用结果，就直接回答用户问题；涉及项目实时数据时，说明需要调用项目已有 HMI 查询入口。',
    '数据查询必须依赖项目人机界面中已有的函数、CreateData 查询入口、MESCommonBase.ashx 或已有 SPC 接口；禁止建议或执行数据库直连，回答中不要出现数据库直连方案。',
    '当上下文包含页面功能画像、质量数据查询画像或查询结果时，要说明如何通过页面已有查询入口完成查询，并用 HTML 表格组织结果。',
    '不要输出可执行 JavaScript；图表由系统结构化渲染。'
  ].join('\n')
  const messages = [{ role: 'system', content: system }]
  if (Object.keys(mergedContext).length) {
    messages.push({ role: 'user', content: '工具/项目上下文：\n' + JSON.stringify(mergedContext, null, 2) })
  }
  if (toolHtml) messages.push({ role: 'user', content: '工具调用结果 HTML：\n' + toolHtml })
  messages.push({ role: 'user', content: question })
  try {
    const response = await postJson(DEEPSEEK_BASE_URL + '/chat/completions', {
      model: DEEPSEEK_MODEL,
      messages,
      temperature: 0.2
    }, { Authorization: 'Bearer ' + DEEPSEEK_API_KEY })
    return response && response.choices && response.choices[0] && response.choices[0].message
      ? response.choices[0].message.content
      : fallback
  } catch (error) {
    return fallback + '<p>DeepSeek 调用失败：' + escapeHtml(error.message) + '</p>'
  }
}

function localAnswer(question) {
  if (isQualityDataQuestion(question)) {
    const profile = findStructuredQualityDataProfile({ query: question }) || qualityDataQueryProfiles()[0]
    if (profile) return describeQualityDataQuery(profile, { query: question })
  }
  if (isFeatureQuestion(question)) return describeModuleFeatures(question, 8)
  if (/项目|功能|模块|有哪些/.test(question)) {
    return '<h4>项目功能概览</h4>' + table([
      { key: 'name', label: '项目' },
      { key: 'value', label: '数量' }
    ], [
      { name: '页面模块', value: knowledgeBase.modules.length },
      { name: 'API 映射', value: knowledgeBase.apiMappings.length },
      { name: '质量相关模块', value: knowledgeBase.qualityModules.length },
      { name: '质量数据查询画像', value: qualityDataQueryProfiles().length },
      { name: '页面功能画像', value: moduleFeatureRows().length },
      { name: 'SPC 图表类型', value: knowledgeBase.spcCharts.length }
    ]) + '<h4>质量相关模块</h4>' + table([
      { key: 'title', label: '页面' },
      { key: 'routePath', label: '路由' },
      { key: 'domains', label: '业务域' }
    ], listQualityModules(12))
  }
  if (isQualityQuestion(question)) return qualitySearchResponse(question)
  const rows = routeCandidates(question, 12)
  return '<h4>项目知识库匹配结果</h4>' + table([
    { key: 'title', label: '页面' },
    { key: 'routePath', label: '路由' },
    { key: 'view', label: '源码文件' },
    { key: 'callCount', label: '调用数' }
  ], rows)
}

function buildAssistantContext(question) {
  return {
    matchedModules: routeCandidates(question, 8),
    moduleFeatures: moduleFeatureCandidates(question, 8),
    qualityModules: pick(knowledgeBase.qualityModules, question, ['Title', 'View', 'Domains', 'QueryNames'], 8),
    qualityDataQueries: qualityDataQueryProfiles(),
    spcCharts: pick(knowledgeBase.spcCharts, question, ['chartType', 'title', 'apiFile', 'endpoint', 'functions'], 8)
  }
}

function localResponse(question) {
  if (isQualityDataQuestion(question)) {
    const profile = findStructuredQualityDataProfile({ query: question }) || qualityDataQueryProfiles()[0]
    if (profile) return describeQualityDataQuery(profile, { query: question })
  }
  if (isFeatureQuestion(question)) return describeModuleFeatures(question, 8)
  if (isQualityQuestion(question)) return qualitySearchResponse(question)
  return localAnswer(question)
}

const tools = [
  { name: 'project_summary', description: '返回 WC-SPC 项目结构、知识库、质量模块和 SPC 图表概览。', inputSchema: { type: 'object', properties: {} } },
  { name: 'search_project_functions', description: '按关键字搜索项目页面、接口和功能。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' }}, required: ['query'] } },
  { name: 'describe_module_features', description: '按模块名、页面名、路由或业务关键词返回页面功能画像，说明筛选条件、按钮、表格字段和后端入口，不调用数据库。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' }}, required: ['query'] } },
  { name: 'search_module_features', description: '搜索所有页面功能画像，返回匹配页面功能摘要，不调用数据库。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' }}, required: ['query'] } },
  { name: 'list_quality_modules', description: '列出质量管理相关页面。', inputSchema: { type: 'object', properties: { limit: { type: 'number' } } } },
  { name: 'search_quality_functions', description: '搜索质量管理相关页面和查询入口。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' }}, required: ['query'] } },
  { name: 'describe_quality_data_query', description: '按查询质量数据相关问题返回页面已有查询入口、参数映射和结果字段，不调用数据库。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' }}, required: ['query'] } },
  { name: 'query_quality_data', description: '按项目 CreateData 查询入口调用质量管理数据，返回 HTML 表格结果。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, nameOrSql: { type: 'string' }, params: { type: 'object' }, startTime: { type: 'string' }, endTime: { type: 'string' }, stationNumber: { type: 'string' }, pageCurrent: { type: 'number' }, pageSize: { type: 'number' }, limit: { type: 'number' } } } },
  { name: 'list_spc_charts', description: '列出 SPC 图表类型与对应接口。', inputSchema: { type: 'object', properties: {} } },
  { name: 'spc_query_chart', description: '根据条件调用 SPC 接口并返回图表、摘要和明细。', inputSchema: { type: 'object', properties: { query: { type: 'string' }, chartType: { type: 'string' }, opName: { type: 'string' }, measureName: { type: 'string' }, measureContent: { type: 'string' }, startTime: { type: 'string' }, endTime: { type: 'string' } } } },
  { name: 'reload_project_index', description: '重新加载知识库。', inputSchema: { type: 'object', properties: {} } },
  { name: 'call_mes_backend', description: '调用 MESCommonBase.ashx，默认禁用。', inputSchema: { type: 'object', properties: { payload: { type: 'object' }}, required: ['payload'] } }
]

async function toolResult(name, args) {
  const limit = Math.min(Number(args.limit || 30), 100)
  if (name === 'project_summary') {
    const rows = [
      { name: '知识库生成时间', value: knowledgeBase.generatedAt || '未生成' },
      { name: '页面模块', value: knowledgeBase.modules.length },
      { name: 'API 映射', value: knowledgeBase.apiMappings.length },
      { name: '质量相关模块', value: knowledgeBase.qualityModules.length },
      { name: 'SPC 图表类型', value: knowledgeBase.spcCharts.length },
      { name: 'DeepSeek 已配置', value: Boolean(DEEPSEEK_API_KEY) },
      { name: 'SPC 后端地址', value: SPC_BACKEND_URL }
    ]
    return { text: JSON.stringify(rows, null, 2), html: '<h4>项目概览</h4>' + table([{ key: 'name', label: '项目' }, { key: 'value', label: '值' }], rows), data: rows }
  }
  if (name === 'search_project_functions') {
    const rows = routeCandidates(args.query, limit)
    return { text: JSON.stringify(rows, null, 2), html: table([{ key: 'title', label: '页面' }, { key: 'routePath', label: '路由' }, { key: 'view', label: '源码' }, { key: 'callCount', label: '调用数' }], rows), data: rows }
  }
  if (name === 'describe_module_features') {
    const rows = moduleFeatureCandidates(args.query, limit)
    return { text: JSON.stringify(rows, null, 2), html: describeModuleFeatures(args.query, limit), data: rows }
  }
  if (name === 'search_module_features') {
    const rows = moduleFeatureCandidates(args.query, limit)
    return { text: JSON.stringify(rows, null, 2), html: searchModuleFeatures(args.query, limit), data: rows }
  }
  if (name === 'list_quality_modules') {
    const rows = listQualityModules(limit)
    return { text: JSON.stringify(rows, null, 2), html: table([{ key: 'title', label: '页面' }, { key: 'routePath', label: '路由' }, { key: 'domains', label: '业务域' }, { key: 'queryable', label: '可查询' }], rows), data: rows }
  }
  if (name === 'search_quality_functions') {
    const rows = pick(knowledgeBase.qualityModules, args.query, ['Title', 'View', 'Domains', 'QueryNames'], limit)
    return { text: JSON.stringify(rows, null, 2), html: table([{ key: 'Title', label: '页面' }, { key: 'RoutePath', label: '路由' }, { key: 'Domains', label: '业务域' }, { key: 'QueryNames', label: '查询入口' }], rows), data: rows }
  }
  if (name === 'describe_quality_data_query') {
    const profile = findStructuredQualityDataProfile(args || {}) || qualityDataQueryProfiles()[0]
    const rows = profile ? qualityDataParameterRows(profile, args) : []
    const html = profile
      ? describeQualityDataQuery(profile, args || {})
      : '<h4>质量数据查询功能</h4>' + table([{ key: 'message', label: '提示' }], [{ message: '未找到质量数据查询画像' }])
    return { text: JSON.stringify(profile || {}, null, 2), html, data: { profile, rows } }
  }
  if (name === 'query_quality_data') {
    const result = await queryQualityData(args || {})
    return { text: JSON.stringify(result.data, null, 2), html: result.html, data: result.data }
  }
  if (name === 'list_spc_charts') {
    const rows = knowledgeBase.spcCharts || []
    return { text: JSON.stringify(rows, null, 2), html: table([{ key: 'title', label: '图表' }, { key: 'chartType', label: '类型' }, { key: 'apiFile', label: 'API 文件' }, { key: 'endpoint', label: '后端接口' }], rows), data: rows }
  }
  if (name === 'spc_query_chart') {
    const result = await querySpcChart(args || {})
    return { text: JSON.stringify(result.data, null, 2), html: result.html, data: result }
  }
  if (name === 'reload_project_index') {
    knowledgeBase = readJson(KNOWLEDGE_FILE, emptyKnowledgeBase())
    return { text: 'reloaded', html: table([{ key: 'name', label: '项目' }, { key: 'value', label: '值' }], [{ name: '状态', value: '知识库已重新加载' }]), data: { ok: true } }
  }
  if (name === 'call_mes_backend') {
    if (!ALLOW_BACKEND_CALLS) {
      return { text: 'disabled', html: table([{ key: 'name', label: '项目' }, { key: 'value', label: '值' }], [{ name: '状态', value: '已禁用，避免 AI 直接修改业务数据' }]), data: { disabled: true } }
    }
    const response = await postJson(MES_BACKEND_URL, args.payload || {})
    return { text: JSON.stringify(response, null, 2), html: htmlForRows('MES 后端返回', Array.isArray(response) ? response : [response], 30), data: response }
  }
  throw new Error('Unknown tool: ' + name)
}

async function handleMcp(body) {
  const id = body.id == null ? null : body.id
  try {
    if (body.method === 'initialize') {
      return { jsonrpc: '2.0', id, result: { protocolVersion: '2025-06-18', serverInfo: { name: 'wc-spc-mcp', version: '1.0.0' }, capabilities: { tools: {} } } }
    }
    if (body.method === 'tools/list') return { jsonrpc: '2.0', id, result: { tools } }
    if (body.method === 'tools/call') {
      const result = await toolResult(body.params && body.params.name, (body.params && body.params.arguments) || {})
      return { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: result.text }, { type: 'text', text: result.html }], structuredContent: result.data } }
    }
    return { jsonrpc: '2.0', id, error: { code: -32601, message: 'Method not found' } }
  } catch (error) {
    return { jsonrpc: '2.0', id, error: { code: -32000, message: error.message } }
  }
}

function parseBody(req) {
  return new Promise((resolve, reject) => {
    const chunks = []
    req.on('data', chunk => { chunks.push(Buffer.from(chunk)) })
    req.on('end', () => {
      try {
        const text = Buffer.concat(chunks).toString('utf8')
        resolve(text ? JSON.parse(text) : {})
      } catch (error) {
        reject(error)
      }
    })
    req.on('error', reject)
  })
}

function send(res, status, payload) {
  const body = typeof payload === 'string' ? payload : JSON.stringify(payload)
  res.writeHead(status, {
    'Content-Type': typeof payload === 'string' ? 'text/html; charset=utf-8' : 'application/json; charset=utf-8',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    'Access-Control-Allow-Methods': 'GET, POST, OPTIONS'
  })
  res.end(body)
}

const server = http.createServer(async (req, res) => {
  if (req.method === 'OPTIONS') return send(res, 204, '')
  try {
    if (req.method === 'GET' && req.url === '/health') {
      return send(res, 200, {
        ok: true,
        model: DEEPSEEK_MODEL,
        apiKeyConfigured: Boolean(DEEPSEEK_API_KEY),
        knowledgeBase: { loaded: Boolean(knowledgeBase.generatedAt), modules: knowledgeBase.modules.length, qualityModules: knowledgeBase.qualityModules.length, spcCharts: knowledgeBase.spcCharts.length },
        endpoints: { mes: MES_BACKEND_URL, spc: SPC_BACKEND_URL }
      })
    }
    if (req.method === 'GET' && req.url === '/api/tools') return send(res, 200, { tools })
    if (req.method === 'POST' && req.url === '/mcp') return send(res, 200, await handleMcp(await parseBody(req)))
    if (req.method === 'POST' && req.url === '/api/assistant/chat') {
      const body = await parseBody(req)
      const question = String(body.question || '').trim()
      if (!question) return send(res, 400, { error: 'question is required' })
      const navigation = navigationResponse(question)
      if (navigation) return send(res, 200, navigation)
      if (!isFeatureQuestion(question) && isQualityChartQuestion(question)) {
        const profile = QUALITY_DATA_QUERY_PROFILE
        const queryResult = await queryQualityData({ query: question, nameOrSql: profile && profile.nameOrSql, limit: 50, pageSize: 50 })
        const rows = queryResult.data && Array.isArray(queryResult.data.rows) ? queryResult.data.rows : []
        const charts = qualityDataCharts(rows, question)
        let html = queryResult.html
        if (charts.some(chart => chart && chart.mixedMeasurePosition)) {
          html = '<h4>图表提示</h4>' + table([{ key: 'name', label: '项目' }, { key: 'value', label: '内容' }], [
            { name: '提示', value: '当前结果包含多个测量位置，图表可能混合多个测点；建议指定单一测量位置。' }
          ]) + html
        }
        if (!charts.length) {
          html += '<h4>SPC图表</h4>' + table([{ key: 'name', label: '项目' }, { key: 'value', label: '内容' }], [
            { name: '状态', value: '未绘制' },
            { name: '原因', value: '质量数据查询结果中没有可识别的数值型测量值。' }
          ])
        }
        return send(res, 200, Object.assign({}, queryResult, {
          html,
          charts
        }))
      }
      if (/图|趋势|直方|正态|过程能力|排列|X-R|XR|X-S|XS/i.test(question)) {
        const chartResult = await querySpcChart({ query: question })
        const html = await askDeepSeek(question, chartResult.html, {
          tool: 'spc_query_chart',
          data: chartResult.data || chartResult
        }, { useProjectContext: false })
        return send(res, 200, Object.assign({}, chartResult, { html }))
      }
      if (!isFeatureQuestion(question) && isQualityDataQuestion(question)) {
        const profile = QUALITY_DATA_QUERY_PROFILE
        const queryResult = await queryQualityData({ query: question, nameOrSql: profile && profile.nameOrSql, limit: 50, pageSize: 50 })
        const rows = queryResult.data && Array.isArray(queryResult.data.rows) ? queryResult.data.rows : []
        if (rows.length > 20) return send(res, 200, queryResult)
        const html = await askDeepSeek(question, queryResult.html, {
          tool: 'query_quality_data',
          query: queryResult.data && queryResult.data.query,
          payload: queryResult.data && queryResult.data.payload,
          rows,
          output: queryResult.data && queryResult.data.output
        }, { useProjectContext: false })
        return send(res, 200, Object.assign({}, queryResult, { html }))
      }
      const html = await askDeepSeek(question, '', {}, { useProjectContext: false })
      return send(res, 200, { html })
    }
    return send(res, 404, { error: 'Not found' })
  } catch (error) {
    return send(res, 500, { error: error.message })
  }
})

server.listen(PORT, () => {
  console.log('WC-SPC AI MCP server listening on http://127.0.0.1:' + PORT)
  console.log('DeepSeek model: ' + DEEPSEEK_MODEL + ', API key configured: ' + Boolean(DEEPSEEK_API_KEY))
  console.log('Knowledge base loaded: ' + Boolean(knowledgeBase.generatedAt))
})
