import { Router } from 'express'
import { query } from '../config/database.js'
import { sendSuccess, sendError, parsePagination, parseMonth } from '../middleware/error.js'
import type { AuthRequest } from '../middleware/auth.js'

const router = Router()

// ============ Tab1: 成本总览 ============

// 成本总览指标
router.get('/overview', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        count(*) AS total_dishes,
        count(*) FILTER (WHERE theoretical_margin_rate_pct < 50) AS low_margin_dishes,
        count(*) FILTER (WHERE cost_variance_amount > 0) AS over_cost_dishes,
        round((1 - sum(theoretical_cost) / nullif(sum(sales_amount), 0)) * 100, 2) AS avg_theo_margin,
        round((1 - sum(actual_cost) / nullif(sum(sales_amount), 0)) * 100, 2) AS avg_actual_margin,
        round(sum(sales_amount)::numeric, 2) AS total_sales,
        round(sum(theoretical_cost)::numeric, 2) AS total_theo_cost,
        round(sum(actual_cost)::numeric, 2) AS total_actual_cost,
        round(sum(cost_variance_amount)::numeric, 2) AS total_variance
      FROM public.dish_cost_analysis_summary
      WHERE import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
    `, [month])
    sendSuccess(res, result.rows[0])
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 品类成本对比
router.get('/category-comparison', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT category_level1,
        count(*) AS dishes,
        round((1 - sum(theoretical_cost) / nullif(sum(sales_amount), 0)) * 100, 2) AS avg_theo_margin,
        round((1 - sum(actual_cost) / nullif(sum(sales_amount), 0)) * 100, 2) AS avg_actual_margin,
        round(sum(sales_amount)::numeric, 2) AS total_sales,
        round(sum(theoretical_cost)::numeric, 2) AS total_theo_cost,
        round(sum(actual_cost)::numeric, 2) AS total_actual_cost,
        round(sum(cost_variance_amount)::numeric, 2) AS total_variance
      FROM public.dish_cost_analysis_summary
      WHERE category_level1 IS NOT NULL
        AND import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY category_level1 ORDER BY total_sales DESC
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 毛利率偏差分布
router.get('/margin-deviation', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        CASE
          WHEN (actual_margin_rate_pct - theoretical_margin_rate_pct) > 10 THEN '实际高于理论>10%'
          WHEN (actual_margin_rate_pct - theoretical_margin_rate_pct) BETWEEN 0 AND 10 THEN '实际略高0-10%'
          WHEN (actual_margin_rate_pct - theoretical_margin_rate_pct) BETWEEN -10 AND 0 THEN '实际略低0-10%'
          WHEN (actual_margin_rate_pct - theoretical_margin_rate_pct) BETWEEN -30 AND -10 THEN '实际偏低10-30%'
          WHEN (actual_margin_rate_pct - theoretical_margin_rate_pct) < -30 THEN '实际严重偏低>30%'
        END AS deviation_band,
        count(*) AS cnt
      FROM public.dish_cost_analysis_summary
      WHERE theoretical_margin_rate_pct IS NOT NULL AND actual_margin_rate_pct IS NOT NULL
        AND import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY deviation_band ORDER BY cnt DESC
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 成本差异TOP榜
router.get('/variance-top', async (req: AuthRequest, res) => {
  try {
    const limit = parseInt((req.query.limit as string) || '50')
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const month = parseMonth(req)
    const result = await query(`
      SELECT dish_name, dish_code, category_level1,
        round(theoretical_margin_rate_pct::numeric, 2) AS theo_margin,
        round(actual_margin_rate_pct::numeric, 2) AS actual_margin,
        round(cost_variance_amount::numeric, 2) AS cost_variance,
        round(sales_amount::numeric, 2) AS sales_amount,
        CASE
          WHEN actual_margin_rate_pct < 0 THEN '数据异常'
          WHEN cost_variance_amount > 0 AND theoretical_cost > 0 AND (cost_variance_amount / theoretical_cost) > 0.2 THEN '紧急'
          WHEN cost_variance_amount > 0 AND theoretical_cost > 0 AND (cost_variance_amount / theoretical_cost) > 0.1 THEN '整改'
          WHEN cost_variance_amount > 0 THEN '关注'
          ELSE '正常'
        END AS cost_tier
      FROM public.dish_cost_analysis_summary
      WHERE import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $2::date ORDER BY import_id DESC LIMIT 1)
      ORDER BY cost_variance_amount ${order} LIMIT $1
    `, [limit, month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab2: 菜品盈利分析 ============

// 菜单工程矩阵
router.get('/menu-engineering', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      WITH imp AS (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      SELECT dish_name, dish_code, category_level1,
        round(sales_quantity::numeric, 2) AS sales_quantity,
        round(sales_amount::numeric, 2) AS sales_amount,
        round(actual_margin_rate_pct::numeric, 2) AS actual_margin,
        CASE
          WHEN actual_margin_rate_pct < 0 THEN '数据异常'
          WHEN sales_quantity >= (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY sales_quantity) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id)
            AND actual_margin_rate_pct >= (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY actual_margin_rate_pct) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id AND actual_margin_rate_pct > 0) THEN '明星盈利品'
          WHEN sales_quantity >= (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY sales_quantity) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id)
            AND actual_margin_rate_pct < (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY actual_margin_rate_pct) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id AND actual_margin_rate_pct > 0) THEN '高销低利品'
          WHEN sales_quantity < (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY sales_quantity) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id)
            AND actual_margin_rate_pct >= (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY actual_margin_rate_pct) FROM public.dish_cost_analysis_summary s, imp WHERE s.import_id = imp.import_id AND actual_margin_rate_pct > 0) THEN '低销高利品'
          ELSE '低销低利品'
        END AS menu_type
      FROM public.dish_cost_analysis_summary s, imp
      WHERE s.import_id = imp.import_id AND actual_margin_rate_pct IS NOT NULL
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 菜品盈利明细
router.get('/profitability', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const category = req.query.category as string
    const menuType = req.query.type as string

    const sort = (req.query.sort as string) || 'sales_amount'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const filter = (req.query.filter as string) || ''

    const month = parseMonth(req)
    let where = 'WHERE actual_margin_rate_pct IS NOT NULL AND import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)'
    const params: any[] = [month]
    if (category) { params.push(category); where += ` AND category_level1 = $${params.length}` }
    if (filter === 'profitable') { where += ` AND actual_margin_rate_pct > 0` }
    else if (filter === 'loss') { where += ` AND actual_margin_rate_pct <= 0` }
    else if (filter === 'high_variance') { where += ` AND cost_variance_amount > 0` }

    const validSorts = ['sales_amount', 'sales_quantity', 'theoretical_margin_rate_pct', 'actual_margin_rate_pct', 'cost_variance_amount', 'theoretical_cost', 'actual_cost', 'price']
    const sortCol = validSorts.includes(sort) ? sort : 'sales_amount'

    const countResult = await query(`SELECT count(*) FROM public.dish_cost_analysis_summary ${where}`, params)
    const total = countResult.rows[0].count

    params.push(pageSize, offset)
    const result = await query(`
      SELECT dish_name, dish_code, category_level1,
        round(sales_quantity::numeric, 2) AS sales_quantity,
        round(sales_amount::numeric, 2) AS sales_amount,
        round((sales_amount - theoretical_cost)::numeric, 2) AS theo_profit,
        round((sales_amount - actual_cost)::numeric, 2) AS actual_profit,
        round(theoretical_margin_rate_pct::numeric, 2) AS theo_margin,
        round(actual_margin_rate_pct::numeric, 2) AS actual_margin,
        round(sales_amount / nullif(sum(sales_amount) OVER (), 0) * 100, 2) AS revenue_contribution
      FROM public.dish_cost_analysis_summary ${where}
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $${params.length - 1} OFFSET $${params.length}
    `, params)
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 定价合理性
router.get('/pricing', async (req: AuthRequest, res) => {
  try {
    const threshold = parseFloat((req.query.threshold as string) || '50')
    const month = parseMonth(req)
    const result = await query(`
      SELECT dish_name, dish_code, category_level1,
        round(price::numeric, 2) AS price,
        round(theoretical_cost::numeric, 2) AS theo_cost,
        round((theoretical_cost / nullif(price, 0) * 100)::numeric, 2) AS theo_cost_rate,
        round(theoretical_margin_rate_pct::numeric, 2) AS theo_margin,
        round(actual_margin_rate_pct::numeric, 2) AS actual_margin,
        round(sales_amount::numeric, 2) AS sales_amount
      FROM public.dish_cost_analysis_summary
      WHERE theoretical_margin_rate_pct < $1 AND sales_amount > 1000
        AND import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $2::date ORDER BY import_id DESC LIMIT 1)
      ORDER BY theoretical_margin_rate_pct ASC
    `, [threshold, month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab3: 原料差异分析 ============

// 菜品原料差异分解
router.get('/material-variance', async (req: AuthRequest, res) => {
  try {
    const dishCode = req.query.dish_code as string
    if (!dishCode) return sendError(res, 'dish_code is required')
    const month = parseMonth(req)

    const result = await query(`
      SELECT m.material_name, m.material_unit,
        round(m.theoretical_quantity::numeric, 4) AS theo_qty,
        round(m.actual_quantity::numeric, 4) AS actual_qty,
        round(m.loss_quantity::numeric, 4) AS loss_qty,
        round(m.loss_quantity_rate_pct::numeric, 2) AS loss_rate,
        round(m.theoretical_amount::numeric, 2) AS theo_amount,
        round(m.actual_amount::numeric, 2) AS actual_amount,
        round(m.loss_amount::numeric, 2) AS loss_amount,
        CASE
          WHEN m.loss_quantity_rate_pct < -100 THEN '分摊遗漏'
          WHEN m.loss_quantity_rate_pct < -30 THEN '份量超标'
          WHEN m.actual_quantity = 0 THEN '未使用'
          ELSE '正常损耗'
        END AS reason
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE s.dish_code = $1
        AND s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $2::date ORDER BY import_id DESC LIMIT 1)
      ORDER BY m.loss_amount ASC
    `, [dishCode, month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 损耗率分布
router.get('/loss-distribution', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        CASE
          WHEN loss_quantity_rate_pct = 0 THEN '无损耗'
          WHEN loss_quantity_rate_pct BETWEEN -1 AND 0 THEN '轻微(-1~0%)'
          WHEN loss_quantity_rate_pct BETWEEN -10 AND -1 THEN '轻度(-1~-10%)'
          WHEN loss_quantity_rate_pct BETWEEN -30 AND -10 THEN '中度(-10~-30%)'
          WHEN loss_quantity_rate_pct BETWEEN -100 AND -30 THEN '严重(-30~-100%)'
          WHEN loss_quantity_rate_pct < -100 THEN '极重(<-100%)'
          ELSE '正向(>0%)'
        END AS loss_band,
        count(*) AS cnt
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY loss_band ORDER BY cnt DESC
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 超耗物料TOP榜
router.get('/material-loss-top', async (req: AuthRequest, res) => {
  try {
    const limit = parseInt((req.query.limit as string) || '50')
    const order = (req.query.order as string) === 'rate' ? 'avg_loss_rate' : 'total_loss_qty'
    const month = parseMonth(req)
    const result = await query(`
      SELECT material_name, material_type,
        count(DISTINCT m.summary_id) AS dish_count,
        round(sum(loss_quantity)::numeric, 2) AS total_loss_qty,
        round((sum(loss_quantity) / nullif(sum(theoretical_quantity), 0) * 100)::numeric, 2) AS avg_loss_rate,
        round(max(loss_quantity_rate_pct)::numeric, 2) AS max_loss_rate,
        round(sum(loss_amount)::numeric, 2) AS total_loss_amount
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE m.loss_quantity < 0
        AND s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $2::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY material_name, material_type
      ORDER BY ${order} ASC LIMIT $1
    `, [limit, month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 物料类型损耗对比
router.get('/material-type-loss', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT material_type,
        count(*) AS cnt,
        round((sum(loss_quantity) / nullif(sum(theoretical_quantity), 0) * 100)::numeric, 2) AS avg_loss_rate,
        round(sum(loss_amount)::numeric, 2) AS total_loss_amount,
        count(DISTINCT material_name) AS unique_materials
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY material_type ORDER BY total_loss_amount ASC
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab4: BOM与配方 ============

// BOM总览
router.get('/bom-overview', async (req: AuthRequest, res) => {
  try {
    const result = await query(`
      SELECT
        (SELECT count(*) FROM analytics.dim_sku) AS total_sku,
        (SELECT count(DISTINCT sku_code) FROM analytics.fact_recipe_bom) AS sku_with_bom,
        (SELECT count(*) FROM analytics.dim_sku) - (SELECT count(DISTINCT sku_code) FROM analytics.fact_recipe_bom) AS sku_without_bom,
        round((SELECT count(DISTINCT sku_code)::numeric FROM analytics.fact_recipe_bom) / nullif((SELECT count(*) FROM analytics.dim_sku), 0) * 100, 2) AS coverage_pct,
        (SELECT count(*) FROM analytics.fact_recipe_bom) AS total_bom_rows,
        (SELECT count(*) FROM analytics.fact_recipe_bom WHERE waste_rate > 20) AS high_loss_bom,
        (SELECT count(*) FROM analytics.fact_recipe_bom WHERE waste_rate > 100) AS extreme_loss_bom,
        (SELECT round(avg(cnt)::numeric, 1) FROM (SELECT count(*) AS cnt FROM analytics.fact_recipe_bom GROUP BY sku_code) t) AS avg_materials_per_sku
    `)
    sendSuccess(res, result.rows[0])
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// BOM复杂度
router.get('/bom-complexity', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'material_count'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const filter = (req.query.filter as string) || ''

    let having = ''
    if (filter === 'high_loss') { having = 'HAVING count(*) FILTER (WHERE b.waste_rate > 20) > 0' }
    else if (filter === 'unique_heavy') { having = 'HAVING count(*) FILTER (WHERE ref_count = 1) > 3' }

    const validSorts = ['material_count', 'semi_finished_count', 'unique_material_count', 'high_loss_count', 'zero_actual_count']
    const sortCol = validSorts.includes(sort) ? sort : 'material_count'

    const countResult = await query(`SELECT count(DISTINCT sku_code) FROM analytics.fact_recipe_bom`)
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT b.sku_code, s.standard_name AS dish_name, s.category_l1,
        count(*) AS material_count,
        count(*) FILTER (WHERE dm.major_category = '半成品') AS semi_finished_count,
        count(*) FILTER (WHERE ref_count = 1) AS unique_material_count,
        count(DISTINCT b.unit) AS unit_count,
        count(*) FILTER (WHERE b.waste_rate > 20) AS high_loss_count,
        count(*) FILTER (WHERE b.standard_net_quantity = 0) AS zero_actual_count,
        CASE
          WHEN count(*) <= 5 THEN '低复杂度'
          WHEN count(*) <= 10 THEN '正常'
          WHEN count(*) <= 15 THEN '较复杂'
          ELSE '重点评审'
        END AS complexity_level
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_sku s ON s.sku_code = b.sku_code
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      LEFT JOIN (SELECT material_code, count(DISTINCT sku_code) AS ref_count FROM analytics.fact_recipe_bom GROUP BY material_code) r ON r.material_code = b.material_code
      GROUP BY b.sku_code, s.standard_name, s.category_l1
      ${having}
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $1 OFFSET $2
    `, [pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 高损耗BOM预警
router.get('/bom-high-loss', async (req: AuthRequest, res) => {
  try {
    const threshold = parseFloat((req.query.threshold as string) || '20')
    const result = await query(`
      SELECT b.sku_code, s.standard_name AS dish_name, dm.material_name,
        round(b.waste_rate::numeric, 2) AS waste_rate,
        round(b.yield_rate::numeric, 2) AS yield_rate,
        round(b.standard_gross_quantity::numeric, 8) AS gross_qty,
        round(b.standard_net_quantity::numeric, 8) AS net_qty,
        b.unit,
        CASE
          WHEN b.waste_rate > 100 THEN '极端'
          WHEN b.waste_rate > 50 THEN '严重'
          ELSE '关注'
        END AS alert_level
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_sku s ON s.sku_code = b.sku_code
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      WHERE b.waste_rate >= $1
      ORDER BY b.waste_rate DESC LIMIT 100
    `, [threshold])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 缺BOM的SKU清单
router.get('/bom-missing', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'sales_amount'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'

    const validSorts = ['sales_amount', 'sales_quantity', 'standard_name', 'category_l1']
    const sortCol = validSorts.includes(sort) ? sort : 'sales_amount'

    const countResult = await query(`
      SELECT count(*) FROM analytics.dim_sku s
      WHERE s.sku_code NOT IN (SELECT DISTINCT sku_code FROM analytics.fact_recipe_bom)
    `)
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT s.sku_code, s.standard_name AS dish_name, s.category_l1,
        COALESCE(round(d.sales_amount::numeric, 2), 0) AS sales_amount,
        COALESCE(round(d.sales_quantity::numeric, 2), 0) AS sales_quantity
      FROM analytics.dim_sku s
      LEFT JOIN public.dish_cost_analysis_summary d ON d.dish_code = s.sku_code
      WHERE s.sku_code NOT IN (SELECT DISTINCT sku_code FROM analytics.fact_recipe_bom)
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $1 OFFSET $2
    `, [pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 菜品物料构成
router.get('/bom-composition', async (req: AuthRequest, res) => {
  try {
    const skuCode = req.query.sku_code as string
    if (!skuCode) return sendError(res, 'sku_code is required')

    const result = await query(`
      SELECT dm.material_name, dm.major_category AS material_type,
        round(b.standard_gross_quantity::numeric, 8) AS gross_qty,
        round(b.standard_net_quantity::numeric, 8) AS net_qty,
        b.unit,
        round(b.waste_rate::numeric, 2) AS waste_rate,
        round(b.yield_rate::numeric, 2) AS yield_rate,
        round(m.theoretical_amount::numeric, 2) AS theo_amount,
        round(m.theoretical_amount / nullif(sum(m.theoretical_amount) OVER (PARTITION BY m.summary_id), 0) * 100, 2) AS cost_share
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      LEFT JOIN public.dish_cost_analysis_material_detail m ON m.material_name = dm.material_name
      WHERE b.sku_code = $1
      ORDER BY theo_amount DESC NULLS LAST
    `, [skuCode])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab5: 供应链与精简 ============

// 原料共用度
router.get('/material-sharing', async (req: AuthRequest, res) => {
  try {
    const limit = parseInt((req.query.limit as string) || '20')
    const result = await query(`
      SELECT dm.material_name, dm.major_category,
        count(DISTINCT b.sku_code) AS sku_count,
        CASE
          WHEN count(DISTINCT b.sku_code) > 50 THEN '核心原料'
          WHEN count(DISTINCT b.sku_code) = 1 THEN '独有原料'
          ELSE '普通共用'
        END AS sharing_type
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      GROUP BY dm.material_name, dm.major_category
      ORDER BY sku_count DESC LIMIT $1
    `, [limit])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 独有原料风险
router.get('/unique-material-risk', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'sales_amount'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const filter = (req.query.filter as string) || ''

    let having = ''
    if (filter === 'high_risk') { having = 'HAVING COALESCE(d.sales_amount, 0) < 5000 AND count(*) > 3' }
    else if (filter === 'low_sales') { having = 'HAVING COALESCE(d.sales_amount, 0) < 5000' }

    const validSorts = ['sales_amount', 'unique_material_count', 'standard_name', 'category_l1']
    const sortCol = validSorts.includes(sort) ? sort : 'sales_amount'

    const countResult = await query(`
      SELECT count(DISTINCT b.sku_code) FROM analytics.fact_recipe_bom b
      WHERE b.material_code IN (SELECT material_code FROM analytics.fact_recipe_bom GROUP BY material_code HAVING count(DISTINCT sku_code) = 1)
    `)
    const total = countResult.rows[0].count

    const result = await query(`
      WITH unique_materials AS (
        SELECT material_code FROM analytics.fact_recipe_bom GROUP BY material_code HAVING count(DISTINCT sku_code) = 1
      )
      SELECT b.sku_code, s.standard_name AS dish_name, s.category_l1,
        COALESCE(round(d.sales_amount::numeric, 2), 0) AS sales_amount,
        count(*) AS unique_material_count,
        CASE
          WHEN COALESCE(d.sales_amount, 0) < 5000 AND count(*) > 3 THEN '高'
          WHEN COALESCE(d.sales_amount, 0) < 5000 THEN '中'
          ELSE '低'
        END AS risk_level
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_sku s ON s.sku_code = b.sku_code
      LEFT JOIN public.dish_cost_analysis_summary d ON d.dish_code = b.sku_code
      WHERE b.material_code IN (SELECT material_code FROM unique_materials)
      GROUP BY b.sku_code, s.standard_name, s.category_l1, d.sales_amount
      ${having}
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $1 OFFSET $2
    `, [pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// SKU精简模拟
router.post('/sku-simplify-simulate', async (req: AuthRequest, res) => {
  try {
    const { sku_codes } = req.body
    if (!sku_codes || !Array.isArray(sku_codes) || sku_codes.length === 0) {
      return sendError(res, 'sku_codes array is required')
    }

    const result = await query(`
      WITH target_skus AS (SELECT unnest($1::text[]) AS sku_code)
      SELECT
        (SELECT round(sum(sales_amount)::numeric, 2) FROM public.dish_cost_analysis_summary WHERE dish_code IN (SELECT sku_code FROM target_skus)) AS revenue_impact,
        (SELECT round(sum(sales_amount - actual_cost)::numeric, 2) FROM public.dish_cost_analysis_summary WHERE dish_code IN (SELECT sku_code FROM target_skus)) AS profit_impact,
        (SELECT count(DISTINCT material_code) FROM analytics.fact_recipe_bom WHERE sku_code IN (SELECT sku_code FROM target_skus) AND material_code NOT IN (SELECT material_code FROM analytics.fact_recipe_bom WHERE sku_code NOT IN (SELECT sku_code FROM target_skus))) AS releasable_materials,
        (SELECT count(DISTINCT material_code) FROM analytics.fact_recipe_bom WHERE sku_code IN (SELECT sku_code FROM target_skus) AND material_code IN (SELECT material_code FROM analytics.fact_recipe_bom WHERE sku_code NOT IN (SELECT sku_code FROM target_skus))) AS shared_materials,
        (SELECT count(*) FROM public.dish_cost_analysis_summary WHERE dish_code IN (SELECT sku_code FROM target_skus) AND actual_margin_rate_pct < 0) AS negative_margin_count
    `, [sku_codes])

    const releasable = await query(`
      WITH target_skus AS (SELECT unnest($1::text[]) AS sku_code)
      SELECT dm.material_name, dm.major_category,
        round(sum(m.actual_amount)::numeric, 2) AS inventory_value
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      LEFT JOIN public.dish_cost_analysis_material_detail m ON m.material_name = dm.material_name
      WHERE b.sku_code IN (SELECT sku_code FROM target_skus)
        AND b.material_code NOT IN (SELECT material_code FROM analytics.fact_recipe_bom WHERE sku_code NOT IN (SELECT sku_code FROM target_skus))
      GROUP BY dm.material_name, dm.major_category
      ORDER BY inventory_value DESC NULLS LAST LIMIT 50
    `, [sku_codes])

    sendSuccess(res, { summary: result.rows[0], releasable_materials: releasable.rows })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// BOM驱动物料需求预测
router.get('/material-demand', async (req: AuthRequest, res) => {
  try {
    const limit = parseInt((req.query.limit as string) || '50')
    const result = await query(`
      SELECT dm.material_name, dm.major_category, dm.base_unit,
        round(sum(b.standard_gross_quantity * dcs.sales_quantity)::numeric, 2) AS estimated_demand,
        count(DISTINCT b.sku_code) AS dish_count
      FROM analytics.fact_recipe_bom b
      JOIN analytics.dim_material dm ON dm.material_code = b.material_code
      JOIN public.dish_cost_analysis_summary dcs ON dcs.dish_code = b.sku_code
      GROUP BY dm.material_name, dm.major_category, dm.base_unit
      ORDER BY estimated_demand DESC LIMIT $1
    `, [limit])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab6: 包装耗材 ============

// 包装总览
router.get('/packaging-overview', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        count(DISTINCT material_name) AS packaging_types,
        round(sum(theoretical_amount)::numeric, 2) AS total_cost,
        round(sum(loss_quantity)::numeric, 2) AS total_loss_qty,
        count(*) FILTER (WHERE loss_quantity_rate_pct < -20) AS high_loss_count
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE material_name ~* '餐盒|餐具|打包|纸巾|餐盒|调味包|餐盒|碗|袋|杯|盒'
        AND s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
    `, [month])
    sendSuccess(res, result.rows[0])
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 包装明细
router.get('/packaging-detail', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'total_cost'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const filter = (req.query.filter as string) || ''

    let having = ''
    if (filter === 'high_loss') { having = 'HAVING sum(loss_quantity) / nullif(sum(theoretical_quantity), 0) * 100 < -20' }
    else if (filter === 'normal') { having = 'HAVING sum(loss_quantity) / nullif(sum(theoretical_quantity), 0) * 100 >= -5' }

    const validSorts = ['total_cost', 'total_qty', 'loss_qty', 'avg_loss_rate', 'dish_count', 'material_name']
    const sortCol = validSorts.includes(sort) ? sort : 'total_cost'

    const month = parseMonth(req)
    const countResult = await query(`
      SELECT count(DISTINCT material_name) FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE material_name ~* '餐盒|餐具|打包|纸巾|碗|袋|杯|盒'
        AND s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
    `, [month])
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT material_name,
        count(DISTINCT m.summary_id) AS dish_count,
        round(sum(m.theoretical_quantity)::numeric, 2) AS total_qty,
        round(sum(m.theoretical_amount)::numeric, 2) AS total_cost,
        round(sum(m.loss_quantity)::numeric, 2) AS loss_qty,
        round((sum(m.loss_quantity) / nullif(sum(m.theoretical_quantity), 0) * 100)::numeric, 2) AS avg_loss_rate,
        CASE
          WHEN sum(m.loss_quantity) / nullif(sum(m.theoretical_quantity), 0) * 100 < -20 THEN '核查'
          WHEN sum(m.loss_quantity) / nullif(sum(m.theoretical_quantity), 0) * 100 < -5 THEN '关注'
          ELSE '正常'
        END AS status
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE material_name ~* '餐盒|餐具|打包|纸巾|碗|袋|杯|盒'
        AND s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
      GROUP BY material_name
      ${having}
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $2 OFFSET $3
    `, [month, pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab7: 数据质量 ============

// 数据质量总览
router.get('/data-quality', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        (SELECT count(*) FROM analytics.dim_sku) AS total_sku,
        (SELECT count(DISTINCT sku_code) FROM analytics.fact_recipe_bom) AS sku_with_bom,
        round((SELECT count(DISTINCT sku_code)::numeric FROM analytics.fact_recipe_bom) / nullif((SELECT count(*) FROM analytics.dim_sku), 0) * 100, 2) AS bom_coverage,
        (SELECT count(*) FROM public.dish_cost_analysis_summary s WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND EXISTS (SELECT 1 FROM public.dish_sales_details d WHERE d.dish_name = s.dish_name)) AS matched_dishes,
        (SELECT count(*) FROM public.dish_cost_analysis_summary WHERE import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)) AS total_dishes,
        (SELECT count(*) FROM analytics.fact_recipe_bom WHERE standard_net_quantity = 0) AS zero_actual_bom,
        (SELECT count(*) FROM public.dish_cost_analysis_summary s WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND s.actual_margin_rate_pct < 0) AS negative_margin_count,
        (SELECT count(*) FROM public.dish_cost_analysis_summary s WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND s.theoretical_margin_rate_pct < 0) AS negative_theo_margin_count,
        (SELECT count(*) FROM public.dish_cost_analysis_material_detail m JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND EXISTS (SELECT 1 FROM public.inventory_cost_records i WHERE i.material_name = m.material_name)) AS matched_materials,
        (SELECT count(*) FROM public.dish_cost_analysis_material_detail m JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)) AS total_materials
    `, [month])
    sendSuccess(res, result.rows[0])
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 未匹配菜品
router.get('/unmatched-dishes', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'sales_amount'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'

    const validSorts = ['sales_amount', 'dish_name', 'dish_code', 'category_level1']
    const sortCol = validSorts.includes(sort) ? sort : 'sales_amount'

    const month = parseMonth(req)
    const countResult = await query(`SELECT count(*) FROM public.dish_cost_analysis_summary s WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND NOT EXISTS (SELECT 1 FROM public.dish_sales_details d WHERE d.dish_name = s.dish_name)`, [month])
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT dish_name, dish_code, category_level1,
        round(sales_amount::numeric, 2) AS sales_amount
      FROM public.dish_cost_analysis_summary s
      WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1) AND NOT EXISTS (SELECT 1 FROM public.dish_sales_details d WHERE d.dish_name = s.dish_name)
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $2 OFFSET $3
    `, [month, pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 未匹配物料
router.get('/unmatched-materials', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'dish_count'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'

    const validSorts = ['dish_count', 'loss_amount', 'material_name', 'material_type']
    const sortCol = validSorts.includes(sort) ? sort : 'dish_count'

    const month = parseMonth(req)
    const countResult = await query(`
      SELECT count(*) FROM (
        SELECT material_name, material_type
        FROM public.dish_cost_analysis_material_detail m
        JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
        WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
          AND NOT EXISTS (SELECT 1 FROM public.inventory_cost_records i WHERE i.material_name = m.material_name)
        GROUP BY material_name, material_type
      ) t
    `, [month])
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT material_name, material_type,
        count(DISTINCT m.summary_id) AS dish_count,
        round(sum(loss_amount)::numeric, 2) AS loss_amount
      FROM public.dish_cost_analysis_material_detail m
      JOIN public.dish_cost_analysis_summary s ON s.summary_id = m.summary_id
      WHERE s.import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
        AND NOT EXISTS (SELECT 1 FROM public.inventory_cost_records i WHERE i.material_name = m.material_name)
      GROUP BY material_name, material_type
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $2 OFFSET $3
    `, [month, pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab8: 门店成本 ============

// 门店成本总览
router.get('/store-overview', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT
        count(*) AS total_stores,
        count(*) FILTER (WHERE variance_level = '红色-严重超耗') AS red_count,
        count(*) FILTER (WHERE variance_level = '橙色-明显超耗') AS orange_count,
        count(*) FILTER (WHERE variance_level = '绿色-基本正常') AS green_count,
        count(*) FILTER (WHERE variance_level = '灰色-口径异常') AS gray_count,
        round(sum(food_cost_variance)::numeric, 2) AS total_variance
      FROM analytics.mv_store_theoretical_actual_cost_monthly
      WHERE month_start = $1
    `, [month])
    sendSuccess(res, result.rows[0])
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 门店地图数据
router.get('/store-map', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT c.store_code, c.store_name, c.variance_level,
        round(c.food_cost_variance::numeric, 2) AS variance,
        round(c.theoretical_cost_rate_pct::numeric, 2) AS theo_cost_rate,
        round(c.actual_food_cost_rate_pct::numeric, 2) AS actual_cost_rate,
        l.latitude_gcj02, l.longitude_gcj02
      FROM analytics.mv_store_theoretical_actual_cost_monthly c
      LEFT JOIN analytics.v_store_location_operating l ON l.store_code = c.store_code
      WHERE c.month_start = $1 AND l.latitude_gcj02 IS NOT NULL
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 门店超耗排名
router.get('/store-ranking', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const sort = (req.query.sort as string) || 'food_cost_variance'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'
    const filter = (req.query.filter as string) || ''

    let where = ''
    if (filter === 'red') { where = "WHERE variance_level = '红色-严重超耗'" }
    else if (filter === 'orange') { where = "WHERE variance_level = '橙色-明显超耗'" }
    else if (filter === 'green') { where = "WHERE variance_level = '绿色-基本正常'" }
    else if (filter === 'gray') { where = "WHERE variance_level = '灰色-口径异常'" }

    const validSorts = ['food_cost_variance', 'theoretical_cost_rate_pct', 'actual_food_cost_rate_pct', 'variance_to_theoretical_pct', 'negative_item_lines', 'store_name']
    const sortCol = validSorts.includes(sort) ? sort : 'food_cost_variance'

    const month = parseMonth(req)
    const countResult = await query(`SELECT count(*) FROM analytics.mv_store_theoretical_actual_cost_monthly WHERE month_start = $1 ${where}`, [month])
    const total = countResult.rows[0].count

    const result = await query(`
      SELECT store_code, store_name,
        round(theoretical_cost_rate_pct::numeric, 2) AS theo_cost_rate,
        round(actual_food_cost_rate_pct::numeric, 2) AS actual_cost_rate,
        round(variance_to_theoretical_pct::numeric, 2) AS variance_pct,
        round(food_cost_variance::numeric, 2) AS variance_amount,
        negative_item_lines,
        variance_level
      FROM analytics.mv_store_theoretical_actual_cost_monthly
      WHERE month_start = $1
      ${where}
      ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $2 OFFSET $3
    `, [month, pageSize, offset])
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab9: 可视化探索 ============

// 散点图数据
router.get('/scatter', async (req: AuthRequest, res) => {
  try {
    const month = parseMonth(req)
    const result = await query(`
      SELECT dish_name, dish_code, category_level1,
        round(sales_quantity::numeric, 2) AS x,
        round(cost_variance_amount::numeric, 2) AS y,
        round(sales_amount::numeric, 2) AS size
      FROM public.dish_cost_analysis_summary
      WHERE cost_variance_amount IS NOT NULL AND sales_quantity IS NOT NULL
        AND import_id = (SELECT import_id FROM public.dish_cost_analysis_import_log WHERE report_month = $1::date ORDER BY import_id DESC LIMIT 1)
    `, [month])
    sendSuccess(res, result.rows)
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// ============ Tab10: 调整管理 ============

// 生成诊断快照
router.post('/diagnosis/generate', async (req: AuthRequest, res) => {
  try {
    const diagDate = new Date().toISOString().slice(0, 10)

    await query(`DELETE FROM public.dish_diagnosis_snapshot WHERE diagnosis_date = $1`, [diagDate])

    const result = await query(`
      INSERT INTO public.dish_diagnosis_snapshot (diagnosis_date, dish_code, dish_name, category_l1, sales_amount, sales_quantity, theoretical_margin_pct, actual_margin_pct, cost_variance_amount, cost_tier, bom_complexity_score, unique_material_count, waste_rate_avg, diagnosis_type, diagnosis_detail, suggested_action, priority)
      SELECT
        $1::date,
        s.dish_code,
        s.dish_name,
        s.category_level1,
        round(s.sales_amount::numeric, 2),
        round(s.sales_quantity::numeric, 2),
        round(s.theoretical_margin_rate_pct::numeric, 2),
        round(s.actual_margin_rate_pct::numeric, 2),
        round(s.cost_variance_amount::numeric, 2),
        CASE
          WHEN s.actual_margin_rate_pct < 0 THEN '数据异常'
          WHEN s.cost_variance_amount > 0 AND s.theoretical_cost > 0 AND (s.cost_variance_amount / s.theoretical_cost) > 0.2 THEN '紧急'
          WHEN s.cost_variance_amount > 0 AND s.theoretical_cost > 0 AND (s.cost_variance_amount / s.theoretical_cost) > 0.1 THEN '整改'
          WHEN s.cost_variance_amount > 0 THEN '关注'
          ELSE '正常'
        END,
        COALESCE(bom.cnt, 0),
        COALESCE(bom.unique_cnt, 0),
        COALESCE(round(bom.avg_waste::numeric, 2), 0),
        CASE
          WHEN s.actual_margin_rate_pct < 0 THEN '数据异常'
          WHEN s.theoretical_margin_rate_pct < 0 THEN '负毛利'
          WHEN s.theoretical_margin_rate_pct < 30 AND s.actual_margin_rate_pct > s.theoretical_margin_rate_pct THEN '低毛利-定价偏低'
          WHEN s.theoretical_margin_rate_pct < 50 AND s.actual_margin_rate_pct < s.theoretical_margin_rate_pct THEN '低毛利-成本超耗'
          WHEN s.cost_variance_amount > 0 AND COALESCE(bom.avg_waste, 0) > 20 THEN '高超耗-份量超标'
          WHEN s.cost_variance_amount > 0 AND COALESCE(bom.avg_waste, 0) > 100 THEN '高超耗-分摊异常'
          WHEN COALESCE(bom.cnt, 0) > 15 AND s.sales_quantity < 5 THEN '配方复杂-低销量'
          WHEN COALESCE(bom.unique_cnt, 0) > 3 AND s.sales_amount < 5000 THEN '独有原料风险'
          WHEN s.cost_variance_amount > 0 THEN '成本差异'
          ELSE '正常'
        END,
        CASE
          WHEN s.actual_margin_rate_pct < 0 THEN '实际毛利率为负，需先核查BOM/单位/分摊'
          WHEN s.theoretical_margin_rate_pct < 0 THEN '理论毛利率为负，定价低于标准成本'
          WHEN s.theoretical_margin_rate_pct < 30 THEN '理论毛利率低于30%，定价偏低'
          WHEN s.theoretical_margin_rate_pct < 50 AND s.actual_margin_rate_pct < s.theoretical_margin_rate_pct THEN '实际成本超理论，存在超耗'
          WHEN s.cost_variance_amount > 0 AND COALESCE(bom.avg_waste, 0) > 20 THEN '物料损耗率超过20%'
          ELSE '成本基本正常'
        END,
        CASE
          WHEN s.actual_margin_rate_pct < 0 THEN 'fix_data'
          WHEN s.theoretical_margin_rate_pct < 0 THEN 'price_up'
          WHEN s.theoretical_margin_rate_pct < 30 AND s.actual_margin_rate_pct > s.theoretical_margin_rate_pct THEN 'price_up'
          WHEN s.theoretical_margin_rate_pct < 50 AND s.actual_margin_rate_pct < s.theoretical_margin_rate_pct THEN 'recipe_optimize'
          WHEN s.cost_variance_amount > 0 AND COALESCE(bom.avg_waste, 0) > 20 THEN 'portion_reduce'
          WHEN COALESCE(bom.cnt, 0) > 15 AND s.sales_quantity < 5 THEN 'delist'
          WHEN COALESCE(bom.unique_cnt, 0) > 3 AND s.sales_amount < 5000 THEN 'evaluate_delist'
          WHEN s.cost_variance_amount > 0 THEN 'monitor'
          ELSE 'keep'
        END,
        CASE
          WHEN s.actual_margin_rate_pct < 0 THEN 'P0'
          WHEN s.theoretical_margin_rate_pct < 0 THEN 'P0'
          WHEN s.cost_variance_amount > 0 AND s.theoretical_cost > 0 AND (s.cost_variance_amount / s.theoretical_cost) > 0.2 THEN 'P0'
          WHEN s.theoretical_margin_rate_pct < 50 THEN 'P1'
          WHEN COALESCE(bom.cnt, 0) > 15 AND s.sales_quantity < 5 THEN 'P2'
          WHEN COALESCE(bom.unique_cnt, 0) > 3 AND s.sales_amount < 5000 THEN 'P2'
          ELSE 'P3'
        END
      FROM public.dish_cost_analysis_summary s
      LEFT JOIN (
        SELECT b.sku_code,
          count(*) AS cnt,
          count(*) FILTER (WHERE r.ref_count = 1) AS unique_cnt,
          sum(b.waste_rate * b.standard_gross_quantity) / nullif(sum(b.standard_gross_quantity), 0) AS avg_waste
        FROM analytics.fact_recipe_bom b
        LEFT JOIN (SELECT material_code, count(DISTINCT sku_code) AS ref_count FROM analytics.fact_recipe_bom GROUP BY material_code) r ON r.material_code = b.material_code
        GROUP BY b.sku_code
      ) bom ON bom.sku_code = s.dish_code
      WHERE s.dish_code IS NOT NULL
    `, [diagDate])

    const countResult = await query(`SELECT count(*) FROM public.dish_diagnosis_snapshot WHERE diagnosis_date = $1`, [diagDate])
    sendSuccess(res, { generated: countResult.rows[0].count, date: diagDate })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 获取诊断建议列表
router.get('/diagnosis', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const priority = req.query.priority as string
    const sort = (req.query.sort as string) || 'priority'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'

    let where = 'WHERE 1=1'
    const params: any[] = []
    if (priority) { params.push(priority); where += ` AND priority = $${params.length}` }

    const validSorts = ['priority', 'cost_variance_amount', 'sales_amount', 'actual_margin_pct', 'theoretical_margin_pct', 'dish_name']
    let sortExpr: string
    if (sort === 'priority') {
      sortExpr = `CASE priority WHEN 'P0' THEN 0 WHEN 'P1' THEN 1 WHEN 'P2' THEN 2 ELSE 3 END ${order}`
    } else if (validSorts.includes(sort)) {
      sortExpr = `${sort} ${order} NULLS LAST`
    } else {
      sortExpr = `CASE priority WHEN 'P0' THEN 0 WHEN 'P1' THEN 1 WHEN 'P2' THEN 2 ELSE 3 END ${order}`
    }

    const countResult = await query(`SELECT count(*) FROM public.dish_diagnosis_snapshot ${where}`, params)
    const total = countResult.rows[0].count

    params.push(pageSize, offset)
    const result = await query(`
      SELECT * FROM public.dish_diagnosis_snapshot ${where}
      ORDER BY ${sortExpr}, cost_variance_amount DESC NULLS LAST
      LIMIT $${params.length - 1} OFFSET $${params.length}
    `, params)
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 获取调整记录列表
router.get('/adjustment', async (req: AuthRequest, res) => {
  try {
    const { page, pageSize, offset } = parsePagination(req)
    const status = req.query.status as string
    const sort = (req.query.sort as string) || 'effective_date'
    const order = (req.query.order as string) === 'asc' ? 'ASC' : 'DESC'

    let where = 'WHERE 1=1'
    const params: any[] = []
    if (status) { params.push(status); where += ` AND status = $${params.length}` }

    const validSorts = ['effective_date', 'created_at', 'dish_name', 'adjustment_type', 'before_theoretical_margin', 'after_target_margin']
    const sortCol = validSorts.includes(sort) ? sort : 'effective_date'

    const countResult = await query(`SELECT count(*) FROM public.dish_adjustment_log ${where}`, params)
    const total = countResult.rows[0].count

    params.push(pageSize, offset)
    const result = await query(`
      SELECT * FROM public.dish_adjustment_log ${where}
      ORDER BY ${sortCol} ${order} NULLS LAST, created_at DESC LIMIT $${params.length - 1} OFFSET $${params.length}
    `, params)
    sendSuccess(res, result.rows, { page, pageSize, total })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 创建调整记录
router.post('/adjustment', async (req: AuthRequest, res) => {
  try {
    const { dish_code, dish_name, adjustment_type, before_price, before_theoretical_cost, before_theoretical_margin, before_sales_avg_daily, before_cost_variance, after_price, after_theoretical_cost, after_target_margin, target_cost_reduction, effective_date, decided_by, reason, diagnosis_id } = req.body

    if (!dish_code || !dish_name || !adjustment_type || !effective_date) {
      return sendError(res, 'dish_code, dish_name, adjustment_type, effective_date are required')
    }

    const result = await query(`
      INSERT INTO public.dish_adjustment_log (dish_code, dish_name, adjustment_type, before_price, before_theoretical_cost, before_theoretical_margin, before_sales_avg_daily, before_cost_variance, after_price, after_theoretical_cost, after_target_margin, target_cost_reduction, effective_date, decided_by, reason, diagnosis_id, status)
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, 'planned')
      RETURNING id
    `, [dish_code, dish_name, adjustment_type, before_price, before_theoretical_cost, before_theoretical_margin, before_sales_avg_daily, before_cost_variance, after_price, after_theoretical_cost, after_target_margin, target_cost_reduction, effective_date, decided_by, reason, diagnosis_id])

    sendSuccess(res, { id: result.rows[0].id })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 更新调整状态
router.put('/adjustment/:id', async (req: AuthRequest, res) => {
  try {
    const id = parseInt(req.params.id)
    const { status } = req.body
    const result = await query(`
      UPDATE public.dish_adjustment_log SET status = $1, updated_at = now() WHERE id = $2 RETURNING id
    `, [status, id])
    if (result.rowCount === 0) return sendError(res, 'Adjustment not found', 404)
    sendSuccess(res, { id: result.rows[0].id })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 获取验证数据（销量对比）
router.get('/adjustment/:id/verify', async (req: AuthRequest, res) => {
  try {
    const id = parseInt(req.params.id)
    const adjResult = await query(`SELECT * FROM public.dish_adjustment_log WHERE id = $1`, [id])
    if (adjResult.rowCount === 0) return sendError(res, 'Adjustment not found', 404)
    const adj = adjResult.rows[0]

    const effectiveDate = adj.effective_date
    const dishName = adj.dish_name

    const beforeData = await query(`
      SELECT (closed_at)::date AS day, count(*) AS bills, round(sum(gross_amount)::numeric, 2) AS sales
      FROM public.dish_sales_details
      WHERE dish_name = $1 AND closed_at >= ($2::date - interval '7 days') AND closed_at < $2::date
      GROUP BY day ORDER BY day
    `, [dishName, effectiveDate])

    const afterData = await query(`
      SELECT (closed_at)::date AS day, count(*) AS bills, round(sum(gross_amount)::numeric, 2) AS sales
      FROM public.dish_sales_details
      WHERE dish_name = $1 AND closed_at >= $2::date AND closed_at < ($2::date + interval '7 days')
      GROUP BY day ORDER BY day
    `, [dishName, effectiveDate])

    const beforeAvg = beforeData.rows.length > 0 ? beforeData.rows.reduce((s, r) => s + Number(r.bills), 0) / beforeData.rows.length : 0
    const afterAvg = afterData.rows.length > 0 ? afterData.rows.reduce((s, r) => s + Number(r.bills), 0) / afterData.rows.length : 0
    const changePct = beforeAvg > 0 ? ((afterAvg - beforeAvg) / beforeAvg * 100) : null

    sendSuccess(res, {
      adjustment: adj,
      before: beforeData.rows,
      after: afterData.rows,
      before_avg_daily: Math.round(beforeAvg * 100) / 100,
      after_avg_daily: Math.round(afterAvg * 100) / 100,
      sales_change_pct: changePct !== null ? Math.round(changePct * 100) / 100 : null,
    })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

// 提交验证结果
router.post('/adjustment/:id/result', async (req: AuthRequest, res) => {
  try {
    const id = parseInt(req.params.id)
    const { period_type, period_start, period_end, actual_sales_avg_daily, actual_sales_change_pct, actual_margin, actual_margin_change, actual_cost_variance, actual_cost_change_pct, target_achieved, assessment, notes } = req.body

    const result = await query(`
      INSERT INTO public.dish_adjustment_result (adjustment_id, period_type, period_start, period_end, actual_sales_avg_daily, actual_sales_change_pct, actual_margin, actual_margin_change, actual_cost_variance, actual_cost_change_pct, target_achieved, assessment, notes)
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
      RETURNING id
    `, [id, period_type, period_start, period_end, actual_sales_avg_daily, actual_sales_change_pct, actual_margin, actual_margin_change, actual_cost_variance, actual_cost_change_pct, target_achieved, assessment, notes])

    sendSuccess(res, { id: result.rows[0].id })
  } catch (err: any) {
    sendError(res, err.message)
  }
})

export default router
