<template>
  <section class="page-grid">
    <section class="panel">
      <div class="panel-header">
        <div>
          <p class="eyebrow">产品需规清单</p>
          <h3>产品 + 用料 + 小程序报工字段合并维护</h3>
        </div>
        <div class="workflow-inline-actions">
          <span class="panel-tag">生产基础</span>
          <button class="primary-button" type="button" @click="openCreateDrawer">新增产品需规</button>
        </div>
      </div>

      <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
      <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

      <TableControls
        v-model:search="controls.search.value"
        v-model:sort-key="controls.sortKey.value"
        v-model:sort-direction="controls.sortDirection.value"
        :sort-options="controls.sortOptions"
        placeholder="搜索考勤点、产品编码、名称、版本、原材料、小程序工序"
      />

      <div class="table-wrap">
        <table class="data-table compact-table">
          <thead>
            <tr>
              <th>产品编码</th>
              <th>考勤点</th>
              <th>小程序项目号</th>
              <th>产品</th>
              <th>版本</th>
              <th>净重</th>
              <th>默认原材料</th>
              <th>产品毛重</th>
              <th>边料损耗率</th>
              <th>允许报废率</th>
              <th>废料单价</th>
              <th>小程序工序</th>
              <th>标准节拍</th>
              <th>状态</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="row in paginatedRows" :key="row.row_key" :class="rowClass(row)">
              <td>
                <button
                  class="link-button"
                  type="button"
                  :disabled="!canModifySpec(row)"
                  :title="canModifySpec(row) ? '编辑' : '已停用的产品需规只读，不允许修改'"
                  @click="openEditDrawer(row)"
                >
                  {{ row.item_code || row.project_no }}
                </button>
              </td>
              <td>{{ row.attendance_point_name || "-" }}</td>
              <td>{{ row.project_no || "-" }}</td>
              <td>{{ row.item_name }}</td>
              <td>{{ row.version_no }}</td>
              <td>{{ row.unit_weight_kg ? formatWeight(row.unit_weight_kg) : "-" }}</td>
              <td>{{ row.default_material_name || "未配置" }}</td>
              <td>{{ row.gross_weight_per_piece_kg ? formatWeight(row.gross_weight_per_piece_kg) : "-" }}</td>
              <td>{{ formatPercent(row.loss_rate) }}</td>
              <td>{{ formatPercent(row.allowed_scrap_rate) }}</td>
              <td>¥{{ formatAmount(row.waste_price_yuan_per_kg) }}</td>
              <td>{{ row.process_name || "-" }}</td>
              <td>{{ row.standard_beat ? `${row.standard_beat}s/件` : "-" }}</td>
              <td>{{ row.miniapp_only ? "小程序清单" : formatStatusLabel(row.status) }}</td>
              <td class="action-row">
                <button
                  class="ghost-button"
                  type="button"
                  :disabled="!canModifySpec(row)"
                  :title="canModifySpec(row) ? '编辑' : '已停用的产品需规只读，不允许修改'"
                  @click="openEditDrawer(row)"
                >
                  编辑
                </button>
                <button
                  class="ghost-button danger-ghost"
                  type="button"
                  :disabled="!canModifySpec(row)"
                  :title="canModifySpec(row) ? '删除' : '已停用的产品需规只读，不允许删除'"
                  @click="removeSpec(row)"
                >
                  删除
                </button>
              </td>
            </tr>
            <tr v-if="!filteredRows.length">
              <td colspan="15" class="empty-row">暂无产品需规</td>
            </tr>
          </tbody>
        </table>
      </div>
      <PaginationBar v-model:page="page" v-model:page-size="pageSize" :total="filteredRows.length" />
    </section>

    <FormDrawer
      :open="drawerOpen"
      eyebrow="产品需规"
      :title="editingRow ? '编辑产品需规' : '新增产品需规'"
      description="lite 模式下，一个抽屉完成产品资料、默认用料和小程序报工字段。系统会按小程序工序自动生成内部默认工艺，供生产工单使用。"
      tag="产品需规清单"
      wide
      @close="drawerOpen = false"
    >
      <form class="stack-form" @submit.prevent="submitSpec">
        <div class="subsection-head">
          <strong>01 产品资料</strong>
        </div>
        <div class="triple-field">
          <label class="form-field">
            <span>产品编码</span>
            <input :value="productCodePreview" type="text" disabled readonly />
          </label>
          <label class="form-field">
            <span>考勤点 <i class="required-mark">*</i></span>
            <select v-model="form.attendance_point_name" required>
              <option disabled value="">请选择考勤点</option>
              <option v-for="point in attendancePoints" :key="point.name" :value="point.name">
                {{ point.name }}
              </option>
            </select>
          </label>
          <label class="form-field">
            <span>小程序项目号</span>
            <input v-model.trim="form.project_no" type="text" placeholder="默认使用产品编码" />
          </label>
          <label class="form-field">
            <span>产品名称</span>
            <input v-model.trim="form.item_name" type="text" required />
          </label>
          <label class="form-field">
            <span>版本号</span>
            <input v-model.trim="form.version_no" type="text" required />
          </label>
        </div>
        <div class="triple-field">
          <label class="form-field">
            <span>产品净重(kg)</span>
            <input v-model.number="form.unit_weight_kg" type="number" min="0.000001" step="0.000001" required />
          </label>
          <label class="form-field">
            <span>产品毛重(kg)</span>
            <input v-model.number="form.gross_weight_per_piece_kg" type="number" min="0.000001" step="0.000001" required />
          </label>
          <label class="form-field">
            <span>边料损耗率</span>
            <input :value="formatPercent(calculatedLossRate)" type="text" disabled />
          </label>
        </div>
        <div class="double-field">
          <label class="form-field">
            <span>小程序型材号</span>
            <input v-model.trim="form.profile_no" type="text" />
          </label>
          <label class="form-field">
            <span>允许报废率</span>
            <input v-model.number="form.allowed_scrap_rate" type="number" min="0" step="0.0001" />
          </label>
        </div>

        <div class="subsection-head">
          <strong>02 默认用料</strong>
        </div>
        <div class="triple-field">
          <label class="form-field">
            <span>原材料（从原材料名录选择）</span>
            <select v-model="form.material_item_id" required>
              <option disabled value="">请选择原材料</option>
              <option v-for="material in selectableMaterials" :key="material.item_id" :value="String(material.item_id)">
                {{ material.item_code }} · {{ material.item_name }} · {{ material.material_grade }}
              </option>
            </select>
          </label>
          <label class="form-field">
            <span>用料备注</span>
            <input v-model.trim="form.bom_remark" type="text" />
          </label>
          <label class="form-field">
            <span>废料单价(元/kg)</span>
            <input :value="formatAmount(selectedMaterialWastePrice)" type="text" disabled />
          </label>
        </div>

        <div class="subsection-head">
          <strong>03 小程序报工字段</strong>
        </div>
        <div class="process-field-list">
          <div class="process-field-header">
            <span>小程序工序</span>
            <span>冲压方式</span>
            <span>操作人数</span>
            <span>标准节拍(秒/件)</span>
          </div>
          <div v-for="(processRow, index) in processFormRows" :key="processRow.row_uid" class="process-field-row">
            <input v-model.trim="processRow.process_name" type="text" :placeholder="`第${index + 1}道工序`" required />
            <input v-model.trim="processRow.stamping_method" type="text" />
            <input v-model.number="processRow.operator_count" type="number" min="0" step="0.01" />
            <input v-model.number="processRow.standard_beat" type="number" min="0" step="0.001" />
          </div>
          <button class="ghost-button" type="button" @click="addProcessFormRow">新增工序</button>
        </div>

        <button class="primary-button" type="submit" :disabled="submitting">
          {{ submitting ? "保存中..." : "保存产品需规" }}
        </button>
      </form>
    </FormDrawer>
  </section>
</template>

<script setup>
import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";

import FormDrawer from "../components/FormDrawer.vue";
import PaginationBar from "../components/PaginationBar.vue";
import TableControls from "../components/TableControls.vue";
import { deleteResource, fetchResource, postResource, putResource } from "../services/api";
import { buildNextProductSpecCode, buildProductCodePreview } from "../utils/codeRules";
import { formatStatusLabel } from "../utils/dictionaries";
import { formatAmount, formatWeight } from "../utils/formatters";
import { usePagination } from "../utils/pagination";
import { useTableControls } from "../utils/tableControls";

const products = ref([]);
const boms = ref([]);
const routes = ref([]);
const materials = ref([]);
const miniappProducts = ref([]);
const attendancePoints = ref([]);
const processes = ref([]);
const workCenters = ref([]);
const drawerOpen = ref(false);
const editingRow = ref(null);
const submitting = ref(false);
const miniappSyncing = ref(false);
const hasLoaded = ref(false);
const feedbackMessage = ref("");
const errorMessage = ref("");
const processFormRows = ref([]);

let processFormRowSerial = 1;

const form = reactive(buildEmptyForm());
const nextProductCode = computed(() => buildNextProductSpecCode(products.value));
const productCodePreview = computed(() =>
  buildProductCodePreview(form, editingRow.value?.item_code || "", nextProductCode.value)
);

const defaultBomByProduct = computed(() => {
  const map = new Map();
  boms.value.forEach((bom) => {
    if (!map.has(Number(bom.product_item_id)) || bom.is_default) {
      map.set(Number(bom.product_item_id), bom);
    }
  });
  return map;
});
const defaultRouteByProduct = computed(() => {
  const map = new Map();
  routes.value.forEach((route) => {
    if (!map.has(Number(route.product_item_id)) || route.is_default) {
      map.set(Number(route.product_item_id), route);
    }
  });
  return map;
});
const productSpecs = computed(() =>
  {
    const rows = [];
    const miniappProcessNamesByProductId = new Map();

    miniappProducts.value.forEach((miniappProduct) => {
      const product = findErpProductForMiniappProduct(miniappProduct);
      if (product?.item_id) {
        const productId = Number(product.item_id);
        if (!miniappProcessNamesByProductId.has(productId)) {
          miniappProcessNamesByProductId.set(productId, new Set());
        }
        const processName = normalizeMiniappProcessName(miniappProduct.process_name);
        if (processName) {
          miniappProcessNamesByProductId.get(productId).add(processName);
        }
      }
      rows.push(buildProcessRow(product, miniappProduct));
    });

    products.value
      .filter((product) => miniappProcessNamesByProductId.has(Number(product.item_id)))
      .forEach((product) => {
        const productProcessNames = miniappProcessNamesByProductId.get(Number(product.item_id)) || new Set();
        const route = defaultRouteByProduct.value.get(Number(product.item_id));
        (route?.operations || [])
          .filter((operation) => {
            const operationName = normalizeMiniappProcessName(operation.operation_name);
            return operationName && !productProcessNames.has(operationName);
          })
          .forEach((operation, index) => {
            rows.push(buildProcessRow(product, null, operation, index));
          });
      });

    products.value
      .filter((product) => !miniappProcessNamesByProductId.has(Number(product.item_id)))
      .forEach((product) => {
        const route = defaultRouteByProduct.value.get(Number(product.item_id));
        const normalizedRouteOperations = (route?.operations || []).filter((operation) =>
          normalizeMiniappProcessName(operation.operation_name)
        );
        const routeOperations = normalizedRouteOperations.length ? normalizedRouteOperations : [null];
        routeOperations.forEach((operation, index) => {
          rows.push(buildProcessRow(product, null, operation, index));
        });
      });

    return rows;
  }
);
const controls = useTableControls(productSpecs, {
  searchFields: ["attendance_point_name", "item_code", "project_no", "item_name", "version_no", "default_material_name", "process_name"],
  sortOptions: [
    { key: "attendance_point_name", label: "考勤点" },
    { key: "item_code", label: "产品编码" },
    { key: "item_name", label: "产品" },
    { key: "version_no", label: "版本" },
    { key: "unit_weight_kg", label: "净重" },
    { key: "gross_weight_per_piece_kg", label: "产品毛重" },
    { key: "loss_rate", label: "边料损耗率" },
    { key: "allowed_scrap_rate", label: "允许报废率" },
    { key: "waste_price_yuan_per_kg", label: "废料单价" },
    { key: "status", label: "状态" }
  ],
  defaultSortKey: "item_code",
  defaultSortDirection: "asc"
});
const filteredRows = controls.rows;
const { page, pageSize, rows: paginatedRows } = usePagination(filteredRows);
const selectedFormMaterial = computed(() =>
  materials.value.find((material) => Number(material.item_id) === Number(form.material_item_id)) || null
);
const selectableMaterials = computed(() => {
  const selectedId = Number(form.material_item_id || 0);
  return materials.value.filter((material) => material.status === "ACTIVE" || Number(material.item_id) === selectedId);
});
const selectedMaterialWastePrice = computed(() =>
  Number(selectedFormMaterial.value?.scrap_sale_price || 0)
);
const calculatedLossRate = computed(() =>
  calculateScrapLossRate(form.unit_weight_kg, form.gross_weight_per_piece_kg)
);

function formatPercent(value) {
  return `${(Number(value || 0) * 100).toFixed(2)}%`;
}

function calculateScrapLossRate(netWeight, grossWeight) {
  const net = Number(netWeight || 0);
  const gross = Number(grossWeight || 0);
  if (gross <= 0) {
    return 0;
  }
  return Math.max(gross - net, 0) / gross;
}

function miniappProductKey(product) {
  return [
    product.attendance_point_name || "",
    product.project_no || "",
    product.product_name || "",
    product.device_no || "",
    product.process_name || ""
  ].join("||");
}

function findMiniappProductForErpProduct(product) {
  return (
    miniappProducts.value.find((item) => item.product_name === product.item_name)
    || miniappProducts.value.find((item) => item.project_no === product.item_code)
    || null
  );
}

function findErpProductForMiniappProduct(miniappProduct) {
  return (
    products.value.find((item) => item.item_name === miniappProduct.product_name)
    || products.value.find((item) => item.item_code === miniappProduct.project_no)
    || null
  );
}

function findMatchedMaterial(miniappProduct) {
  if (!miniappProduct) {
    return null;
  }
  return (
    materials.value.find((material) => sameText(material.item_code, miniappProduct.material_code))
    || materials.value.find((material) => sameText(material.item_name, miniappProduct.material_name))
    || null
  );
}

function formatMiniappMaterialLabel(miniappProduct) {
  const code = normalizeText(miniappProduct?.material_code);
  const name = normalizeText(miniappProduct?.material_name);
  return [code, name].filter(Boolean).join(" · ");
}

function sameText(left, right) {
  return String(left || "").trim() === String(right || "").trim();
}

function normalizeText(value) {
  return String(value || "").trim();
}

function normalizeMiniappProcessName(value) {
  const text = normalizeText(value);
  if (!text || text === "清洗") {
    return "";
  }
  if (text === "杂活") {
    return text;
  }
  const legacyMatch = text.match(/^0*(\d+)序$/);
  if (legacyMatch) {
    return String(Number(legacyMatch[1]));
  }
  const numberMatch = text.match(/^0*(\d+)$/);
  if (numberMatch) {
    return String(Number(numberMatch[1]));
  }
  return text;
}

function validateMiniappProcessName(value) {
  const rawText = normalizeText(value);
  if (rawText === "清洗") {
    throw new Error("清洗请填写在冲压方式中，小程序工序只能填写正整数");
  }
  const processName = normalizeMiniappProcessName(value);
  if (!processName) {
    return "";
  }
  if (processName === "杂活") {
    return processName;
  }
  if (!/^\d+$/.test(processName) || Number(processName) <= 0) {
    throw new Error(`小程序工序「${rawText}」不符合规范，请填写正整数`);
  }
  return processName;
}

function hasNumberValue(value) {
  return value !== null && value !== undefined && value !== "";
}

function pickNumber(...values) {
  const matched = values.find((value) => hasNumberValue(value));
  return Number(matched || 0);
}

function resolveMaterialItemId(row) {
  if (row.miniappProduct) {
    const matchedMaterial = findMatchedMaterial(row.miniappProduct);
    return matchedMaterial?.item_id ? String(matchedMaterial.item_id) : "";
  }
  if (row.material_item_id) {
    return String(row.material_item_id);
  }
  const miniappProduct = row.miniappProduct || null;
  const matchedMaterial = findMatchedMaterial(miniappProduct);
  if (matchedMaterial?.item_id) {
    return String(matchedMaterial.item_id);
  }
  const defaultMaterialName = String(row.default_material_name || "");
  const fallbackMaterial = materials.value.find((material) => (
    defaultMaterialName.includes(material.item_code) || defaultMaterialName.includes(material.item_name)
  ));
  return fallbackMaterial?.item_id ? String(fallbackMaterial.item_id) : "";
}

function buildProcessRow(product, miniappProduct, routeOperation = null, routeOperationIndex = 0) {
  const matchedMaterial = findMatchedMaterial(miniappProduct);
  const bom = product ? defaultBomByProduct.value.get(Number(product.item_id)) : null;
  const route = product ? defaultRouteByProduct.value.get(Number(product.item_id)) : null;
  const bomItem = bom?.items?.[0] || null;
  const firstRouteOperation = route?.operations?.[0] || null;
  const miniappProcessName = normalizeMiniappProcessName(miniappProduct?.process_name);
  const matchedRouteOperation = route?.operations?.find((operation) =>
    sameText(normalizeMiniappProcessName(operation.operation_name), miniappProcessName)
  ) || null;
  const activeRouteOperation = routeOperation || matchedRouteOperation || firstRouteOperation;
  const routeSummary = route?.operations?.length
    ? route.operations.map((item) => normalizeMiniappProcessName(item.operation_name)).filter(Boolean).join(" → ")
    : "";
  const miniappMaterialLabel = formatMiniappMaterialLabel(miniappProduct);
  const materialWastePrice = Number(
    miniappProduct
      ? (miniappProduct.waste_price_yuan_per_kg ?? 0)
      : (matchedMaterial?.scrap_sale_price || 0)
  );
  const netWeight = pickNumber(miniappProduct?.product_net_weight_kg, bomItem?.net_weight_per_piece_kg, product?.unit_weight_kg, 0);
  const grossWeight = pickNumber(miniappProduct?.product_gross_weight_kg, bomItem?.gross_weight_per_piece_kg, 0);
  const lossRate = grossWeight > 0 ? calculateScrapLossRate(netWeight, grossWeight) : Number(bomItem?.loss_rate || 0);
  const allowedScrapRate = pickNumber(miniappProduct?.allowed_scrap_rate, miniappProduct?.scrap_loss_rate, product?.allowed_scrap_rate, 0);
  return {
    row_key: miniappProduct
      ? `miniapp-${miniappProductKey(miniappProduct)}`
      : `erp-${product.item_id}-${activeRouteOperation?.route_operation_id || activeRouteOperation?.seq_no || routeOperationIndex}`,
    miniapp_only: !product,
    product_only: !miniappProduct,
    ...(product || {}),
    bom,
    route,
    miniappProduct,
    item_id: product?.item_id || null,
    item_code: product?.item_code || miniappProduct?.project_no || "",
    item_name: product?.item_name || miniappProduct?.product_name || "",
    attendance_point_name: miniappProduct?.attendance_point_name || "",
    specification: product?.specification || "",
    version_no: product?.version_no || "V1.0",
    unit_weight_kg: netWeight,
    allowed_scrap_rate: allowedScrapRate,
    project_no: miniappProduct?.project_no || product?.item_code || "",
    profile_no: miniappProduct?.profile_no || "",
    process_name: miniappProcessName || normalizeMiniappProcessName(activeRouteOperation?.operation_name) || "",
    stamping_method: miniappProduct?.stamping_method || "",
    operator_count: miniappProduct?.operator_count || 1,
    standard_beat: miniappProduct?.standard_beat || activeRouteOperation?.std_cycle_seconds || 0,
    standard_workload: 0,
    default_material_name: miniappProduct ? miniappMaterialLabel : (bomItem ? `${bomItem.material_code} · ${bomItem.material_name}` : ""),
    material_item_id: miniappProduct
      ? (matchedMaterial?.item_id ? String(matchedMaterial.item_id) : "")
      : (bomItem?.material_item_id ? String(bomItem.material_item_id) : ""),
    net_weight_per_piece_kg: netWeight,
    gross_weight_per_piece_kg: grossWeight,
    loss_rate: lossRate,
    waste_price_yuan_per_kg: materialWastePrice,
    route_summary: routeSummary || miniappProduct?.process_name || "",
    route_operation_id: activeRouteOperation?.route_operation_id || null,
    route_operation_status: activeRouteOperation?.status || null,
    status: activeRouteOperation?.status || product?.status || "MINIAPP_ONLY"
  };
}

function isInactiveSyncedProcess(row) {
  return row.product_only && row.route_operation_id && row.status === "INACTIVE";
}

function canModifySpec(row) {
  return row.status !== "INACTIVE";
}

function rowClass(row) {
  return {
    "inactive-spec-row": row.status === "INACTIVE",
    "stale-miniapp-process-row": isInactiveSyncedProcess(row)
  };
}

function buildEmptyForm() {
  return {
    attendance_point_name: "",
    project_no: "",
    item_name: "",
    specification: "",
    profile_no: "",
    version_no: "V1.0",
    unit_weight_kg: 1,
    allowed_scrap_rate: 0,
    material_item_id: "",
    gross_weight_per_piece_kg: 1,
    loss_rate: 0,
    waste_price_yuan_per_kg: 0,
    bom_remark: "",
    process_name: "",
    stamping_method: "",
    operator_count: 1,
    standard_beat: 0,
    standard_workload: 0
  };
}

function resetForm() {
  Object.assign(form, buildEmptyForm());
  processFormRows.value = [];
  editingRow.value = null;
}

function openCreateDrawer() {
  resetForm();
  form.attendance_point_name = attendancePoints.value[0]?.name || "";
  processFormRows.value = [buildProcessFormRow()];
  feedbackMessage.value = "";
  errorMessage.value = "";
  drawerOpen.value = true;
}

function openEditDrawer(row) {
  if (!canModifySpec(row)) {
    errorMessage.value = "已停用的产品需规只读，不允许修改";
    return;
  }
  resetForm();
  editingRow.value = row;
  Object.assign(form, {
    project_no: row.project_no || row.item_code || "",
    attendance_point_name: row.attendance_point_name || attendancePoints.value[0]?.name || "",
    item_name: row.item_name,
    specification: row.specification || "",
    profile_no: row.profile_no || "",
    version_no: row.version_no,
    unit_weight_kg: Number(row.unit_weight_kg || 0),
    allowed_scrap_rate: Number(row.allowed_scrap_rate || 0),
    material_item_id: resolveMaterialItemId(row),
    gross_weight_per_piece_kg: Number(row.gross_weight_per_piece_kg || 0),
    loss_rate: Number(row.loss_rate || 0),
    waste_price_yuan_per_kg: Number(row.waste_price_yuan_per_kg || 0),
    bom_remark: row.bom?.remark || ""
  });
  processFormRows.value = buildEditableProcessFormRows(row);
  drawerOpen.value = true;
}

function buildBomPayload(productItemId) {
  return {
    product_item_id: productItemId,
    version_no: form.version_no,
    is_default: true,
    status: "ACTIVE",
    remark: form.bom_remark || null,
    items: [
      {
        seq_no: 1,
        material_item_id: Number(form.material_item_id),
        usage_type: "WEIGHT",
        qty_per_piece: 1,
        net_weight_per_piece_kg: Number(form.unit_weight_kg || 0),
        gross_weight_per_piece_kg: Number(form.gross_weight_per_piece_kg || 0),
        loss_rate: calculatedLossRate.value,
        issue_over_tolerance_rate: 0,
        remark: null
      }
    ]
  };
}

function sortProcessRows(rows) {
  return [...rows].sort((left, right) => {
    const leftNo = Number(String(left.process_name || "").match(/\d+/)?.[0] || 9999);
    const rightNo = Number(String(right.process_name || "").match(/\d+/)?.[0] || 9999);
    if (leftNo !== rightNo) {
      return leftNo - rightNo;
    }
    return String(left.process_name || "").localeCompare(String(right.process_name || ""), "zh-Hans-CN");
  });
}

function buildProcessFormRow(source = {}) {
  return {
    row_uid: `process-form-${processFormRowSerial++}`,
    original_attendance_point_name: source.original_attendance_point_name || null,
    original_project_no: source.original_project_no || null,
    original_product_name: source.original_product_name || null,
    original_device_no: source.original_device_no || null,
    original_process_name: source.original_process_name || null,
    device_no: source.device_no || "",
    process_name: source.process_name || "",
    stamping_method: source.stamping_method || "",
    operator_count: Number(source.operator_count || 1),
    standard_beat: Number(source.standard_beat || 0),
    standard_workload: 0
  };
}

function buildProcessFormRowFromMiniapp(miniappProduct) {
  return buildProcessFormRow({
    original_attendance_point_name: miniappProduct.attendance_point_name || null,
    original_project_no: miniappProduct.project_no || null,
    original_product_name: miniappProduct.product_name || null,
    original_device_no: miniappProduct.device_no || "",
    original_process_name: miniappProduct.process_name || "",
    device_no: miniappProduct.device_no || "",
    process_name: normalizeMiniappProcessName(miniappProduct.process_name) || miniappProduct.process_name || "",
    stamping_method: miniappProduct.stamping_method || "",
    operator_count: Number(miniappProduct.operator_count || 1),
    standard_beat: Number(miniappProduct.standard_beat || 0),
    standard_workload: 0
  });
}

function buildProcessFormRowFromRouteOperation(operation) {
  return buildProcessFormRow({
    process_name: normalizeMiniappProcessName(operation?.operation_name),
    operator_count: 1,
    standard_beat: Number(operation?.std_cycle_seconds || 0),
    standard_workload: 0
  });
}

function findMiniappProcessRowsForProduct(row) {
  const source = row.miniappProduct || row;
  const attendancePointName = normalizeText(source.attendance_point_name || row.attendance_point_name);
  const projectNo = normalizeText(source.project_no || row.project_no || row.item_code);
  const productName = normalizeText(source.product_name || row.item_name);

  return miniappProducts.value.filter((item) => {
    const sameAttendancePoint = attendancePointName
      ? sameText(item.attendance_point_name, attendancePointName)
      : true;
    const sameProjectNo = projectNo && sameText(item.project_no, projectNo);
    const sameProductName = productName && sameText(item.product_name, productName);
    return sameAttendancePoint && sameProjectNo && sameProductName;
  });
}

function buildEditableProcessFormRows(row) {
  const rows = [];
  const seen = new Set();
  const addRow = (processRow) => {
    const processName = normalizeText(processRow.process_name);
    if (!processName || seen.has(processName)) {
      return;
    }
    seen.add(processName);
    rows.push(processRow);
  };

  const miniappRows = findMiniappProcessRowsForProduct(row);
  miniappRows.forEach((miniappProduct) => {
    addRow(buildProcessFormRowFromMiniapp(miniappProduct));
  });
  if (!miniappRows.length) {
    (row.route?.operations || []).forEach((operation) => {
      addRow(buildProcessFormRowFromRouteOperation(operation));
    });
    addRow(buildProcessFormRow({
      process_name: normalizeMiniappProcessName(row.process_name) || row.process_name || "",
      stamping_method: row.stamping_method || "",
      operator_count: Number(row.operator_count || 1),
      standard_beat: Number(row.standard_beat || 0)
    }));
  }

  return sortProcessRows(rows.length ? rows : [buildProcessFormRow()]);
}

function addProcessFormRow() {
  processFormRows.value.push(buildProcessFormRow());
}

function normalizeProcessFormRowsForSave() {
  const rows = processFormRows.value
    .map((row) => ({
      ...row,
      process_name: validateMiniappProcessName(row.process_name),
      stamping_method: normalizeText(row.stamping_method),
      operator_count: Number(row.operator_count || 1),
      standard_beat: Number(row.standard_beat || 0),
      standard_workload: 0
    }))
    .filter((row) => row.process_name);
  if (!rows.length) {
    throw new Error("请至少维护一道小程序工序");
  }

  const seen = new Set();
  rows.forEach((row) => {
    if (seen.has(row.process_name)) {
      throw new Error(`小程序工序「${row.process_name}」重复，请合并后再保存`);
    }
    if (row.process_name === "杂活" && row.stamping_method !== "处理杂活") {
      throw new Error("工序为「杂活」时，冲压方式必须填写「处理杂活」");
    }
    if (row.process_name !== "杂活" && row.stamping_method === "处理杂活") {
      throw new Error("冲压方式为「处理杂活」时，小程序工序必须填写「杂活」");
    }
    if (row.stamping_method !== "清洗" && row.process_name !== "杂活" && Number(row.standard_beat || 0) <= 0) {
      throw new Error(`第 ${row.process_name} 道工序必须填写大于 0 的标准节拍`);
    }
    seen.add(row.process_name);
  });
  return sortProcessRows(rows);
}

function buildMiniappCommonPayload(product) {
  const selectedMaterial = materials.value.find((item) => Number(item.item_id) === Number(form.material_item_id));
  return {
    attendance_point_name: form.attendance_point_name,
    project_no: form.project_no || product.item_code,
    product_name: form.item_name,
    profile_no: form.profile_no || null,
    material_code: selectedMaterial?.item_code || null,
    material_name: selectedMaterial?.item_name || null,
    supplier: selectedMaterial?.supplier_name || null,
    product_net_weight_kg: Number(form.unit_weight_kg || 0),
    product_gross_weight_kg: Number(form.gross_weight_per_piece_kg || 0),
    allowed_scrap_rate: Number(form.allowed_scrap_rate || 0),
    scrap_loss_rate: Number(form.allowed_scrap_rate || 0),
    waste_price_yuan_per_kg: Number(selectedMaterial?.scrap_sale_price ?? form.waste_price_yuan_per_kg ?? 0),
  };
}

function buildMiniappProductPayload(product, processRow) {
  const standardBeat = Number(processRow.standard_beat || 0);
  return {
    ...buildMiniappCommonPayload(product),
    device_no: processRow.device_no || "",
    process_name: processRow.process_name,
    stamping_method: processRow.stamping_method || null,
    operator_count: Number(processRow.operator_count || 1),
    standard_beat: standardBeat,
    standard_workload: 0,
    original_attendance_point_name: processRow.original_attendance_point_name || null,
    original_project_no: processRow.original_project_no || null,
    original_product_name: processRow.original_product_name || null,
    original_device_no: processRow.original_device_no || null,
    original_process_name: processRow.original_process_name || null
  };
}

function buildProcessGroupForRoute(processRowsForSave) {
  return processRowsForSave;
}

function buildAutoRouteOperation(processRow, index) {
  const processName = String(processRow.process_name || "").trim();
  if (!processName) {
    throw new Error("请填写小程序工序，系统需要用它自动生成内部默认工艺");
  }
  const matchedProcess = processes.value.find((process) => process.process_name === processName) || processes.value[0];
  const defaultWorkCenter = workCenters.value[0];
  if (!matchedProcess?.process_id || !defaultWorkCenter?.id) {
    throw new Error("基础资料缺少工序类型或工作中心，无法自动生成内部默认工艺");
  }
  return {
    seq_no: index + 1,
    process_id: Number(matchedProcess.process_id),
    work_center_id: Number(defaultWorkCenter.id),
    operation_name: processName,
    std_setup_minutes: 0,
    std_cycle_seconds: Number(processRow.standard_beat || 0),
    std_labor_cost: 0,
    std_machine_cost: 0,
    std_outsource_cost: 0,
    allowed_scrap_rate: Number(form.allowed_scrap_rate || 0),
    report_required: true,
    is_key_operation: false,
    status: "ACTIVE"
  };
}

function buildRoutePayload(product, processRowsForSave) {
  const productItemId = product.item_id;
  const processRows = buildProcessGroupForRoute(processRowsForSave);
  if (!processRows.length) {
    throw new Error("请至少维护一道小程序工序");
  }
  return {
    route_name: `${form.item_name} 工艺路线`,
    product_item_id: productItemId,
    version_no: form.version_no,
    is_default: true,
    status: "ACTIVE",
    operations: processRows.map((processRow, index) => buildAutoRouteOperation(processRow, index))
  };
}

function buildMiniappProductPayloads(product, processRowsForSave) {
  const payloads = processRowsForSave.map((processRow) => buildMiniappProductPayload(product, processRow));
  const deduped = new Map();
  payloads.forEach((payload) => {
    const key = `${payload.attendance_point_name || ""}||${payload.project_no || ""}||${payload.product_name || ""}||${payload.device_no || ""}||${payload.process_name || ""}`;
    deduped.set(key, payload);
  });
  return Array.from(deduped.values());
}

function buildMiniappDeletePath(miniappProduct) {
  const params = new URLSearchParams({
    attendance_point_name: miniappProduct.attendance_point_name || "",
    project_no: miniappProduct.project_no || "",
    product_name: miniappProduct.product_name || "",
    device_no: miniappProduct.device_no || "",
    process_name: miniappProduct.process_name || ""
  });
  return `/master-data/miniapp-products?${params.toString()}`;
}

async function removeSpec(row) {
  feedbackMessage.value = "";
  errorMessage.value = "";
  if (!canModifySpec(row)) {
    errorMessage.value = "已停用的产品需规只读，不允许删除";
    return;
  }

  const rowLabel = `${row.item_name || row.product_name || "产品"} ${row.process_name || ""}`.trim();
  if (!window.confirm(`确认删除产品需规“${rowLabel}”吗？`)) {
    return;
  }

  try {
    let result = null;
    if (row.miniappProduct) {
      result = await deleteResource(buildMiniappDeletePath(row.miniappProduct));
    } else if (row.item_id) {
      result = await deleteResource(`/master-data/product-specs/${row.item_id}`);
    } else {
      throw new Error("该产品需规缺少可删除的业务主键");
    }
    feedbackMessage.value = result?.message || `产品需规 ${rowLabel} 已删除`;
    await loadAll();
  } catch (error) {
    errorMessage.value = error.message || "删除产品需规失败";
  }
}

watch(selectedFormMaterial, (material) => {
  form.waste_price_yuan_per_kg = Number(material?.scrap_sale_price || 0);
});

watch(calculatedLossRate, (lossRate) => {
  form.loss_rate = lossRate;
}, { immediate: true });

async function fetchMasterData() {
  const [productRows, bomRows, routeRows, materialRows, miniappProductRows, pointRows, processRows, centerRows] = await Promise.all([
    fetchResource("/master-data/products?limit=200", []),
    fetchResource("/master-data/boms?limit=500", []),
    fetchResource("/master-data/process-routes?limit=500", []),
    fetchResource("/master-data/materials?limit=200", []),
    fetchResource("/master-data/miniapp-products?limit=1000", []),
    fetchResource("/master-data/miniapp-attendance-points", []),
    fetchResource("/master-data/processes", []),
    fetchResource("/master-data/work-centers", [])
  ]);
  products.value = productRows;
  boms.value = bomRows;
  routes.value = routeRows;
  materials.value = materialRows;
  miniappProducts.value = miniappProductRows;
  attendancePoints.value = pointRows;
  processes.value = processRows;
  workCenters.value = centerRows;
}

async function syncMiniappInBackground() {
  if (miniappSyncing.value) {
    return;
  }
  miniappSyncing.value = true;
  try {
    const result = await postResource("/master-data/miniapp-materials/sync", {});
    if (!result?.skipped_by_cache) {
      await fetchMasterData();
    }
  } catch (error) {
    errorMessage.value = error.message || "小程序主数据后台同步失败";
  } finally {
    miniappSyncing.value = false;
  }
}

async function loadAll() {
  await fetchMasterData();
  void syncMiniappInBackground();
}

async function submitSpec() {
  feedbackMessage.value = "";
  errorMessage.value = "";
  submitting.value = true;
  try {
    const processRowsForSave = normalizeProcessFormRowsForSave();
    const productPayload = {
      item_name: form.item_name,
      specification: editingRow.value?.specification || null,
      drawing_no: null,
      product_family: null,
      version_no: form.version_no,
      unit_weight_kg: Number(form.unit_weight_kg || 0),
      allowed_scrap_rate: Number(form.allowed_scrap_rate || 0),
      standard_output_qty: 0,
      quality_standard: null,
      safety_stock_weight_kg: 0,
      status: "ACTIVE"
    };
    const shouldUpdateErpProduct = editingRow.value && !editingRow.value.miniapp_only && editingRow.value.item_id;
    const product = shouldUpdateErpProduct
      ? await putResource(`/master-data/products/${editingRow.value.item_id}`, productPayload)
      : await postResource("/master-data/products", productPayload);
    const productItemId = product.item_id;
    const canUpdateExistingSpec = editingRow.value && Number(editingRow.value.item_id) === Number(productItemId);
    if (canUpdateExistingSpec && editingRow.value.bom?.bom_id) {
      await putResource(`/master-data/boms/${editingRow.value.bom.bom_id}`, buildBomPayload(productItemId));
    } else {
      await postResource("/master-data/boms", buildBomPayload(productItemId));
    }
    if (canUpdateExistingSpec && editingRow.value.route?.route_id) {
      await putResource(`/master-data/process-routes/${editingRow.value.route.route_id}`, buildRoutePayload(product, processRowsForSave));
    } else {
      await postResource("/master-data/process-routes", buildRoutePayload(product, processRowsForSave));
    }
    const miniappPayloads = buildMiniappProductPayloads(product, processRowsForSave);
    for (const payload of miniappPayloads) {
      await postResource("/master-data/miniapp-products?sync=false", payload);
    }
    await postResource("/master-data/miniapp-materials/sync?force=true", {});
    feedbackMessage.value = `产品需规 ${product.item_code} 已保存`;
    drawerOpen.value = false;
    await loadAll();
  } catch (error) {
    errorMessage.value = error.message || "产品需规保存失败";
  } finally {
    submitting.value = false;
  }
}

onMounted(async () => {
  await loadAll();
  hasLoaded.value = true;
});

onActivated(async () => {
  if (!hasLoaded.value) {
    return;
  }
  await fetchMasterData();
  void syncMiniappInBackground();
});
</script>

<style scoped>
.process-field-list {
  display: grid;
  gap: 10px;
  padding: 12px;
  border: 1px solid rgba(220, 223, 230, 0.9);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.78);
}

.process-field-header,
.process-field-row {
  display: grid;
  grid-template-columns: minmax(140px, 1.4fr) minmax(110px, 1fr) minmax(86px, 0.7fr) minmax(120px, 0.9fr);
  gap: 8px;
  align-items: center;
}

.process-field-header {
  color: #606266;
  font-size: 12px;
  font-weight: 700;
}

.process-field-row input {
  width: 100%;
  min-height: 34px;
  border: 1px solid #dcdfe6;
  border-radius: 8px;
  background: #ffffff;
  color: #303133;
  padding: 0 10px;
}

.inactive-spec-row {
  opacity: 0.56;
  filter: grayscale(0.35);
}

.stale-miniapp-process-row {
  background: rgba(144, 147, 153, 0.08);
}

.stale-miniapp-process-row .link-button {
  color: #909399;
}

.inactive-spec-row button:disabled {
  cursor: not-allowed;
}

@media (max-width: 900px) {
  .process-field-header {
    display: none;
  }

  .process-field-row {
    grid-template-columns: 1fr;
  }
}
</style>
