<script setup lang="tsx">
import { computed, ref } from 'vue';
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
import { jsonClone } from '@sa/utils';
import { fetchBatchDeleteMajor, fetchCreateMajor, fetchGetMajorList, fetchUpdateMajor } from '@/service/api/art/major';
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 MajorSearch from './modules/major-search.vue';

defineOptions({
  name: 'MajorList'
});

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

const searchParams = ref<Api.Art.MajorSearchParams>({
  pageNum: 1,
  pageSize: 10,
  majorName: null,
  educationLevel: null,
  majorIcon: null,
  schoolingYears: null,
  disciplinePrimary: null,
  disciplineSecondary: null,
  degreeAwarded: null,
  summary: null,
  trainingDirection: null,
  coreCourses: null,
  params: {}
});

type TableRow = Api.Art.Major & { tempKey?: string };
type Model = Api.Art.MajorOperateParams;
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 { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
  useNaivePaginatedTable({
    api: () => fetchGetMajorList(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: 'majorId',
      title: '专业主键ID',
      align: 'center',
      minWidth: 120
    },
    createEditableColumn('majorName', '专业名称'),
    createEditableColumn('educationLevel', '学历层次'),
    createEditableColumn('majorIcon', '专业图标'),
    createEditableColumn('schoolingYears', '学制(年)'),
    createEditableColumn('disciplinePrimary', '所属一级学科'),
    createEditableColumn('disciplineSecondary', '所属二级学科'),
    createEditableColumn('degreeAwarded', '授予学士学位'),
    createEditableColumn('summary', '专业概括', { textarea: true }),
    createEditableColumn('trainingDirection', '培养方向', { textarea: true }),
    createEditableColumn('coreCourses', '主要课程', { textarea: true }),
    createEditableColumn('remark', '备注', { textarea: true }),
    createOperateColumn()
  ];
}

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

function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
  return {
    key: 'operate',
    title: $t('common.operate'),
    align: 'center',
    fixed: 'right',
    width: 100,
    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:major:edit');
      const showDelete = hasAuth('art:major: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.majorId)}
            />
          ) : 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.majorId !== null && row.majorId !== undefined && row.majorId !== '') {
    return String(row.majorId);
  }
  return row.tempKey ?? '';
}

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

function createDefaultModel(): Model {
  return {
    majorId: null,
    majorName: '',
    educationLevel: '',
    majorIcon: '',
    schoolingYears: null,
    disciplinePrimary: '',
    disciplineSecondary: '',
    degreeAwarded: '',
    summary: '',
    trainingDirection: '',
    coreCourses: '',
    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: 'majorName', label: '专业名称' },
  { key: 'educationLevel', 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 {
    majorId,
    majorName,
    educationLevel,
    majorIcon,
    schoolingYears,
    disciplinePrimary,
    disciplineSecondary,
    degreeAwarded,
    summary,
    trainingDirection,
    coreCourses,
    remark
  } = editingModel.value;

  savingRowKey.value = editingRowKey.value;

  const requestResult =
    editingMode.value === 'add'
      ? await fetchCreateMajor({
          majorName,
          educationLevel,
          majorIcon,
          schoolingYears,
          disciplinePrimary,
          disciplineSecondary,
          degreeAwarded,
          summary,
          trainingDirection,
          coreCourses,
          remark
        })
      : await fetchUpdateMajor({
          majorId,
          majorName,
          educationLevel,
          majorIcon,
          schoolingYears,
          disciplinePrimary,
          disciplineSecondary,
          degreeAwarded,
          summary,
          trainingDirection,
          coreCourses,
          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 fetchBatchDeleteMajor(checkedRowKeys.value);
    if (error) return;
    window.$message?.success($t('common.deleteSuccess'));
    checkedRowKeys.value = [];
    await getData();
  });
}

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

function handleExport() {
  download('/art/major/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">
    <MajorSearch 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:major:add')"
          :show-delete="hasAuth('art:major:remove')"
          :show-export="hasAuth('art:major: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>
