<script setup lang="ts">
import type { FormInstance } from 'element-plus';

import { nextTick, ref, watch } from 'vue';

import { EasyFlowFormModal } from '@easyflow/common-ui';

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

import { api } from '#/api/request';
import { $t } from '#/locales';

const emit = defineEmits<{
  reload: [];
}>();

defineExpose({
  openDialog,
});

type ResourceType = '' | 'BOT' | 'KNOWLEDGE' | 'WORKFLOW';
type ActionType = '' | 'DELETE' | 'OFFLINE' | 'PUBLISH';
type AssigneeType = 'ROLE' | 'USER';
type ScopeType = 'CATEGORY' | 'DEPT';
type FlowStatus = 'DISABLED' | 'ENABLED';

interface SelectOption {
  id: number | string;
  label: string;
}

interface ScopeItem {
  id?: number | string;
  includeChildren: boolean;
  scopeType: ScopeType;
  scopeValue?: number | string;
}

interface StepItem {
  assigneeTargetCode?: string;
  assigneeTargetId?: number | string;
  assigneeTargetName?: string;
  assigneeType: AssigneeType;
  id?: number | string;
  stepName: string;
}

interface FlowFormModel {
  actionType: ActionType;
  id?: number | string;
  name: string;
  priority: number;
  remark: string;
  resourceType: ResourceType;
  scopes: ScopeItem[];
  status: FlowStatus;
  steps: StepItem[];
}

const RESOURCE_OPTIONS = [
  { label: $t('approval.resource.bot'), value: 'BOT' },
  { label: $t('approval.resource.workflow'), value: 'WORKFLOW' },
  { label: $t('approval.resource.knowledge'), value: 'KNOWLEDGE' },
];

const ACTION_OPTIONS = [
  { label: $t('approval.action.publish'), value: 'PUBLISH' },
  { label: $t('approval.action.offline'), value: 'OFFLINE' },
  { label: $t('approval.action.delete'), value: 'DELETE' },
];

const STATUS_OPTIONS = [
  { label: $t('approval.status.enabled'), value: 'ENABLED' },
  { label: $t('approval.status.disabled'), value: 'DISABLED' },
];

const SCOPE_TYPE_OPTIONS = [
  { label: $t('approval.scope.category'), value: 'CATEGORY' },
  { label: $t('approval.scope.dept'), value: 'DEPT' },
];

const ASSIGNEE_TYPE_OPTIONS: Array<{ label: string; value: AssigneeType }> = [
  { label: $t('approval.assignee.role'), value: 'ROLE' },
  { label: $t('approval.assignee.user'), value: 'USER' },
];

const saveForm = ref<FormInstance>();
const dialogVisible = ref(false);
const isAdd = ref(true);
const btnLoading = ref(false);
const categoryLoaded = ref(false);
const deptLoaded = ref(false);
const roleLoaded = ref(false);
const accountLoading = ref(false);
const deptTreeOptions = ref<any[]>([]);
const roleOptions = ref<SelectOption[]>([]);
const accountOptions = ref<SelectOption[]>([]);
const categoryOptions = ref<Record<Exclude<ResourceType, ''>, SelectOption[]>>({
  BOT: [],
  KNOWLEDGE: [],
  WORKFLOW: [],
});

const formModel = ref<FlowFormModel>(buildDefaultForm());

const rules = {
  actionType: [
    { required: true, message: $t('message.required'), trigger: 'change' },
  ],
  name: [{ required: true, message: $t('message.required'), trigger: 'blur' }],
  priority: [
    { required: true, message: $t('message.required'), trigger: 'change' },
  ],
  resourceType: [
    { required: true, message: $t('message.required'), trigger: 'change' },
  ],
  status: [
    { required: true, message: $t('message.required'), trigger: 'change' },
  ],
};

watch(
  () => formModel.value.resourceType,
  (resourceType) => {
    formModel.value.scopes = formModel.value.scopes.map((scope) => {
      if (scope.scopeType === 'CATEGORY') {
        return {
          ...scope,
          scopeValue: undefined,
        };
      }
      return scope;
    });
    if (resourceType) {
      void ensureCategoryOptions();
    }
  },
);

function buildDefaultForm(): FlowFormModel {
  return {
    actionType: 'PUBLISH',
    name: '',
    priority: 100,
    remark: '',
    resourceType: 'BOT',
    scopes: [],
    status: 'ENABLED',
    steps: [{ assigneeType: 'ROLE', stepName: '' }],
  };
}

async function openDialog(row: any = {}) {
  isAdd.value = !row?.id;
  formModel.value = buildDefaultForm();
  dialogVisible.value = true;
  await Promise.all([
    ensureCategoryOptions(),
    ensureDeptOptions(),
    ensureRoleOptions(),
  ]);
  if (!row?.id) {
    await nextTick();
    saveForm.value?.clearValidate();
    return;
  }
  btnLoading.value = true;
  try {
    const res = await api.get('/api/v1/approvalFlow/detail', {
      params: {
        id: row.id,
      },
    });
    if (res.errorCode !== 0) {
      return;
    }
    formModel.value = {
      actionType: res.data?.actionType || 'PUBLISH',
      id: res.data?.id,
      name: res.data?.name || '',
      priority: Number(res.data?.priority || 100),
      remark: res.data?.remark || '',
      resourceType: res.data?.resourceType || 'BOT',
      scopes: (res.data?.scopes || []).map((item: any) => ({
        id: item.id,
        includeChildren: item.includeChildren === 1,
        scopeType: item.scopeType,
        scopeValue: item.scopeValue,
      })),
      status: res.data?.status || 'ENABLED',
      steps: (res.data?.steps || []).map((item: any) => ({
        assigneeTargetCode: item.assigneeTargetCode,
        assigneeTargetId: item.assigneeTargetId,
        assigneeTargetName: item.assigneeTargetName,
        assigneeType: item.assigneeType || 'ROLE',
        id: item.id,
        stepName: item.stepName,
      })),
    };
    if (formModel.value.steps.length === 0) {
      formModel.value.steps = [{ assigneeType: 'ROLE', stepName: '' }];
    }
    for (const step of formModel.value.steps) {
      registerAccountOption(step);
    }
    if (formModel.value.steps.some((item) => item.assigneeType === 'USER')) {
      await searchAccountOptions('');
    }
  } finally {
    btnLoading.value = false;
    await nextTick();
    saveForm.value?.clearValidate();
  }
}

async function ensureCategoryOptions() {
  if (categoryLoaded.value) {
    return;
  }
  const [botRes, workflowRes, knowledgeRes] = await Promise.all([
    api.get('/api/v1/botCategory/list', {
      params: { sortKey: 'sortNo', sortType: 'asc' },
    }),
    api.get('/api/v1/workflowCategory/list', {
      params: { sortKey: 'sortNo', sortType: 'asc' },
    }),
    api.get('/api/v1/documentCollectionCategory/list', {
      params: { sortKey: 'sortNo', sortType: 'asc' },
    }),
  ]);
  categoryOptions.value = {
    BOT: normalizeCategoryOptions(botRes.data, 'categoryName'),
    KNOWLEDGE: normalizeCategoryOptions(knowledgeRes.data, 'categoryName'),
    WORKFLOW: normalizeCategoryOptions(workflowRes.data, 'categoryName'),
  };
  categoryLoaded.value = true;
}

async function ensureDeptOptions() {
  if (deptLoaded.value) {
    return;
  }
  const res = await api.get('/api/v1/sysDept/list', {
    params: {
      asTree: true,
      sortKey: 'sortNo',
      sortType: 'asc',
    },
  });
  deptTreeOptions.value = Array.isArray(res.data) ? res.data : [];
  deptLoaded.value = true;
}

async function ensureRoleOptions() {
  if (roleLoaded.value) {
    return;
  }
  const res = await api.get('/api/v1/approvalFlow/assigneeRoleOptions');
  roleOptions.value = normalizeSelectOptions(res.data);
  roleLoaded.value = true;
}

async function searchAccountOptions(keyword = '') {
  accountLoading.value = true;
  try {
    const res = await api.get('/api/v1/approvalFlow/assigneeAccountPage', {
      params: {
        keyword,
        pageNumber: 1,
        pageSize: 20,
      },
    });
    for (const item of normalizeSelectOptions(res?.data?.records)) {
      if (
        !accountOptions.value.some(
          (option) => String(option.id) === String(item.id),
        )
      ) {
        accountOptions.value.push(item);
      }
    }
  } finally {
    accountLoading.value = false;
  }
}

function normalizeCategoryOptions(data: any[] = [], labelKey: string) {
  return (Array.isArray(data) ? data : []).map((item) => ({
    id: item.id,
    label: item[labelKey],
  }));
}

function normalizeSelectOptions(data: any[] = []) {
  return (Array.isArray(data) ? data : []).map((item) => ({
    id: item.id,
    label: item.name || item.label || item.code || String(item.id),
  }));
}

function addScope() {
  formModel.value.scopes.push({
    includeChildren: false,
    scopeType: 'CATEGORY',
    scopeValue: undefined,
  });
}

function removeScope(index: number) {
  formModel.value.scopes.splice(index, 1);
}

function addStep() {
  formModel.value.steps.push({ assigneeType: 'ROLE', stepName: '' });
}

function removeStep(index: number) {
  if (formModel.value.steps.length <= 1) {
    return;
  }
  formModel.value.steps.splice(index, 1);
}

function getCategoryScopeOptions() {
  const resourceType = formModel.value.resourceType;
  if (!resourceType) {
    return [];
  }
  return categoryOptions.value[resourceType];
}

function handleAssigneeTypeChange(step: StepItem) {
  step.assigneeTargetId = undefined;
  step.assigneeTargetCode = '';
  step.assigneeTargetName = '';
  if (step.assigneeType === 'USER') {
    void searchAccountOptions('');
  }
}

function handleAssigneeTargetChange(step: StepItem) {
  const options =
    step.assigneeType === 'ROLE' ? roleOptions.value : accountOptions.value;
  const selected = options.find(
    (item) => String(item.id) === String(step.assigneeTargetId),
  );
  step.assigneeTargetName = selected?.label || '';
}

function registerAccountOption(step?: StepItem) {
  if (!step?.assigneeTargetId || !step.assigneeTargetName) {
    return;
  }
  if (
    accountOptions.value.some(
      (item) => String(item.id) === String(step.assigneeTargetId),
    )
  ) {
    return;
  }
  accountOptions.value.push({
    id: step.assigneeTargetId,
    label: step.assigneeTargetName,
  });
}

async function save() {
  saveForm.value?.validate(async (valid) => {
    if (!valid) {
      return;
    }
    btnLoading.value = true;
    try {
      const steps = formModel.value.steps
        .map((step, index) => ({
          assigneeTargetId: step.assigneeTargetId,
          assigneeType: step.assigneeType,
          id: step.id,
          stepName: step.stepName?.trim(),
          stepNo: index + 1,
        }))
        .filter((step) => step.stepName);
      if (steps.length === 0) {
        ElMessage.warning($t('approval.message.needStep'));
        btnLoading.value = false;
        return;
      }
      if (steps.some((step) => !step.assigneeType || !step.assigneeTargetId)) {
        ElMessage.warning($t('approval.message.needStepAssignee'));
        btnLoading.value = false;
        return;
      }

      const scopes = formModel.value.scopes
        .filter((scope) => scope.scopeValue && scope.scopeType)
        .map((scope) => ({
          id: scope.id,
          includeChildren: scope.includeChildren ? 1 : 0,
          scopeType: scope.scopeType,
          scopeValue: scope.scopeValue,
        }));

      const payload = {
        actionType: formModel.value.actionType,
        id: formModel.value.id,
        name: formModel.value.name.trim(),
        priority: formModel.value.priority,
        remark: formModel.value.remark.trim(),
        resourceType: formModel.value.resourceType,
        scopes,
        status: formModel.value.status,
        steps,
      };

      const url = isAdd.value
        ? '/api/v1/approvalFlow/save'
        : '/api/v1/approvalFlow/update';
      const res = await api.post(url, payload);
      if (res.errorCode !== 0) {
        btnLoading.value = false;
        return;
      }
      ElMessage.success($t('approval.message.saveSuccess'));
      emit('reload');
      closeDialog();
    } finally {
      btnLoading.value = false;
    }
  });
}

function closeDialog() {
  formModel.value = buildDefaultForm();
  dialogVisible.value = false;
  saveForm.value?.resetFields();
}
</script>

<template>
  <EasyFlowFormModal
    v-model:open="dialogVisible"
    :title="
      isAdd ? $t('approval.action.addFlow') : $t('approval.action.editFlow')
    "
    :before-close="closeDialog"
    :confirm-loading="btnLoading"
    :submitting="btnLoading"
    :confirm-text="$t('button.save')"
    width="xl"
    @confirm="save"
  >
    <ElForm
      ref="saveForm"
      :model="formModel"
      :rules="rules"
      label-position="top"
      class="easyflow-modal-form"
    >
      <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
        <ElFormItem prop="name" :label="$t('approval.fields.flowName')">
          <ElInput v-model.trim="formModel.name" maxlength="128" />
        </ElFormItem>
        <ElFormItem prop="priority" :label="$t('approval.fields.priority')">
          <ElInputNumber
            v-model="formModel.priority"
            :min="0"
            :step="10"
            class="!w-full"
          />
        </ElFormItem>
        <ElFormItem
          prop="resourceType"
          :label="$t('approval.fields.resourceType')"
        >
          <ElSelect v-model="formModel.resourceType">
            <ElOption
              v-for="item in RESOURCE_OPTIONS"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </ElSelect>
        </ElFormItem>
        <ElFormItem prop="actionType" :label="$t('approval.fields.actionType')">
          <ElSelect v-model="formModel.actionType">
            <ElOption
              v-for="item in ACTION_OPTIONS"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </ElSelect>
        </ElFormItem>
        <ElFormItem prop="status" :label="$t('approval.fields.status')">
          <ElSelect v-model="formModel.status">
            <ElOption
              v-for="item in STATUS_OPTIONS"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </ElSelect>
        </ElFormItem>
        <ElFormItem prop="remark" :label="$t('approval.fields.remark')">
          <ElInput v-model.trim="formModel.remark" maxlength="500" />
        </ElFormItem>
      </div>

      <ElDivider>{{ $t('approval.section.scope') }}</ElDivider>
      <div class="approval-flow-modal__section">
        <div class="approval-flow-modal__section-meta">
          <p>{{ $t('approval.helper.scope') }}</p>
          <ElButton text type="primary" class="w-fit" @click="addScope">
            {{ $t('approval.action.addScope') }}
          </ElButton>
        </div>

        <div class="approval-flow-modal__scope-list">
          <div
            v-if="formModel.scopes.length === 0"
            class="approval-flow-modal__scope-empty"
          >
            {{ $t('approval.helper.scopeEmpty') }}
          </div>

          <div
            v-for="(scope, index) in formModel.scopes"
            :key="`scope-${index}`"
            class="approval-flow-modal__scope-row"
          >
            <ElSelect
              v-model="scope.scopeType"
              class="approval-flow-modal__scope-control approval-flow-modal__scope-type"
            >
              <ElOption
                v-for="item in SCOPE_TYPE_OPTIONS"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </ElSelect>

            <ElSelect
              v-if="scope.scopeType === 'CATEGORY'"
              v-model="scope.scopeValue"
              class="approval-flow-modal__scope-control"
              filterable
              clearable
              :placeholder="$t('approval.placeholder.scopeValue')"
            >
              <ElOption
                v-for="item in getCategoryScopeOptions()"
                :key="item.id"
                :label="item.label"
                :value="item.id"
              />
            </ElSelect>

            <ElTreeSelect
              v-else
              v-model="scope.scopeValue"
              class="approval-flow-modal__scope-control"
              clearable
              check-strictly
              :data="deptTreeOptions"
              :props="{
                label: 'deptName',
                children: 'children',
                value: 'id',
              }"
              :placeholder="$t('approval.placeholder.scopeValue')"
            />

            <div class="approval-flow-modal__scope-switch">
              <span>{{ $t('approval.fields.includeChildren') }}</span>
              <ElSwitch v-model="scope.includeChildren" />
            </div>

            <ElButton text type="danger" @click="removeScope(index)">
              {{ $t('button.delete') }}
            </ElButton>
          </div>
        </div>
      </div>

      <ElDivider>{{ $t('approval.section.steps') }}</ElDivider>
      <div class="flex flex-col gap-3">
        <div
          v-for="(step, index) in formModel.steps"
          :key="`step-${index}`"
          class="grid grid-cols-1 gap-3 rounded-2xl border border-[hsl(var(--divider-faint)/0.66)] bg-[hsl(var(--surface-panel)/0.72)] p-4 md:grid-cols-[72px,minmax(0,1.1fr),148px,minmax(0,1fr),88px]"
        >
          <div
            class="flex items-center text-sm font-medium text-[hsl(var(--text-muted))]"
          >
            {{ $t('approval.fields.stepNo', { value: index + 1 }) }}
          </div>
          <ElInput
            v-model.trim="step.stepName"
            :placeholder="$t('approval.placeholder.stepName')"
          />
          <div class="approval-flow-modal__assignee-type">
            <button
              v-for="item in ASSIGNEE_TYPE_OPTIONS"
              :key="item.value"
              type="button"
              class="approval-flow-modal__assignee-type-item"
              :class="{
                'is-active': step.assigneeType === item.value,
              }"
              :aria-pressed="step.assigneeType === item.value"
              @click="
                step.assigneeType !== item.value &&
                ((step.assigneeType = item.value),
                handleAssigneeTypeChange(step))
              "
            >
              {{ item.label }}
            </button>
          </div>
          <ElSelect
            v-if="step.assigneeType === 'ROLE'"
            v-model="step.assigneeTargetId"
            filterable
            clearable
            :placeholder="$t('approval.placeholder.assigneeTarget')"
            @change="handleAssigneeTargetChange(step)"
          >
            <ElOption
              v-for="item in roleOptions"
              :key="item.id"
              :label="item.label"
              :value="item.id"
            />
          </ElSelect>
          <ElSelect
            v-else
            v-model="step.assigneeTargetId"
            filterable
            clearable
            remote
            reserve-keyword
            :loading="accountLoading"
            :placeholder="$t('approval.placeholder.assigneeTarget')"
            :remote-method="searchAccountOptions"
            @change="handleAssigneeTargetChange(step)"
            @visible-change="(visible) => visible && searchAccountOptions('')"
          >
            <ElOption
              v-for="item in accountOptions"
              :key="item.id"
              :label="item.label"
              :value="item.id"
            />
          </ElSelect>
          <ElButton
            text
            type="danger"
            :disabled="formModel.steps.length <= 1"
            @click="removeStep(index)"
          >
            {{ $t('button.delete') }}
          </ElButton>
        </div>

        <ElButton text type="primary" class="w-fit" @click="addStep">
          {{ $t('approval.action.addStep') }}
        </ElButton>
      </div>
    </ElForm>
  </EasyFlowFormModal>
</template>

<style scoped>
.approval-flow-modal__section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.approval-flow-modal__section-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.approval-flow-modal__section-meta p {
  margin: 0;
  color: hsl(var(--text-muted));
  font-size: 13px;
  line-height: 1.5;
}

.approval-flow-modal__scope-list {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.approval-flow-modal__scope-empty {
  padding: 4px 0 2px;
  color: hsl(var(--text-muted));
  font-size: 13px;
}

.approval-flow-modal__scope-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  padding: 10px 0;
}

.approval-flow-modal__scope-row + .approval-flow-modal__scope-row {
  border-top: 1px solid hsl(var(--divider-faint) / 0.72);
}

.approval-flow-modal__scope-control {
  width: 100%;
}

.approval-flow-modal__scope-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  color: hsl(var(--text-muted));
  font-size: 13px;
}

.approval-flow-modal__assignee-type {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  min-height: 40px;
  padding: 4px;
  border: 1px solid hsl(var(--divider-faint) / 0.72);
  border-radius: 14px;
  background: hsl(var(--surface-contrast-soft) / 0.82);
}

.approval-flow-modal__assignee-type-item {
  min-width: 64px;
  height: 32px;
  padding: 0 14px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: hsl(var(--text-muted));
  font-size: 13px;
  font-weight: 600;
  line-height: 32px;
  transition:
    background-color 0.18s ease,
    color 0.18s ease,
    box-shadow 0.18s ease;
  cursor: pointer;
}

.approval-flow-modal__assignee-type-item:hover {
  color: hsl(var(--foreground));
}

.approval-flow-modal__assignee-type-item:focus-visible {
  outline: 2px solid hsl(var(--primary) / 0.28);
  outline-offset: 1px;
}

.approval-flow-modal__assignee-type-item.is-active {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.12);
  box-shadow: inset 0 0 0 1px hsl(var(--primary) / 0.14);
}

@media (min-width: 768px) {
  .approval-flow-modal__scope-row {
    grid-template-columns: 148px minmax(0, 1fr) 128px 72px;
    align-items: center;
  }
}
</style>
