<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue';

import { $t } from '@easyflow/locales';

import {
  ElButton,
  ElForm,
  ElFormItem,
  ElInput,
  ElInputNumber,
  ElMessage,
  ElOption,
  ElSelect,
  ElSlider,
  ElSwitch,
} from 'element-plus';

import { api } from '#/api/request';
import { buildKnowledgePath } from '#/views/ai/documentCollection/share-path';
import SplitterDocPreview from '#/views/ai/documentCollection/SplitterDocPreview.vue';

interface SourceRange {
  end: number;
  start: number;
}

interface ChunkItem {
  answer?: string;
  charCount?: number;
  chunkId?: string;
  chunkType?: string;
  content?: string;
  headingPath?: string[];
  options?: Record<string, any>;
  partNo?: number;
  partTotal?: number;
  question?: string;
  sourceLabel?: string;
  sourceRanges?: SourceRange[];
  tokenEstimate?: number;
  warnings?: string[];
}

interface PreviewItem {
  analysis?: {
    normalizedContent?: string;
    recommendedStrategyLabel?: string;
  };
  chunks?: ChunkItem[];
  fileName: string;
  normalizedContent?: string;
  pageNo?: number;
  pageSize?: number;
  previewSessionId: string;
  strategyCode?: string;
  strategyLabel?: string;
  totalChunks?: number;
  totalWarnings?: number;
}

const props = defineProps({
  documentId: {
    type: String,
    required: true,
  },
  documentTitle: {
    type: String,
    default: '',
  },
  endpointPrefix: {
    type: String,
    default: '',
  },
  knowledgeId: {
    type: String,
    required: true,
  },
  requestClient: {
    type: Object as any,
    default: () => api,
  },
});

const emits = defineEmits(['cancel', 'started']);

const createDefaultFormState = () => ({
  chunkSize: 512,
  mdSplitterLevel: 2,
  overlapSize: 128,
  regex: '',
  retainRegexMatch: false,
  rowsPerChunk: 10,
  strategyCode: 'AUTO',
});

const formState = reactive(createDefaultFormState());
const previewItems = ref<PreviewItem[]>([]);
const currentPreviewSessionId = ref('');
const activeDocumentId = ref(props.documentId || '');
const previewError = ref('');
const previewLoading = ref(false);
const startLoading = ref(false);
const previewPageSize = 20;
let previewDebounceTimer: null | ReturnType<typeof setTimeout> = null;
let previewSequence = 0;

const strategyOptions = [
  {
    label: $t('documentCollection.splitterDoc.autoStrategy'),
    value: 'AUTO',
  },
  {
    label: $t('documentCollection.splitterDoc.markdownSection'),
    value: 'MARKDOWN_SECTION',
  },
  {
    label: $t('documentCollection.splitterDoc.outlineSection'),
    value: 'OUTLINE_SECTION',
  },
  {
    label: $t('documentCollection.splitterDoc.qaPair'),
    value: 'QA_PAIR',
  },
  {
    label: $t('documentCollection.splitterDoc.paragraphLength'),
    value: 'PARAGRAPH_LENGTH',
  },
  {
    label: $t('documentCollection.splitterDoc.customRegex'),
    value: 'CUSTOM_REGEX',
  },
];

const fileExt = computed(
  () =>
    String(props.documentTitle || '')
      .split('.')
      .pop()
      ?.toLowerCase() || '',
);

const isPptx = computed(() => fileExt.value === 'pptx');
const isXlsx = computed(() => fileExt.value === 'xlsx');
const isCsv = computed(() => fileExt.value === 'csv');
const isTabular = computed(() => isXlsx.value || isCsv.value);
const showStrategySelector = computed(() => !isPptx.value && !isTabular.value);

const mdLevels = [1, 2, 3, 4, 5, 6];

const showLengthSettings = (strategyCode?: string) =>
  ['AUTO', 'MARKDOWN_SECTION', 'OUTLINE_SECTION', 'PARAGRAPH_LENGTH'].includes(
    strategyCode || '',
  );

const showOverlapSettings = (strategyCode?: string) =>
  ['AUTO', 'PARAGRAPH_LENGTH'].includes(strategyCode || '');

const maxOverlapSize = computed(() =>
  Math.max(0, Number(formState.chunkSize || 0) - 1),
);

const clearPreviewTimer = () => {
  if (!previewDebounceTimer) {
    return;
  }
  clearTimeout(previewDebounceTimer);
  previewDebounceTimer = null;
};

const resetPreviewState = () => {
  previewItems.value = [];
  currentPreviewSessionId.value = '';
  previewError.value = '';
};

const buildStrategyConfig = () => {
  if (isPptx.value) {
    return {
      strategyCode: 'OFFICE_PPTX_PAGE',
    };
  }
  if (isTabular.value) {
    return {
      rowsPerChunk: formState.rowsPerChunk,
      strategyCode: isCsv.value ? 'TABLE_ROW' : 'OFFICE_XLSX_ROW_WINDOW',
    };
  }
  return {
    ...formState,
  };
};

const normalizeSourceRanges = (ranges?: SourceRange[]) =>
  Array.isArray(ranges)
    ? ranges.filter(
        (item) =>
          Number.isFinite(item?.start) &&
          Number.isFinite(item?.end) &&
          Number(item.end) > Number(item.start),
      )
    : [];

const normalizePreviewItems = (items: PreviewItem[]) =>
  (items || []).map((item) => ({
    ...item,
    normalizedContent:
      item.normalizedContent || item.analysis?.normalizedContent || '',
    strategyLabel:
      item.strategyLabel || item.analysis?.recommendedStrategyLabel || '',
    totalChunks:
      Number(item.totalChunks || 0) > 0
        ? item.totalChunks
        : (item.chunks || []).length,
    chunks: (item.chunks || []).map((chunk) => ({
      ...chunk,
      sourceRanges: normalizeSourceRanges(
        chunk.sourceRanges ||
          (Array.isArray(chunk.options?.sourceRanges)
            ? chunk.options?.sourceRanges
            : []),
      ),
    })),
  }));

const generatePreview = async () => {
  if (!activeDocumentId.value) {
    return;
  }
  const requestSequence = ++previewSequence;
  previewLoading.value = true;
  previewError.value = '';
  try {
    const res = await props.requestClient.post(
      buildKnowledgePath(
        props.endpointPrefix,
        '/api/v1/document/import/task/preview',
      ),
      {
        documentId: activeDocumentId.value,
        files: [
          {
            strategyConfig: buildStrategyConfig(),
          },
        ],
        knowledgeId: props.knowledgeId,
        pageNo: 1,
        pageSize: previewPageSize,
      },
    );
    if (requestSequence !== previewSequence) {
      return;
    }
    const items = normalizePreviewItems(
      (res.data?.items || []) as PreviewItem[],
    );
    previewItems.value = items;
    currentPreviewSessionId.value = items[0]?.previewSessionId || '';
    previewError.value = '';
  } catch (error: any) {
    if (requestSequence !== previewSequence) {
      return;
    }
    const message =
      error?.message || $t('documentCollection.importDoc.previewRequestFailed');
    previewItems.value = [];
    currentPreviewSessionId.value = '';
    previewError.value = message;
    ElMessage.error(message);
  } finally {
    if (requestSequence === previewSequence) {
      previewLoading.value = false;
    }
  }
};

const loadPreviewPage = async (pageNo: number) => {
  if (
    !activeDocumentId.value ||
    !currentPreviewSessionId.value ||
    previewLoading.value
  ) {
    return;
  }
  const requestSequence = ++previewSequence;
  previewLoading.value = true;
  previewError.value = '';
  try {
    const res = await props.requestClient.post(
      buildKnowledgePath(
        props.endpointPrefix,
        '/api/v1/document/import/task/preview',
      ),
      {
        documentId: activeDocumentId.value,
        knowledgeId: props.knowledgeId,
        pageNo,
        pageSize: previewPageSize,
        previewSessionId: currentPreviewSessionId.value,
      },
    );
    if (requestSequence !== previewSequence) {
      return;
    }
    previewItems.value = normalizePreviewItems(
      (res.data?.items || []) as PreviewItem[],
    );
  } catch (error: any) {
    if (requestSequence !== previewSequence) {
      return;
    }
    const message =
      error?.message || $t('documentCollection.importDoc.previewRequestFailed');
    previewError.value = message;
    ElMessage.error(message);
  } finally {
    if (requestSequence === previewSequence) {
      previewLoading.value = false;
    }
  }
};

const schedulePreviewGeneration = () => {
  if (!activeDocumentId.value) {
    return;
  }
  clearPreviewTimer();
  resetPreviewState();
  previewDebounceTimer = setTimeout(() => {
    previewDebounceTimer = null;
    void generatePreview();
  }, 320);
};

const handlePreviewSessionChange = (previewSessionId: string) => {
  currentPreviewSessionId.value = previewSessionId;
};

const handleCancel = () => {
  previewSequence += 1;
  clearPreviewTimer();
  previewLoading.value = false;
  emits('cancel');
};

const startIndex = async () => {
  if (!currentPreviewSessionId.value) {
    ElMessage.warning($t('documentCollection.importDoc.previewEmpty'));
    return;
  }
  startLoading.value = true;
  try {
    const res = await props.requestClient.post(
      buildKnowledgePath(
        props.endpointPrefix,
        '/api/v1/document/import/task/startIndex',
      ),
      {
        documentId: activeDocumentId.value,
        knowledgeId: props.knowledgeId,
        previewSessionId: currentPreviewSessionId.value,
      },
    );
    if (res.errorCode === 0) {
      ElMessage.success($t('documentCollection.importDoc.indexQueued'));
      emits('started');
    }
  } finally {
    startLoading.value = false;
  }
};

watch(
  () => props.documentId,
  (value) => {
    activeDocumentId.value = value || '';
    previewSequence += 1;
    clearPreviewTimer();
    Object.assign(formState, createDefaultFormState());
    if (isPptx.value) {
      formState.strategyCode = 'OFFICE_PPTX_PAGE';
    }
    if (isTabular.value) {
      formState.strategyCode = isCsv.value
        ? 'TABLE_ROW'
        : 'OFFICE_XLSX_ROW_WINDOW';
      formState.rowsPerChunk = 10;
    }
    resetPreviewState();
    if (activeDocumentId.value) {
      schedulePreviewGeneration();
    }
  },
  { immediate: true },
);

watch(
  () => formState.chunkSize,
  () => {
    if (formState.overlapSize > maxOverlapSize.value) {
      formState.overlapSize = maxOverlapSize.value;
    }
  },
);

watch(
  formState,
  () => {
    if (!activeDocumentId.value) {
      return;
    }
    schedulePreviewGeneration();
  },
  { deep: true },
);
</script>

<template>
  <div class="workbench">
    <ElForm :model="formState" label-position="top" class="workbench__form">
      <div class="workbench__form-grid">
        <ElFormItem
          v-if="showStrategySelector"
          :label="$t('documentCollection.importDoc.strategySelection')"
          class="workbench__form-full"
        >
          <ElSelect v-model="formState.strategyCode" class="w-full">
            <ElOption
              v-for="option in strategyOptions"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
          </ElSelect>
        </ElFormItem>

        <ElFormItem
          v-if="isTabular"
          label="每多少行分一块"
          class="workbench__form-full"
        >
          <ElInputNumber
            v-model="formState.rowsPerChunk"
            :min="1"
            :max="200"
            :step="1"
            class="workbench__rows-input"
          />
        </ElFormItem>

        <ElFormItem
          v-if="showLengthSettings(formState.strategyCode)"
          :label="$t('documentCollection.splitterDoc.chunkSize')"
          :class="
            showOverlapSettings(formState.strategyCode)
              ? ''
              : 'workbench__form-full'
          "
        >
          <ElSlider
            v-model="formState.chunkSize"
            :max="2048"
            :min="128"
            show-input
          />
        </ElFormItem>

        <ElFormItem
          v-if="showOverlapSettings(formState.strategyCode)"
          :label="$t('documentCollection.splitterDoc.overlapSize')"
        >
          <ElSlider
            v-model="formState.overlapSize"
            :max="Math.min(512, maxOverlapSize)"
            :min="0"
            show-input
          />
        </ElFormItem>

        <ElFormItem
          v-if="formState.strategyCode === 'MARKDOWN_SECTION'"
          :label="$t('documentCollection.splitterDoc.mdSplitterLevel')"
          class="workbench__form-full"
        >
          <ElSelect v-model="formState.mdSplitterLevel" class="w-full">
            <ElOption
              v-for="level in mdLevels"
              :key="level"
              :label="'#'.repeat(level)"
              :value="level"
            />
          </ElSelect>
        </ElFormItem>

        <ElFormItem
          v-if="formState.strategyCode === 'CUSTOM_REGEX'"
          :label="$t('documentCollection.splitterDoc.regex')"
          class="workbench__form-full"
        >
          <ElInput v-model="formState.regex" />
        </ElFormItem>

        <ElFormItem
          v-if="formState.strategyCode === 'CUSTOM_REGEX'"
          :label="$t('documentCollection.splitterDoc.regexMatchHandling')"
          class="workbench__form-full"
        >
          <ElSwitch
            v-model="formState.retainRegexMatch"
            :active-text="$t('documentCollection.splitterDoc.retainRegexMatch')"
            :inactive-text="
              $t('documentCollection.splitterDoc.discardRegexMatch')
            "
          />
        </ElFormItem>
      </div>
    </ElForm>

    <div class="workbench__actions">
      <ElButton :disabled="startLoading" @click="handleCancel">
        {{ $t('button.cancel') }}
      </ElButton>
      <ElButton
        type="primary"
        :disabled="previewLoading || !currentPreviewSessionId"
        :loading="startLoading"
        @click="startIndex"
      >
        {{ $t('button.startIndex') }}
      </ElButton>
    </div>

    <section class="workbench__content">
      <SplitterDocPreview
        :loading="previewLoading"
        :preview-items="previewItems"
        @page-change="loadPreviewPage"
        @preview-session-change="handlePreviewSessionChange"
      />
    </section>

    <div v-if="previewError" class="workbench__error">
      {{ previewError }}
    </div>
  </div>
</template>

<style scoped>
.workbench {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 100%;
  padding: 4px 4px 0;
}

.workbench__form {
  padding: 0;
  background: transparent;
}

.workbench__form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 18px;
}

.workbench__form-full {
  grid-column: 1 / -1;
}

.workbench__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.workbench__content {
  min-height: 620px;
}

.workbench__error {
  padding: 14px 16px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--el-color-danger-dark-2);
  background: var(--el-color-danger-light-9);
  background: color-mix(in srgb, var(--el-color-danger-light-9) 88%, white);
  border: 1px solid var(--el-color-danger-light-8);
  border: 1px solid color-mix(in srgb, var(--el-color-danger) 14%, white);
  border-radius: 16px;
}

:deep(.el-form-item__label) {
  padding-bottom: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--el-text-color-secondary);
}

:deep(.workbench__form .el-input__wrapper),
:deep(.workbench__form .el-select__wrapper) {
  box-shadow: 0 0 0 1px rgb(15 23 42 / 7%) inset;
}

.workbench__rows-input {
  width: 100%;
}

:deep(.workbench__form .el-slider__runway) {
  background: rgb(15 23 42 / 8%);
}

@media (max-width: 1180px) {
  .workbench__content {
    min-height: 520px;
  }
}

@media (max-width: 768px) {
  .workbench {
    padding: 0;
  }

  .workbench__header,
  .workbench__form-grid {
    grid-template-columns: 1fr;
  }

  .workbench__form,
  .workbench__content {
    min-height: auto;
  }
}

@supports not (color: color-mix(in srgb, red, blue)) {
  .workbench__error {
    border-color: var(--el-color-danger-light-8);
  }
}
</style>
