<template>
  <div class="table-box table-management">
    <!-- 筛选（与原先一致：搜索/重置后再请求列表） -->
    <div class="filter-bar">
      <div class="filter-row">
        <span class="filter-label">位置</span>
        <el-select v-model="filterLocation" placeholder="请选择" clearable style="width: 200px">
          <el-option v-for="item in categoryOptions" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
        <el-button type="primary" @click="handleSearch"> 搜索 </el-button>
        <el-button @click="handleReset"> 重置 </el-button>
      </div>
    </div>

    <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :init-param="initParam">
      <template #tableHeader>
        <div class="table-header">
          <el-button type="primary" @click="handleNew"> 新建 </el-button>
        </div>
      </template>
      <template #operation="scope">
        <el-button link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
        <el-button link type="primary" @click="handleDelete(scope.row)"> 删除 </el-button>
      </template>
    </ProTable>

    <!-- 新建/编辑弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      class="table-management-dialog"
      :title="editId == null ? '新建桌位' : '编辑桌位'"
      width="480px"
      append-to-body
      destroy-on-close
      @close="onDialogClose"
    >
      <el-form
        ref="formRef"
        class="table-dialog-form"
        :model="form"
        :rules="formRules"
        label-width="80px"
        v-loading="detailLoading"
        require-asterisk-position="right"
      >
        <el-form-item label="选择分类" prop="categoryId">
          <el-select v-model="form.categoryId" placeholder="请选择分类" clearable>
            <el-option v-for="item in categoryOptions" :key="item.id" :label="item.name" :value="item.id" />
          </el-select>
        </el-form-item>

        <!-- 新建：可添加多组桌号/座位数（表单项与编辑态一致：作为 el-form 直接子节点，避免包一层 div 导致输入区宽度异常） -->
        <template v-if="editId == null">
          <div class="add-table-toolbar">
            <el-button type="primary" link @click="addTableRow"> 添加 </el-button>
          </div>
          <template v-for="(row, index) in form.tableRows" :key="index">
            <el-form-item label="桌号" :prop="'tableRows.' + index + '.tableNo'" :rules="tableNoFieldRules">
              <el-input
                :model-value="row.tableNo"
                placeholder="如 A01"
                maxlength="3"
                @update:model-value="(val: string) => onTableNumberInput(row, val)"
              />
            </el-form-item>
            <el-form-item label="座位数" :prop="'tableRows.' + index + '.seatCount'" :rules="seatCountFieldRulesRow">
              <el-input
                :model-value="row.seatCount"
                placeholder="请输入"
                maxlength="2"
                inputmode="numeric"
                @update:model-value="(val: string) => onSeatCountInput(row, val)"
              />
            </el-form-item>
            <div v-if="form.tableRows.length > 1" class="table-row-delete">
              <el-button type="danger" link @click="removeTableRow(index)"> 删除 </el-button>
            </div>
            <div v-if="index < form.tableRows.length - 1" class="table-row-sep" aria-hidden="true" />
          </template>
        </template>

        <!-- 编辑：单条 -->
        <template v-else>
          <el-form-item label="桌号" prop="tableNo">
            <el-input
              :model-value="form.tableNo"
              placeholder="如 A01"
              clearable
              maxlength="3"
              @update:model-value="onTableNumberInputEdit"
            />
          </el-form-item>
          <el-form-item label="座位数" prop="seatCount">
            <el-input
              :model-value="seatCountEditDisplay"
              placeholder="请输入"
              maxlength="2"
              inputmode="numeric"
              @update:model-value="onSeatCountInputEdit"
            />
          </el-form-item>
        </template>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false"> 取消 </el-button>
        <el-button type="primary" :loading="submitLoading" @click="submitForm"> 确定 </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import type { FormInstance, FormRules, FormItemRule } from "element-plus";
import ProTable from "@/components/ProTable/index.vue";
import type { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
import {
  scheduleList,
  tableList,
  tableDetail,
  tableAdd,
  tableEdit,
  tableDel,
  getTableHasReservation
} from "@/api/modules/scheduledService";
import { localGet } from "@/utils";

export interface TableRow {
  id: number | string;
  _seq?: number;
  tableNo: string;
  /** 列表接口返回的座位数字段 */
  seatingCapacity: number;
  categoryId?: number | string;
  locationName?: string;
}

const proTable = ref<ProTableInstance>();

const columns: ColumnProps<TableRow>[] = [
  { prop: "_seq", label: "序号", width: 60, align: "center" },
  { prop: "tableNo", label: "桌号" },
  { prop: "seatingCapacity", label: "座位数", align: "center" },
  { prop: "locationName", label: "位置" },
  { prop: "operation", label: "操作", fixed: "right", width: 140, align: "center" }
];

const initParam = reactive({
  storeId: localGet("geeker-user")?.userInfo?.storeId ?? localGet("createdId") ?? ""
});

/** 列表请求时使用的位置筛选（仅点击「搜索」后生效） */
const listFilterCategoryId = ref<number | string | undefined>(undefined);
const filterLocation = ref<number | string | undefined>(undefined);
const categoryOptions = ref<{ id: number | string; name: string }[]>([]);

function getStoreId(): number | string | null {
  return localGet("geeker-user")?.userInfo?.storeId ?? localGet("createdId") ?? null;
}

/** 接口返回 true 表示该桌有预定，不可编辑/删除 */
function parseHasReservationResponse(res: any): boolean {
  const body = res?.data ?? res;
  const val = body?.data !== undefined ? body.data : body;
  if (typeof val === "boolean") return val;
  if (val != null && typeof val === "object" && "hasReservation" in val) {
    return Boolean((val as { hasReservation?: boolean }).hasReservation);
  }
  return Boolean(val);
}

async function tableHasReservation(tableId: number | string, storeId: number | string): Promise<boolean> {
  const res: any = await getTableHasReservation({ tableId: tableId, storeId: getStoreId() });
  return parseHasReservationResponse(res);
}

async function loadCategories() {
  const storeId = getStoreId();
  if (!storeId) {
    categoryOptions.value = [];
    return;
  }
  try {
    const res: any = await scheduleList({ storeId: Number(storeId), pageNum: 1, pageSize: 500 });
    const raw = res?.data?.records ?? res?.data ?? res?.list ?? res ?? [];
    const arr = Array.isArray(raw) ? raw : [];
    categoryOptions.value = arr.map((item: any) => ({
      id: item.id,
      name: item.categoryName ?? item.name ?? ""
    }));
  } catch {
    categoryOptions.value = [];
  }
}

function mapTableRow(item: any, categoryMap: Record<string, string>): TableRow {
  const cap = item.seatingCapacity ?? item.seatCount ?? item.seats;
  return {
    id: item.id,
    tableNo: item.tableNo ?? item.tableNumber ?? item.deskNo ?? "",
    seatingCapacity: cap != null && cap !== "" ? Number(cap) || 0 : 0,
    categoryId: item.categoryId ?? item.category_id,
    locationName: item.locationName ?? item.categoryName ?? categoryMap[String(item.categoryId ?? item.category_id)] ?? ""
  };
}

/** ProTable / useTable 约定：返回 { data: { list, total } } */
async function getTableList(params: any) {
  const storeId = params.storeId ?? initParam.storeId;
  if (!storeId) {
    return { data: { list: [] as TableRow[], total: 0 } };
  }
  if (categoryOptions.value.length === 0) {
    await loadCategories();
  }
  const pageNum = params.pageNum ?? 1;
  const pageSize = params.pageSize ?? 10;
  const categoryMap = Object.fromEntries(categoryOptions.value.map(c => [String(c.id), c.name]));
  const req: {
    storeId: number;
    pageNum: number;
    pageSize: number;
    categoryId?: number | string;
  } = {
    storeId: Number(storeId),
    pageNum,
    pageSize
  };
  if (listFilterCategoryId.value != null && listFilterCategoryId.value !== "") {
    req.categoryId = listFilterCategoryId.value;
  }
  try {
    const res: any = await tableList(req);
    const body = res?.data ?? res;
    const inner = body?.data ?? body;
    const raw = inner?.records ?? inner?.list ?? (Array.isArray(inner) ? inner : []);
    const arr = Array.isArray(raw) ? raw : [];
    const total = inner?.total ?? body?.total ?? inner?.totalCount ?? 0;
    const list = arr.map((item: any, i: number) => ({
      ...mapTableRow(item, categoryMap),
      _seq: (pageNum - 1) * pageSize + i + 1
    }));
    return { data: { list, total: Number(total) || 0 } };
  } catch (e: any) {
    ElMessage.error(e?.message || "加载失败");
    return { data: { list: [] as TableRow[], total: 0 } };
  }
}

function handleSearch() {
  listFilterCategoryId.value = filterLocation.value;
  proTable.value?.getTableList();
}

function handleReset() {
  filterLocation.value = undefined;
  listFilterCategoryId.value = undefined;
  proTable.value?.getTableList();
}

const dialogVisible = ref(false);
const editId = ref<number | string | null>(null);
const submitLoading = ref(false);
const detailLoading = ref(false);
const formRef = ref<FormInstance>();

const form = reactive<{
  tableNo: string;
  seatCount: number;
  categoryId: number | string | undefined;
  /** 与 App addTableNumber 一致：座位为字符串便于输入过滤 */
  tableRows: { tableNo: string; seatCount: string }[];
}>({
  tableNo: "",
  seatCount: 0,
  categoryId: undefined,
  tableRows: [{ tableNo: "", seatCount: "" }]
});

/** 与 group_merchant/pages/scheduledService/addTableNumber.vue 一致 */
const TABLE_NUMBER_REG = /^[A-Z][0-9]{2}$/;

/** 勿在 el-form-item 上写 required：会注入无 message 的必填规则，嵌套 prop 会报英文「tableRows.0.xxx is required」 */
const tableNoFieldRules: FormItemRule[] = [
  { required: true, message: "请输入桌号", trigger: ["blur", "change"] },
  {
    validator: (_rule, value, callback) => {
      const num = String(value ?? "").trim();
      if (!num) {
        callback();
        return;
      }
      if (!TABLE_NUMBER_REG.test(num)) {
        callback(new Error("桌号格式为：大写字母+2位数字，如 A01"));
        return;
      }
      callback();
    },
    trigger: "blur"
  }
];

const seatCountFieldRulesRow: FormItemRule[] = [
  { required: true, message: "请输入座位数", trigger: ["blur", "change"] },
  {
    validator: (_rule, value, callback) => {
      const seat = String(value ?? "").trim();
      if (!seat) {
        callback();
        return;
      }
      const n = parseInt(seat, 10);
      if (isNaN(n) || n < 1 || n > 99) {
        callback(new Error("请输入1-99的整数"));
        return;
      }
      callback();
    },
    trigger: "blur"
  }
];

const seatCountFieldRulesEdit: FormItemRule[] = [
  {
    validator: (_rule, value, callback) => {
      const n = Number(value);
      if (value === undefined || value === null || value === "" || Number.isNaN(n) || n === 0) {
        callback(new Error("请输入座位数"));
        return;
      }
      if (n < 1 || n > 99) {
        callback(new Error("请输入1-99的整数"));
        return;
      }
      callback();
    },
    trigger: "blur"
  }
];

/** 编辑座位展示：0 视为空（与 App 输入行为一致） */
const seatCountEditDisplay = computed(() => {
  const s = form.seatCount;
  if (s === 0 || s === undefined || s === null || Number.isNaN(Number(s))) return "";
  return String(s);
});

function onTableNumberInput(row: { tableNo: string }, val: string) {
  let v = String(val ?? "");
  v = v.toUpperCase().replace(/[^A-Z0-9]/g, "");
  const letter = /[A-Z]/.test(v[0]) ? v[0] : "";
  const digits = v
    .slice(letter ? 1 : 0)
    .replace(/[^0-9]/g, "")
    .slice(0, 2);
  row.tableNo = letter + digits;
}

function onSeatCountInput(row: { seatCount: string }, val: string) {
  let v = String(val ?? "")
    .replace(/\D/g, "")
    .slice(0, 2);
  const num = parseInt(v, 10);
  if (v !== "" && !isNaN(num) && num > 99) v = "99";
  row.seatCount = v;
}

function onTableNumberInputEdit(val: string) {
  let v = String(val ?? "");
  v = v.toUpperCase().replace(/[^A-Z0-9]/g, "");
  const letter = /[A-Z]/.test(v[0]) ? v[0] : "";
  const digits = v
    .slice(letter ? 1 : 0)
    .replace(/[^0-9]/g, "")
    .slice(0, 2);
  form.tableNo = letter + digits;
}

function onSeatCountInputEdit(val: string) {
  let v = String(val ?? "")
    .replace(/\D/g, "")
    .slice(0, 2);
  const num = parseInt(v, 10);
  if (v !== "" && !isNaN(num) && num > 99) v = "99";
  if (v === "") {
    form.seatCount = 0;
  } else {
    const n = parseInt(v, 10);
    form.seatCount = isNaN(n) ? 0 : n;
  }
}

/** 新建只校验分类 + 各行（行规则在 el-form-item 上）；编辑校验分类 + 单条桌号/座位数 */
const formRules = computed<FormRules>(() => {
  const base: FormRules = {
    categoryId: [{ required: true, message: "请选择分类", trigger: "change" }]
  };
  if (editId.value != null) {
    base.tableNo = tableNoFieldRules;
    base.seatCount = seatCountFieldRulesEdit;
  }
  return base;
});

function addTableRow() {
  form.tableRows.push({ tableNo: "", seatCount: "" });
}

function removeTableRow(index: number) {
  if (form.tableRows.length <= 1) return;
  form.tableRows.splice(index, 1);
}

function handleNew() {
  editId.value = null;
  form.tableNo = "";
  form.seatCount = 0;
  form.categoryId = undefined;
  form.tableRows = [{ tableNo: "", seatCount: "" }];
  dialogVisible.value = true;
}

async function handleEdit(row: TableRow) {
  try {
    const has = await tableHasReservation(row.id);
    if (has) {
      ElMessage.warning("此分类所属桌号有预订信息，不可修改");
      return;
    }
  } catch (e: any) {
    ElMessage.error(e?.message || "校验失败，请稍后重试");
    return;
  }
  editId.value = row.id;
  form.tableRows = [{ tableNo: "", seatCount: "" }];
  dialogVisible.value = true;
  detailLoading.value = true;
  try {
    const res: any = await tableDetail({ id: row.id });
    const d = res?.data ?? res ?? {};
    const categoryId = d.categoryId ?? d.classifyId ?? undefined;
    form.categoryId = categoryId != null ? categoryId : (row.categoryId ?? undefined);
    form.tableNo = d.tableNumber ?? d.tableNo ?? row.tableNo ?? "";
    form.seatCount = d.seatingCapacity ?? d.seatCount ?? row.seatingCapacity ?? 0;
    if (typeof form.seatCount === "string") form.seatCount = parseInt(form.seatCount, 10) || 0;
    if (
      categoryId != null &&
      categoryOptions.value.length > 0 &&
      !categoryOptions.value.find(c => String(c.id) === String(categoryId))
    ) {
      await loadCategories();
    }
  } catch (e: any) {
    ElMessage.error(e?.message || "获取详情失败");
    form.tableNo = row.tableNo;
    form.seatCount = row.seatingCapacity ?? 0;
    form.categoryId = row.categoryId ?? undefined;
  } finally {
    detailLoading.value = false;
  }
}

async function handleDelete(row: TableRow) {
  try {
    const has = await tableHasReservation(row.id);
    if (has) {
      ElMessage.warning("此分类所属桌号有预订信息，不可修改");
      return;
    }
  } catch (e: any) {
    ElMessage.error(e?.message || "校验失败，请稍后重试");
    return;
  }
  ElMessageBox.confirm(`是否确认删除？`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning"
  })
    .then(async () => {
      try {
        await tableDel({ id: row.id });
        ElMessage.success("删除成功");
        proTable.value?.getTableList();
      } catch (e: any) {
        ElMessage.error(e?.message || "删除失败");
      }
    })
    .catch(() => {});
}

function onDialogClose() {
  editId.value = null;
  form.tableNo = "";
  form.seatCount = 0;
  form.categoryId = undefined;
  form.tableRows = [{ tableNo: "", seatCount: "" }];
}

async function submitForm() {
  if (!formRef.value) return;
  try {
    await formRef.value.validate();
  } catch {
    return;
  }
  const storeId = getStoreId();
  if (!storeId) {
    ElMessage.warning("未找到门店信息");
    return;
  }
  submitLoading.value = true;
  try {
    const tableNumber = form.tableNo.trim();
    const seatingCapacity = Number(form.seatCount) || 0;
    if (editId.value != null) {
      await tableEdit({
        id: editId.value,
        storeId,
        categoryId: form.categoryId,
        tableNumber,
        seatingCapacity
      });
    } else {
      const tables = form.tableRows.map(r => ({
        tableNumber: r.tableNo.trim(),
        seatingCapacity: parseInt(String(r.seatCount).trim(), 10) || 0
      }));
      await tableAdd({
        storeId,
        categoryId: form.categoryId,
        tables
      });
    }
    ElMessage.success(editId.value == null ? "添加成功" : "修改成功");
    dialogVisible.value = false;
    onDialogClose();
    proTable.value?.getTableList();
  } catch (e: any) {
    ElMessage.error(e?.message || "保存失败");
  } finally {
    submitLoading.value = false;
  }
}

onMounted(() => {
  loadCategories();
});
</script>
<style lang="scss">
.table-management-dialog .el-dialog__body {
  box-sizing: border-box;
  height: 300px;
}
</style>
<style scoped lang="scss">
.table-management {
  padding: 0;
}
.filter-bar {
  padding: 0 4px;
  margin-bottom: 12px;
}
.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.filter-label {
  min-width: 32px;
  font-size: 14px;
  color: #606266;
}
.table-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px 0;
}
.add-table-toolbar {
  position: relative;
  bottom: 5px;
  margin: 0 0 8px 80px;
}
.table-row-sep {
  width: 100%;
  margin: 0 0 12px;
  border: none;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.table-row-delete {
  position: relative;
  bottom: 5px;
  margin: 0 0 4px 80px;
  text-align: right;
}
</style>

<!-- append-to-body 时弹窗挂到 body，需非 scoped 才能稳定命中 -->
<style lang="scss">
.table-management-dialog.el-dialog .el-dialog__body {
  box-sizing: border-box;
  max-height: min(420px, 65vh);
  overflow: hidden auto;
}

/* 弹窗内表单：与 .table-search 一致，控件占满标签右侧（scoped 对 teleport 不生效） */
.table-management-dialog .table-dialog-form {
  width: 100%;
  .el-form-item {
    width: 100%;
  }
  .el-form-item__content {
    flex: 1;
    min-width: 0;
  }
  .el-form-item__content > .el-input,
  .el-form-item__content > .el-select {
    width: 100%;
    max-width: 100%;
  }
  .el-input {
    width: 100%;
  }
  .el-select {
    width: 100%;
  }
}
</style>
