<script setup lang="ts">
/* cspell:ignore tryit */
import type { AgentInfo } from './types';

import type {
  ActionButton,
  CardPrimaryAction,
} from '#/components/page/CardList.vue';

import { computed, markRaw, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';

import { useAccess } from '@easyflow/access';
import { defaultAssistantAvatar } from '@easyflow/common-ui';

import {
  Check,
  Delete,
  Edit,
  Lock,
  OfficeBuilding,
  Plus,
  Promotion,
} from '@element-plus/icons-vue';
import { ElIcon, ElMessage, ElMessageBox, ElPopover } from 'element-plus';
import { tryit } from 'radash';

import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
import CardList from '#/components/page/CardList.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
import {
  buildListRouteFullPath,
  mergeListRouteStateQuery,
  parseListRouteState,
  watchListRouteState,
} from '#/composables/useListRouteState';
import { $t } from '#/locales';
import { withListReturnTo } from '#/router/list-return-context';
import AiResourceCornerMeta from '#/views/ai/shared/AiResourceCornerMeta.vue';
import {
  canAiResourceDelete,
  canAiResourceOffline,
  canAiResourcePublish,
  canAiResourceRepublish,
  isAiResourceApprovalPending,
  resolveAiResourceDisplayStatus,
} from '#/views/ai/shared/publish-status';

import { agentListRouteSchema } from './agent-list-route-state';
import {
  getAgentCategories,
  submitAgentDeleteApproval,
  submitAgentOfflineApproval,
  submitAgentPublishApproval,
  updateAgentVisibilityScope,
} from './api';

const route = useRoute();
const router = useRouter();
const initialListState = parseListRouteState(route.query, agentListRouteSchema);
const pageDataRef = ref();
const sideList = ref<any[]>([]);
const selectedCategoryId = ref(initialListState.categoryId);
const searchKeyword = ref(initialListState.keyword);
const currentPageNumber = ref(initialListState.pageNumber);
const currentPageSize = ref(initialListState.pageSize);
let initialCategoryChangePending = Boolean(initialListState.categoryId);
watchListRouteState(route, agentListRouteSchema, (state) => {
  if (state.categoryId !== selectedCategoryId.value) {
    initialCategoryChangePending = Boolean(state.categoryId);
  }
  selectedCategoryId.value = state.categoryId;
  searchKeyword.value = state.keyword;
  currentPageNumber.value = state.pageNumber;
  currentPageSize.value = state.pageSize;
  pageDataRef.value?.restoreState?.({
    pageNumber: state.pageNumber,
    pageSize: state.pageSize,
    queryParams: {
      categoryId: state.categoryId || undefined,
      keyword: state.keyword || undefined,
    },
  });
});
const AGENT_TAB_PAGE_KEY = '/ai/agents';
const DEFAULT_AGENT_TITLE = '未命名智能体';
type VisibilityScope = 'DEPT' | 'PRIVATE' | 'PUBLIC';

const { hasAccessByCodes } = useAccess();
const canManageAgent = computed(() => hasAccessByCodes(['/api/v1/agent/save']));
const updatingVisibilityScopeId = ref<null | number | string>(null);
const visibilityScopePopoverRefs = ref<Record<string, any>>({});
const visibilityScopeMeta = {
  PRIVATE: {
    description: '仅创建者可使用',
    icon: Lock,
    label: '个人',
    tone: 'private',
  },
  DEPT: {
    description: '本部门及下级部门可使用',
    icon: OfficeBuilding,
    label: '部门',
    tone: 'dept',
  },
  PUBLIC: {
    description: '租户内所有有聊天权限的用户可使用',
    icon: Promotion,
    label: '公开',
    tone: 'public',
  },
};
const visibilityScopeOptions = (
  ['PRIVATE', 'DEPT', 'PUBLIC'] as VisibilityScope[]
).map((value) => ({ value, ...visibilityScopeMeta[value] }));

const headerButtons = [
  {
    key: 'create',
    text: '创建智能体',
    icon: markRaw(Plus),
    type: 'primary',
    data: { action: 'create' },
    permission: '/api/v1/agent/save',
  },
];

const primaryAction: CardPrimaryAction = {
  icon: Edit,
  text: '编排',
  permission: '/api/v1/agent/update',
  onClick(row: AgentInfo) {
    router.push({
      path: `/ai/agents/designer/${row.id}`,
      query: {
        pageKey: AGENT_TAB_PAGE_KEY,
        navTitle: resolveNavTitle(row),
        ...withListReturnTo(currentListFullPath()),
      },
    });
  },
};

const actions: ActionButton[] = [
  {
    icon: Promotion,
    text: (row: AgentInfo) =>
      canAiResourceRepublish(row.displayPublishStatus, row.publishStatus)
        ? '重新发布'
        : '发布',
    permission: '/api/v1/agent/save',
    placement: 'inline',
    visible: (row: AgentInfo) =>
      canAiResourcePublish(row.displayPublishStatus, row.publishStatus) ||
      canAiResourceRepublish(row.displayPublishStatus, row.publishStatus),
    onClick: handlePublishAction,
  },
  {
    icon: Promotion,
    text: '下线',
    permission: '/api/v1/agent/save',
    placement: 'menu',
    visible: (row: AgentInfo) =>
      canAiResourceOffline(row.displayPublishStatus, row.publishStatus),
    onClick: handleOfflineAction,
  },
  {
    icon: Delete,
    text: '删除',
    permission: '/api/v1/agent/remove',
    placement: 'menu',
    tone: 'danger',
    visible: (row: AgentInfo) =>
      canAiResourceDelete(row.displayPublishStatus, row.publishStatus),
    onClick: handleDeleteAction,
  },
];

onMounted(() => {
  loadCategories();
});

function handleSearch(keyword: string) {
  searchKeyword.value = keyword;
  pageDataRef.value?.setQuery({
    categoryId: selectedCategoryId.value || undefined,
    keyword: keyword || undefined,
  });
}

function handleButtonClick(payload: any) {
  if (payload?.key === 'create' || payload?.data?.action === 'create') {
    router.push({
      path: '/ai/agents/designer/new',
      query: {
        pageKey: AGENT_TAB_PAGE_KEY,
        navTitle: DEFAULT_AGENT_TITLE,
        ...withListReturnTo(currentListFullPath()),
      },
    });
  }
}

function resolveNavTitle(row: AgentInfo) {
  return String(row.name || '').trim() || DEFAULT_AGENT_TITLE;
}

function resolveVisibilityScopeMeta(scope?: string) {
  return (
    visibilityScopeMeta[scope as VisibilityScope] || visibilityScopeMeta.PRIVATE
  );
}

function setVisibilityScopePopoverRef(id: number | string, el: any) {
  const cacheKey = String(id);
  if (el) {
    visibilityScopePopoverRefs.value[cacheKey] = el;
    return;
  }
  delete visibilityScopePopoverRefs.value[cacheKey];
}

function closeVisibilityScopePopover(id: number | string) {
  visibilityScopePopoverRefs.value[String(id)]?.hide?.();
}

async function updateVisibilityScope(
  row: AgentInfo,
  visibilityScope: VisibilityScope,
) {
  if (!row?.id) {
    return;
  }
  if (
    !canManageAgent.value ||
    updatingVisibilityScopeId.value === row.id ||
    row.visibilityScope === visibilityScope
  ) {
    closeVisibilityScopePopover(row.id);
    return;
  }
  updatingVisibilityScopeId.value = row.id;
  try {
    const res = await updateAgentVisibilityScope(row.id, visibilityScope);
    if (res.errorCode === 0) {
      row.visibilityScope = visibilityScope;
      ElMessage.success(res.message || $t('message.saveOkMessage'));
      closeVisibilityScopePopover(row.id);
      pageDataRef.value?.reload?.();
    }
  } finally {
    updatingVisibilityScopeId.value = null;
  }
}

function changeCategory(category: any) {
  const categoryId = String(category?.id || '');
  if (initialCategoryChangePending && categoryId === selectedCategoryId.value) {
    initialCategoryChangePending = false;
    return;
  }
  initialCategoryChangePending = false;
  selectedCategoryId.value = categoryId;
  pageDataRef.value?.setQuery({
    categoryId: categoryId || undefined,
    keyword: searchKeyword.value || undefined,
  });
}

async function loadCategories() {
  const [, res] = await tryit(getAgentCategories)();
  if (res?.errorCode === 0) {
    sideList.value = [
      { id: '', categoryName: $t('common.allCategories') },
      ...(res.data || []),
    ];
    if (
      selectedCategoryId.value &&
      !sideList.value.some(
        (item) => String(item.id) === selectedCategoryId.value,
      )
    ) {
      selectedCategoryId.value = '';
      initialCategoryChangePending = false;
      pageDataRef.value?.setQuery?.({
        keyword: searchKeyword.value || undefined,
      });
    }
  }
}

function currentListFullPath() {
  return buildListRouteFullPath(
    router,
    route.query,
    {
      categoryId: selectedCategoryId.value,
      keyword: searchKeyword.value,
      pageNumber: currentPageNumber.value,
      pageSize: currentPageSize.value,
    },
    agentListRouteSchema,
  );
}

function handlePageStateChange(state: {
  pageNumber: number;
  pageSize: number;
}) {
  currentPageNumber.value = state.pageNumber;
  currentPageSize.value = state.pageSize;
  const query = mergeListRouteStateQuery(
    route.query,
    {
      categoryId: selectedCategoryId.value,
      keyword: searchKeyword.value,
      pageNumber: state.pageNumber,
      pageSize: state.pageSize,
    },
    agentListRouteSchema,
  );
  const target = router.resolve({ path: agentListRouteSchema.path, query });
  if (target.fullPath !== route.fullPath) void router.replace(target);
}

function resolvePublishStatusMeta(
  displayPublishStatus?: string,
  publishStatus?: string,
) {
  switch (resolveAiResourceDisplayStatus(displayPublishStatus, publishStatus)) {
    case 'DELETE_PENDING': {
      return { label: '删除中', tone: 'danger' };
    }
    case 'OFFLINE': {
      return { label: '已下线', tone: 'draft' };
    }
    case 'OFFLINE_PENDING': {
      return { label: '下线中', tone: 'pending' };
    }
    case 'PUBLISH_PENDING': {
      return { label: '发布中', tone: 'pending' };
    }
    case 'PUBLISHED': {
      return { label: '已发布', tone: 'published' };
    }
    default: {
      return { label: '草稿', tone: 'draft' };
    }
  }
}

async function confirmAction(
  message: string,
  type: 'info' | 'warning' = 'info',
) {
  try {
    await ElMessageBox.confirm(message, $t('message.noticeTitle'), {
      confirmButtonText: $t('button.confirm'),
      cancelButtonText: $t('button.cancel'),
      type,
    });
    return true;
  } catch {
    return false;
  }
}

async function handlePublishAction(row: AgentInfo) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning('当前智能体正在审批中');
    return;
  }
  const ok = await confirmAction(
    canAiResourceRepublish(row.displayPublishStatus, row.publishStatus)
      ? '确认提交重新发布审批？'
      : '确认提交发布审批？',
  );
  if (!ok) return;
  const res = await submitAgentPublishApproval(String(row.id));
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}

async function handleOfflineAction(row: AgentInfo) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning('当前智能体正在审批中');
    return;
  }
  const ok = await confirmAction('确认提交下线审批？', 'warning');
  if (!ok) return;
  const res = await submitAgentOfflineApproval(String(row.id));
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}

async function handleDeleteAction(row: AgentInfo) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning('当前智能体正在审批中');
    return;
  }
  const ok = await confirmAction('确认提交删除审批？', 'warning');
  if (!ok) return;
  const res = await submitAgentDeleteApproval(String(row.id));
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}
</script>

<template>
  <div class="agent-list-page">
    <HeaderSearch
      :buttons="headerButtons"
      :initial-value="searchKeyword"
      @search="handleSearch"
      @button-click="handleButtonClick"
    />
    <div class="agent-list-page__body">
      <PageSide
        label-key="categoryName"
        value-key="id"
        :menus="sideList"
        :default-selected="selectedCategoryId"
        @change="changeCategory"
      />
      <div class="agent-list-page__content">
        <PageData
          ref="pageDataRef"
          page-url="/api/v1/agent/page"
          :page-sizes="[12, 18, 24]"
          :page-size="initialListState.pageSize"
          :initial-page-number="initialListState.pageNumber"
          :initial-query-params="{
            categoryId: initialListState.categoryId || undefined,
            keyword: initialListState.keyword || undefined,
          }"
          @state-change="handlePageStateChange"
        >
          <template #default="{ pageList }">
            <CardList
              title-field="name"
              icon-field="avatar"
              :default-icon="defaultAssistantAvatar"
              :data="pageList"
              :primary-action="primaryAction"
              :actions="actions"
              density="compact"
              meta-layout="compact"
            >
              <template #details="{ item }">
                <AiResourceCornerMeta>
                  <template #publish>
                    <div
                      class="agent-publish-chip"
                      :class="`agent-publish-chip--${
                        resolvePublishStatusMeta(
                          item.displayPublishStatus,
                          item.publishStatus,
                        ).tone
                      }`"
                    >
                      <span class="agent-publish-chip__dot"></span>
                      <span>{{
                        resolvePublishStatusMeta(
                          item.displayPublishStatus,
                          item.publishStatus,
                        ).label
                      }}</span>
                    </div>
                  </template>
                  <template #scope>
                    <ElPopover
                      v-if="canManageAgent"
                      :ref="(el) => setVisibilityScopePopoverRef(item.id, el)"
                      trigger="click"
                      placement="bottom-end"
                      :width="208"
                      popper-class="agent-visibility-popover"
                    >
                      <template #reference>
                        <button
                          type="button"
                          class="agent-scope-chip"
                          :class="`agent-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
                          :disabled="updatingVisibilityScopeId === item.id"
                          @click.stop
                        >
                          <ElIcon class="agent-scope-chip__icon">
                            <component
                              :is="
                                resolveVisibilityScopeMeta(item.visibilityScope)
                                  .icon
                              "
                            />
                          </ElIcon>
                          <span class="agent-scope-chip__label">
                            {{
                              resolveVisibilityScopeMeta(item.visibilityScope)
                                .label
                            }}
                          </span>
                        </button>
                      </template>
                      <div class="agent-scope-panel" @click.stop>
                        <button
                          v-for="option in visibilityScopeOptions"
                          :key="option.value"
                          type="button"
                          class="agent-scope-option"
                          :class="[
                            `agent-scope-option--${option.tone}`,
                            {
                              'agent-scope-option--active':
                                item.visibilityScope === option.value,
                            },
                          ]"
                          :disabled="updatingVisibilityScopeId === item.id"
                          @click.stop="
                            updateVisibilityScope(item, option.value)
                          "
                        >
                          <span class="agent-scope-option__leading">
                            <span class="agent-scope-option__icon-wrap">
                              <ElIcon class="agent-scope-option__icon">
                                <component :is="option.icon" />
                              </ElIcon>
                            </span>
                            <span class="agent-scope-option__text">
                              <span class="agent-scope-option__label">
                                {{ option.label }}
                              </span>
                              <span class="agent-scope-option__desc">
                                {{ option.description }}
                              </span>
                            </span>
                          </span>
                          <ElIcon
                            v-if="item.visibilityScope === option.value"
                            class="agent-scope-option__check"
                          >
                            <Check />
                          </ElIcon>
                        </button>
                      </div>
                    </ElPopover>
                    <div
                      v-else
                      class="agent-scope-chip agent-scope-chip--readonly"
                      :class="`agent-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
                    >
                      <ElIcon class="agent-scope-chip__icon">
                        <component
                          :is="
                            resolveVisibilityScopeMeta(item.visibilityScope)
                              .icon
                          "
                        />
                      </ElIcon>
                      <span class="agent-scope-chip__label">
                        {{
                          resolveVisibilityScopeMeta(item.visibilityScope).label
                        }}
                      </span>
                    </div>
                  </template>
                </AiResourceCornerMeta>
              </template>
            </CardList>
          </template>
        </PageData>
      </div>
    </div>
  </div>
</template>

<style scoped>
.agent-list-page {
  display: flex;
  flex-direction: column;
  gap: 24px;
  height: 100%;
  padding: 24px;
}

.agent-list-page__body {
  display: flex;
  flex: 1;
  gap: 24px;
  min-height: 0;
}

.agent-list-page__content {
  flex: 1;
  min-width: 0;
  height: calc(100vh - 192px);
  overflow: auto;
}

.agent-publish-chip {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  padding: 0 8px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: 999px;
  box-shadow: inset 0 1px 0 hsl(var(--card) / 46%);
}

.agent-publish-chip__dot {
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 999px;
  opacity: 0.88;
}

.agent-publish-chip--draft {
  color: hsl(var(--muted-foreground));
  background: hsl(var(--muted) / 42%);
  border-color: hsl(var(--line-subtle));
}

.agent-publish-chip--pending {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
  border-color: hsl(var(--warning) / 14%);
}

.agent-publish-chip--published {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
  border-color: hsl(var(--success) / 14%);
}

.agent-publish-chip--danger {
  color: hsl(var(--destructive));
  background: hsl(var(--destructive) / 10%);
  border-color: hsl(var(--destructive) / 14%);
}

.agent-scope-chip {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  padding: 0 8px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  color: hsl(var(--text-strong));
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  transition:
    border-color 0.18s ease,
    background-color 0.18s ease,
    color 0.18s ease,
    transform 0.18s ease,
    box-shadow 0.18s ease;
}

button.agent-scope-chip {
  cursor: pointer;
}

button.agent-scope-chip:hover {
  background: hsl(var(--card) / 76%);
  box-shadow: 0 8px 16px -16px hsl(var(--foreground) / 28%);
}

button.agent-scope-chip:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 4px hsl(var(--primary) / 12%),
    0 10px 22px -18px hsl(var(--foreground) / 32%);
}

button.agent-scope-chip:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.agent-scope-chip--readonly {
  cursor: default;
}

.agent-scope-chip__icon {
  font-size: 12px;
}

.agent-scope-chip__label {
  white-space: nowrap;
}

.agent-scope-chip--private {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 10%);
  border-color: hsl(var(--primary) / 14%);
}

.agent-scope-chip--dept {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
  border-color: hsl(var(--warning) / 14%);
}

.agent-scope-chip--public {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
  border-color: hsl(var(--success) / 14%);
}

.agent-scope-panel {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.agent-scope-option {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 10px 8px;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 12px;
  transition:
    background-color 0.18s ease,
    transform 0.18s ease,
    color 0.18s ease;
}

.agent-scope-option:hover {
  background: hsl(var(--foreground) / 4%);
}

.agent-scope-option:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px hsl(var(--primary) / 12%);
}

.agent-scope-option:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.agent-scope-option__leading {
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 0;
}

.agent-scope-option__icon-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: transparent;
  border-radius: 9px;
}

.agent-scope-option__icon {
  font-size: 15px;
}

.agent-scope-option__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.agent-scope-option__label {
  font-size: 13px;
  font-weight: 600;
  color: hsl(var(--text-strong));
}

.agent-scope-option__desc {
  font-size: 11px;
  line-height: 1.35;
  color: hsl(var(--text-muted));
}

.agent-scope-option__check {
  flex-shrink: 0;
  font-size: 15px;
}

.agent-scope-option--private .agent-scope-option__icon-wrap {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 10%);
}

.agent-scope-option--dept .agent-scope-option__icon-wrap {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
}

.agent-scope-option--public .agent-scope-option__icon-wrap {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
}

.agent-scope-option--private.agent-scope-option--active {
  background: hsl(var(--primary) / 8%);
}

.agent-scope-option--dept.agent-scope-option--active {
  background: hsl(var(--warning) / 8%);
}

.agent-scope-option--public.agent-scope-option--active {
  background: hsl(var(--success) / 8%);
}

.agent-scope-option--private .agent-scope-option__check {
  color: hsl(var(--primary));
}

.agent-scope-option--dept .agent-scope-option__check {
  color: hsl(var(--warning));
}

.agent-scope-option--public .agent-scope-option__check {
  color: hsl(var(--success));
}

:global(.agent-visibility-popover.el-popover.el-popper) {
  padding: 8px;
  border-color: hsl(var(--line-subtle));
  border-radius: 16px;
  box-shadow: 0 18px 34px -28px hsl(var(--foreground) / 20%);
}
</style>
