<script setup lang="tsx">
import { computed, ref } from 'vue';
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
import { jsonClone } from '@sa/utils';
import {
  fetchBatchDeleteSchoolRecruitMajorHistory,
  fetchCreateSchoolRecruitMajorHistory,
  fetchGetSchoolRecruitMajorHistoryList,
  fetchUpdateSchoolRecruitMajorHistory
} from '@/service/api/art/school-recruit-major-history';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import InlineExpandTextarea from '@/components/custom/inline-expand-textarea.vue';
import SchoolRecruitMajorHistorySearch from './modules/school-recruit-major-history-search.vue';

defineOptions({
  name: 'SchoolRecruitMajorHistoryList'
});

const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();

const searchParams = ref<Api.Art.SchoolRecruitMajorHistorySearchParams>({
  pageNum: 1,
  pageSize: 10,
  recruitMajorId: null,
  schoolId: null,
  schoolCode: null,
  collegeCode: null,
  schoolName: null,
  majorId: null,
  majorCode: null,
  majorName: null,
  enrollCode: null,
  majorType: null,
  majorTypeSub: null,
  mainExamSubject: null,
  year: null,
  subjectType: null,
  batchName: null,
  admissionFormula: null,
  probabilityOperator: null,
  controlScore: null,
  admissionScore: null,
  planEnroll: null,
  filedAmount: null,
  admitAmount: null,
  firstChoiceAdmitAmount: null,
  minScoreDiff: null,
  tuitionFee: null,
  params: {}
});

type TableRow = Api.Art.SchoolRecruitMajorHistory & { tempKey?: string };
type Model = Api.Art.SchoolRecruitMajorHistoryOperateParams;
type EditableField = Extract<keyof Model, keyof TableRow>;

const editingModel = ref<Model>(createDefaultModel());
const editingSnapshot = ref<TableRow | null>(null);
const editingMode = ref<NaiveUI.TableOperateType | null>(null);
const editingRowKey = ref<string | null>(null);
const savingRowKey = ref<string | null>(null);
const tempRow = ref<TableRow | null>(null);
const checkedRowKeys = ref<CommonType.IdType[]>([]);

const editableColumns: Array<{ key: EditableField; title: string; textarea?: boolean; minWidth?: number }> = [
  { key: 'recruitMajorId', title: '对应招录专业ID' },
  { key: 'schoolId', title: '学校ID' },
  { key: 'schoolCode', title: '学校代码' },
  { key: 'collegeCode', title: '院校代码' },
  { key: 'schoolName', title: '学校名称', minWidth: 160 },
  { key: 'majorId', title: '专业ID' },
  { key: 'majorCode', title: '专业代码' },
  { key: 'majorName', title: '专业名称', minWidth: 160 },
  { key: 'enrollCode', title: '招生代码' },
  { key: 'majorType', title: '专业类型' },
  { key: 'majorTypeSub', title: '专业类别子级' },
  { key: 'mainExamSubject', title: '主考科目' },
  { key: 'year', title: '年份' },
  { key: 'subjectType', title: '科类(文/理)' },
  { key: 'batchName', title: '批次' },
  { key: 'admissionFormula', title: '录取方式', minWidth: 160 },
  { key: 'probabilityOperator', title: '录取概率规则运算符', minWidth: 160 },
  { key: 'controlScore', title: '省控线' },
  { key: 'admissionScore', title: '录取线' },
  { key: 'planEnroll', title: '招生人数' },
  { key: 'filedAmount', title: '实际投档人数' },
  { key: 'admitAmount', title: '录取数' },
  { key: 'firstChoiceAdmitAmount', title: '一志愿录取数', minWidth: 160 },
  { key: 'minScoreDiff', title: '最低分数差' },
  { key: 'tuitionFee', title: '学费(元/年)' },
  { key: 'remark', title: '备注', textarea: true, minWidth: 160 }
];

const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
  useNaivePaginatedTable({
    api: () => fetchGetSchoolRecruitMajorHistoryList(searchParams.value),
    transform: response => defaultTransform(response),
    onPaginationParamsChange: params => {
      searchParams.value.pageNum = params.page;
      searchParams.value.pageSize = params.pageSize;
    },
    columns: () => createColumns()
  });

const tableData = computed<TableRow[]>(() => {
  const rows = data.value as TableRow[];
  return tempRow.value ? [tempRow.value, ...rows] : rows;
});

function createColumns(): NaiveUI.TableColumn<TableRow>[] {
  return [
    {
      type: 'selection',
      align: 'center',
      width: 48
    },
    {
      key: 'index',
      title: $t('common.index'),
      align: 'center',
      width: 64,
      render: (_, index) => index + 1
    },
    {
      key: 'historyId',
      title: '历年录取数据ID',
      align: 'center',
      minWidth: 160
    },
    ...editableColumns.map(column => createEditableColumn(column)),
    createOperateColumn()
  ];
}

function createEditableColumn(column: { key: EditableField; title: string; textarea?: boolean; minWidth?: number }) {
  return {
    key: column.key,
    title: column.title,
    align: 'center',
    minWidth: column.minWidth ?? 140,
    render: (row: TableRow) => renderEditableCell(row, column.key, column)
  } satisfies NaiveUI.TableColumn<TableRow>;
}

function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
  return {
    key: 'operate',
    title: $t('common.operate'),
    align: 'center',
    fixed: 'right',
    width: 200,
    render: (row: TableRow) => {
      const rowKey = resolveRowKey(row);
      const editing = editingRowKey.value === rowKey;
      const saving = savingRowKey.value === rowKey;

      if (editing) {
        return (
          <NSpace size={8} justify="center">
            <NButton size="tiny" quaternary disabled={saving} onClick={handleCancelEdit}>
              {$t('common.cancel')}
            </NButton>
            <NButton size="tiny" type="primary" loading={saving} onClick={handleSaveRow}>
              {$t('common.save')}
            </NButton>
          </NSpace>
        );
      }

      const showEdit = hasAuth('art:schoolRecruitMajorHistory:edit');
      const showDelete = hasAuth('art:schoolRecruitMajorHistory:remove');

      return (
        <div class="flex-center gap-8px">
          {showEdit ? (
            <NButton size="tiny" text type="primary" onClick={() => handleEditRow(row)}>
              {$t('common.edit')}
            </NButton>
          ) : null}
          {showEdit && showDelete ? <NDivider vertical /> : null}
          {showDelete ? (
            <ButtonIcon
              text
              type="error"
              icon="material-symbols:delete-outline"
              tooltipContent={$t('common.delete')}
              popconfirmContent={$t('common.confirmDelete')}
              onPositiveClick={() => handleDelete(row.historyId)}
            />
          ) : null}
        </div>
      );
    }
  };
}

function getRowFieldValue(row: TableRow, field: EditableField) {
  return row[field as keyof TableRow];
}

function formatDisplayValue(value: unknown) {
  if (value === null || value === undefined || value === '') {
    return '-';
  }
  return typeof value === 'number' ? value : String(value);
}

function renderEditableCell(row: TableRow, field: EditableField, options?: { textarea?: boolean }) {
  if (!isEditingRow(row)) {
    return formatDisplayValue(getRowFieldValue(row, field));
  }

  const inputValue = editingModel.value[field];
  const resolvedValue = inputValue === null || inputValue === undefined ? '' : String(inputValue);

  if (options?.textarea) {
    return renderTextareaEditor(row, field, resolvedValue);
  }

  return (
    <NInput
      size="small"
      type={options?.textarea ? 'textarea' : 'text'}
      autosize={options?.textarea ? { minRows: 1, maxRows: 4 } : undefined}
      value={resolvedValue}
      onUpdateValue={value => updateEditingField(field, value)}
    />
  );
}

function renderTextareaEditor(row: TableRow, field: EditableField, value: string) {
  return <InlineExpandTextarea value={value} onUpdateValue={val => updateEditingField(field, val)} />;
}

function updateEditingField(field: EditableField, value: string | number | null) {
  (editingModel.value as Record<string, string | number | null>)[field] = value;
}

function resolveRowKey(row: TableRow) {
  if (row.historyId !== null && row.historyId !== undefined && row.historyId !== '') {
    return String(row.historyId);
  }
  return row.tempKey ?? '';
}

function isEditingRow(row: TableRow) {
  return editingRowKey.value !== null && editingRowKey.value === resolveRowKey(row);
}

function createDefaultModel(): Model {
  return {
    historyId: null,
    recruitMajorId: null,
    schoolId: null,
    schoolCode: '',
    collegeCode: '',
    schoolName: '',
    majorId: null,
    majorCode: '',
    majorName: '',
    enrollCode: '',
    majorType: '',
    majorTypeSub: '',
    mainExamSubject: '',
    year: null,
    subjectType: '',
    batchName: '',
    admissionFormula: '',
    probabilityOperator: '',
    controlScore: null,
    admissionScore: null,
    planEnroll: null,
    filedAmount: null,
    admitAmount: null,
    firstChoiceAdmitAmount: null,
    minScoreDiff: null,
    tuitionFee: null,
    remark: ''
  };
}

function createTempKey() {
  return `temp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
}

function ensureEditingGuard(action: () => void | Promise<void>) {
  const executeAction = () => Promise.resolve(action());

  if (!editingRowKey.value) {
    return executeAction();
  }

  if (!window.$dialog) {
    resetEditingState();
    return executeAction();
  }

  return new Promise<void>((resolve, reject) => {
    window.$dialog?.warning({
      title: '提示',
      content: '当前行尚未保存，确定放弃修改吗？',
      positiveText: '放弃',
      negativeText: '继续编辑',
      onPositiveClick: async () => {
        resetEditingState();
        try {
          await executeAction();
          resolve();
        } catch (error) {
          reject(error);
        }
      },
      onNegativeClick: () => resolve()
    });
  });
}

function resetEditingState() {
  editingModel.value = createDefaultModel();
  editingSnapshot.value = null;
  editingMode.value = null;
  editingRowKey.value = null;
  savingRowKey.value = null;
  tempRow.value = null;
}

async function handleAddRow() {
  await ensureEditingGuard(() => {
    editingMode.value = 'add';
    editingModel.value = createDefaultModel();
    editingSnapshot.value = null;
    const key = createTempKey();
    tempRow.value = { ...(editingModel.value as TableRow), tempKey: key };
    editingRowKey.value = key;
    checkedRowKeys.value = [];
  });
}

async function handleEditRow(row: TableRow) {
  await ensureEditingGuard(() => {
    editingMode.value = 'edit';
    tempRow.value = null;
    editingRowKey.value = resolveRowKey(row);
    editingSnapshot.value = jsonClone(row);
    editingModel.value = Object.assign(createDefaultModel(), jsonClone(row));
  });
}

function handleCancelEdit() {
  resetEditingState();
}

const requiredFields: Array<{ key: EditableField; label: string }> = [
  { key: 'recruitMajorId', label: '对应招录专业ID' },
  { key: 'schoolId', label: '学校ID' },
  { key: 'schoolCode', label: '学校代码' },
  { key: 'schoolName', label: '学校名称' },
  { key: 'majorCode', label: '专业代码' },
  { key: 'majorName', label: '专业名称' },
  { key: 'year', label: '年份' }
];

function validateModel() {
  for (const item of requiredFields) {
    const value = editingModel.value[item.key];
    if (value === null || value === undefined || value === '') {
      window.$message?.warning(`${item.label}不能为空`);
      return false;
    }
  }
  return true;
}

async function handleSaveRow() {
  if (!editingMode.value) return;
  if (!validateModel()) return;

  const {
    historyId,
    recruitMajorId,
    schoolId,
    schoolCode,
    collegeCode,
    schoolName,
    majorId,
    majorCode,
    majorName,
    enrollCode,
    majorType,
    majorTypeSub,
    mainExamSubject,
    year,
    subjectType,
    batchName,
    admissionFormula,
    probabilityOperator,
    controlScore,
    admissionScore,
    planEnroll,
    filedAmount,
    admitAmount,
    firstChoiceAdmitAmount,
    minScoreDiff,
    tuitionFee,
    remark
  } = editingModel.value;

  savingRowKey.value = editingRowKey.value;

  const requestResult =
    editingMode.value === 'add'
      ? await fetchCreateSchoolRecruitMajorHistory({
          recruitMajorId,
          schoolId,
          schoolCode,
          collegeCode,
          schoolName,
          majorId,
          majorCode,
          majorName,
          enrollCode,
          majorType,
          majorTypeSub,
          mainExamSubject,
          year,
          subjectType,
          batchName,
          admissionFormula,
          probabilityOperator,
          controlScore,
          admissionScore,
          planEnroll,
          filedAmount,
          admitAmount,
          firstChoiceAdmitAmount,
          minScoreDiff,
          tuitionFee,
          remark
        })
      : await fetchUpdateSchoolRecruitMajorHistory({
          historyId,
          recruitMajorId,
          schoolId,
          schoolCode,
          collegeCode,
          schoolName,
          majorId,
          majorCode,
          majorName,
          enrollCode,
          majorType,
          majorTypeSub,
          mainExamSubject,
          year,
          subjectType,
          batchName,
          admissionFormula,
          probabilityOperator,
          controlScore,
          admissionScore,
          planEnroll,
          filedAmount,
          admitAmount,
          firstChoiceAdmitAmount,
          minScoreDiff,
          tuitionFee,
          remark
        });

  savingRowKey.value = null;

  if (requestResult.error) {
    window.$message?.error(requestResult.error.message || '保存失败');
    if (editingMode.value === 'edit' && editingSnapshot.value) {
      editingModel.value = Object.assign(createDefaultModel(), jsonClone(editingSnapshot.value));
    }
    return;
  }

  window.$message?.success($t('common.updateSuccess'));
  resetEditingState();
  await getData();
}

async function handleBatchDelete() {
  await ensureEditingGuard(async () => {
    const { error } = await fetchBatchDeleteSchoolRecruitMajorHistory(checkedRowKeys.value);
    if (error) return;
    window.$message?.success($t('common.deleteSuccess'));
    checkedRowKeys.value = [];
    await getData();
  });
}

async function handleDelete(historyId: CommonType.IdType) {
  await ensureEditingGuard(async () => {
    const { error } = await fetchBatchDeleteSchoolRecruitMajorHistory([historyId]);
    if (error) return;
    window.$message?.success($t('common.deleteSuccess'));
    await getData();
  });
}

function handleExport() {
  download(
    '/art/schoolRecruitMajorHistory/export',
    searchParams.value,
    `院校招录专业历年录取数据_${new Date().getTime()}.xlsx`
  );
}

async function handleSearch() {
  await ensureEditingGuard(async () => {
    await getDataByPage();
  });
}

async function handleRefresh() {
  await ensureEditingGuard(getData);
}

function rowClassName(row: TableRow) {
  return isEditingRow(row) ? 'inline-edit-row' : '';
}
</script>

<template>
  <div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <SchoolRecruitMajorHistorySearch v-model:model="searchParams" @search="handleSearch" />
    <NCard title="院校招录专业历年录取数据列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
      <template #header-extra>
        <TableHeaderOperation
          v-model:columns="columnChecks"
          :disabled-delete="checkedRowKeys.length === 0"
          :loading="loading"
          :show-add="hasAuth('art:schoolRecruitMajorHistory:add')"
          :show-delete="hasAuth('art:schoolRecruitMajorHistory:remove')"
          :show-export="hasAuth('art:schoolRecruitMajorHistory:export')"
          @add="handleAddRow"
          @delete="handleBatchDelete"
          @export="handleExport"
          @refresh="handleRefresh"
        />
      </template>
      <NDataTable
        v-model:checked-row-keys="checkedRowKeys"
        :columns="columns"
        :data="tableData"
        size="small"
        :flex-height="!appStore.isMobile"
        :scroll-x="scrollX"
        :loading="loading"
        remote
        :row-key="resolveRowKey"
        :row-class-name="rowClassName"
        :pagination="mobilePagination"
        class="sm:h-full"
      />
    </NCard>
  </div>
</template>

<style scoped>
:deep(.inline-edit-row .n-data-table-td) {
  background-color: rgba(24, 160, 88, 0.08);
}
</style>
