<script setup lang="tsx">
import { computed, onMounted, reactive, ref } from 'vue';
import type { Component } from 'vue';
import { ElButton, ElTag } from 'element-plus';
import dayjs from 'dayjs';
import { Box, DeleteFilled, VideoPause, VideoPlay } from '@element-plus/icons-vue';
import { RDMS_OBJECT_DIRECTION_DICT_CODE } from '@/constants/dict';
import { OBJECT_CONTEXT_QUERY_KEY } from '@/constants/object-context';
import { fetchGetProductOverviewSummary, fetchGetProductPage, fetchGetUserSimpleList } from '@/service/api';
import { useDict } from '@/hooks/business/dict';
import { useRouterPush } from '@/hooks/common/router';
import { useUIPaginatedTable } from '@/hooks/common/table';
import { getProductStatusLabel, getProductStatusTagType } from '../shared/product-master-data';
import ProductOperateDialog from './modules/product-operate-dialog.vue';
import ProductSearch from './modules/product-search.vue';

defineOptions({ name: 'ProductList' });

interface StatusNavMeta {
  key: Api.Product.ProductStatusCode;
  label: string;
  description: string;
  tone: 'teal' | 'slate' | 'amber' | 'rose';
  icon: Component;
}

type ProductPageResponse = Awaited<ReturnType<typeof fetchGetProductPage>>;

const PRODUCT_ENTRY_ROUTE_PATH = '/product/list';

function getInitSearchParams(): Api.Product.ProductSearchParams {
  return {
    pageNo: 1,
    pageSize: 10,
    keyword: '',
    directionCode: undefined,
    managerUserId: undefined,
    statusCode: undefined,
    updateTime: undefined
  };
}

function transformProductPage(response: ProductPageResponse, pageNo: number, pageSize: number) {
  if (!response.error && response.data) {
    return {
      data: response.data.list,
      pageNum: pageNo,
      pageSize,
      total: response.data.total
    };
  }

  return {
    data: [],
    pageNum: pageNo,
    pageSize,
    total: 0
  };
}

function sortManagerOptions(list: Api.SystemManage.UserSimple[]) {
  return list.slice().sort((left, right) => left.nickname.localeCompare(right.nickname, 'zh-CN'));
}

function formatDateTime(value?: string | null) {
  if (!value) {
    return '--';
  }

  return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
}

const statusNavMetas: StatusNavMeta[] = [
  {
    key: 'active',
    label: '启用产品',
    description: '当前正常服务中的产品',
    tone: 'teal',
    icon: VideoPlay
  },
  {
    key: 'archived',
    label: '归档产品',
    description: '已完成阶段目标的产品',
    tone: 'slate',
    icon: Box
  },
  {
    key: 'paused',
    label: '暂停产品',
    description: '阶段性暂停投入的产品',
    tone: 'amber',
    icon: VideoPause
  },
  {
    key: 'abandoned',
    label: '废弃产品',
    description: '已明确停止建设的产品',
    tone: 'rose',
    icon: DeleteFilled
  }
];

const searchParams = reactive(getInitSearchParams());
const selectedStatus = ref<Api.Product.ProductStatusCode>('active');
const managerFilterOptions = ref<Api.SystemManage.UserSimple[]>([]);
const managerUserOptions = ref<Api.SystemManage.UserSimple[]>([]);
const operateVisible = ref(false);
const editingRow = ref<Api.Product.Product | null>(null);
const { routerPush } = useRouterPush();

const { getLabel: getDirectionDictLabel } = useDict(RDMS_OBJECT_DIRECTION_DICT_CODE);

const statusCounts = ref<Record<string, number>>({
  active: 0,
  archived: 0,
  paused: 0,
  abandoned: 0
});

const managerLabelMap = computed(() => {
  return new Map(managerUserOptions.value.map(item => [String(item.id), item.nickname]));
});

const statusItems = computed(() =>
  statusNavMetas.map(item => ({
    ...item,
    count: statusCounts.value[item.key] ?? 0
  }))
);

function getDirectionLabel(directionCode?: string | null) {
  return getDirectionDictLabel(directionCode, '--');
}

function getManagerLabel(managerUserId?: string | null) {
  if (!managerUserId) {
    return '--';
  }

  return managerLabelMap.value.get(String(managerUserId)) || String(managerUserId);
}

function createRequestParams(): Api.Product.ProductSearchParams {
  return {
    ...searchParams,
    keyword: searchParams.keyword?.trim() || undefined,
    statusCode: selectedStatus.value
  };
}

const { columns, columnChecks, data, loading, getDataByPage, mobilePagination } = useUIPaginatedTable<
  ProductPageResponse,
  Api.Product.Product
>({
  paginationProps: {
    currentPage: searchParams.pageNo,
    pageSize: searchParams.pageSize
  },
  api: () => fetchGetProductPage(createRequestParams()),
  transform: response => transformProductPage(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
  onPaginationParamsChange: params => {
    searchParams.pageNo = params.currentPage ?? 1;
    searchParams.pageSize = params.pageSize ?? 10;
  },
  columns: () => [
    { prop: 'index', type: 'index', label: '序号', width: 64 },
    {
      prop: 'name',
      label: '产品名称',
      minWidth: 220,
      formatter: row => (
        <ElButton link type="primary" class="product-name-link" onClick={() => enterProductContext(row)}>
          {row.name}
        </ElButton>
      )
    },
    { prop: 'code', label: '产品编码', minWidth: 140, showOverflowTooltip: true },
    {
      prop: 'managerUserId',
      label: '产品经理',
      minWidth: 120,
      formatter: row => getManagerLabel(row.managerUserId)
    },
    {
      prop: 'directionCode',
      label: '产品方向',
      minWidth: 140,
      showOverflowTooltip: true,
      formatter: row => getDirectionLabel(row.directionCode)
    },
    {
      prop: 'statusCode',
      label: '管理状态',
      width: 120,
      align: 'center',
      formatter: row => (
        <ElTag type={getProductStatusTagType(row.statusCode)}>{getProductStatusLabel(row.statusCode)}</ElTag>
      )
    },
    {
      prop: 'lastStatusReason',
      label: '状态原因',
      minWidth: 180,
      showOverflowTooltip: true,
      formatter: row => row.lastStatusReason?.trim() || '--'
    },
    {
      prop: 'updateTime',
      label: '最近更新',
      width: 170,
      align: 'center',
      formatter: row => formatDateTime(row.updateTime)
    }
  ],
  immediate: false
});

async function loadManagerOptions() {
  const { error, data: userList } = await fetchGetUserSimpleList();

  if (error || !userList) {
    managerUserOptions.value = [];
    managerFilterOptions.value = [];
    return;
  }

  const userSimpleList = sortManagerOptions(userList);
  managerUserOptions.value = userSimpleList;
  managerFilterOptions.value = userSimpleList;
}

async function loadOverviewData() {
  const { error, data: overviewSummary } = await fetchGetProductOverviewSummary();

  if (error || !overviewSummary) {
    statusCounts.value = {};
    return;
  }

  statusCounts.value = overviewSummary.statusCounts || {};
}

async function reloadProductTable(page = searchParams.pageNo ?? 1) {
  await getDataByPage(page);
}

async function refreshPageData(page = searchParams.pageNo ?? 1) {
  await Promise.all([loadManagerOptions(), loadOverviewData(), reloadProductTable(page)]);
}

async function handleSearch() {
  await reloadProductTable(1);
}

async function handleResetSearch() {
  const pageSize = searchParams.pageSize ?? 10;

  Object.assign(searchParams, getInitSearchParams(), {
    pageSize
  });

  await reloadProductTable(1);
}

async function handleStatusChange(status: Api.Product.ProductStatusCode) {
  selectedStatus.value = status;
  await Promise.all([loadOverviewData(), reloadProductTable(1)]);
}

function openCreate() {
  editingRow.value = null;
  operateVisible.value = true;
}

async function enterProductContext(row: Api.Product.Product) {
  await routerPush({
    path: PRODUCT_ENTRY_ROUTE_PATH,
    query: {
      [OBJECT_CONTEXT_QUERY_KEY]: row.id
    }
  });
}

async function handleProductSubmitted(productId?: string) {
  const isEditing = Boolean(productId && editingRow.value?.id === productId);

  await refreshPageData(isEditing ? (searchParams.pageNo ?? 1) : 1);

  if (isEditing) {
    editingRow.value = null;
  }
}

onMounted(async () => {
  await refreshPageData();
});
</script>

<template>
  <div
    class="min-h-560px gap-16px overflow-hidden xl:grid xl:grid-cols-[396px_minmax(0,1fr)] lt-xl:flex lt-xl:flex-col lt-xl:overflow-auto"
  >
    <div class="flex-col-stretch gap-16px xl:min-h-0">
      <ElCard class="product-overview-card card-wrapper">
        <div class="product-status-panel__list">
          <button
            v-for="item in statusItems"
            :key="item.key"
            type="button"
            class="product-status-item"
            :class="[`product-status-item--${item.tone}`, { 'is-active': selectedStatus === item.key }]"
            :aria-pressed="selectedStatus === item.key"
            @click="handleStatusChange(item.key)"
          >
            <div class="product-status-item__icon">
              <ElIcon>
                <component :is="item.icon" />
              </ElIcon>
            </div>
            <div class="product-status-item__main">
              <div class="product-status-item__top">
                <strong>{{ item.label }}</strong>
                <em>{{ item.count }}</em>
              </div>
              <p class="product-status-item__desc">{{ item.description }}</p>
            </div>
          </button>
        </div>
      </ElCard>
    </div>

    <div class="flex-col-stretch gap-16px xl:min-h-0">
      <ProductSearch
        v-model:model="searchParams"
        :manager-options="managerFilterOptions"
        @reset="handleResetSearch"
        @search="handleSearch"
      />

      <ElCard class="card-wrapper xl:flex-1-hidden" body-class="product-table-card-body">
        <template #header>
          <div class="product-card-header">
            <div class="min-w-0 flex-1">
              <div class="flex flex-wrap items-center gap-8px">
                <p class="truncate text-16px font-600">产品列表</p>
                <ElTag effect="plain" :type="getProductStatusTagType(selectedStatus)">
                  {{
                    statusItems.find(item => item.key === selectedStatus)?.label ||
                    getProductStatusLabel(selectedStatus)
                  }}
                </ElTag>
                <ElTag effect="plain">{{ mobilePagination.total || data.length }}</ElTag>
              </div>
            </div>
            <TableHeaderOperation
              v-model:columns="columnChecks"
              :disabled-delete="true"
              :loading="loading"
              @refresh="refreshPageData"
            >
              <template #default>
                <ElButton plain type="primary" @click="openCreate">
                  <template #icon>
                    <icon-ic-round-plus class="text-icon" />
                  </template>
                  新增
                </ElButton>
              </template>
            </TableHeaderOperation>
          </div>
        </template>

        <div class="flex-1">
          <ElTable v-loading="loading" height="100%" border row-key="id" :data="data">
            <ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col" />

            <template #empty>
              <ElEmpty description="当前筛选条件下暂无产品" />
            </template>
          </ElTable>
        </div>
        <div class="mt-20px flex justify-end">
          <ElPagination
            v-if="mobilePagination.total"
            layout="total,prev,pager,next,sizes"
            v-bind="mobilePagination"
            @current-change="mobilePagination['current-change']"
            @size-change="mobilePagination['size-change']"
          />
        </div>
      </ElCard>
    </div>

    <ProductOperateDialog
      v-model:visible="operateVisible"
      :manager-user-options="managerUserOptions"
      :row-data="editingRow"
      @submitted="handleProductSubmitted"
    />
  </div>
</template>

<style lang="scss" scoped>
.product-overview-card {
  overflow: hidden;
  border: 1px solid rgb(226 232 240 / 92%);
  background:
    radial-gradient(circle at top left, rgb(15 118 110 / 8%), transparent 36%),
    linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 97%));
}

.product-status-panel__list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.product-status-item {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px;
  border: 1px solid rgb(226 232 240 / 90%);
  border-radius: 18px;
  background-color: rgb(255 255 255 / 86%);
  text-align: left;
  transition:
    transform 0.2s ease,
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

.product-status-item:hover {
  transform: translateY(-1px);
  border-color: rgb(148 163 184 / 60%);
}

.product-status-item.is-active {
  border-color: rgb(15 118 110 / 40%);
  box-shadow: 0 10px 24px rgb(15 118 110 / 8%);
}

.product-status-item__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 14px;
  font-size: 20px;
}

.product-status-item__main {
  min-width: 0;
  flex: 1;
}

.product-status-item__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

.product-status-item__top strong {
  color: rgb(15 23 42 / 94%);
  font-size: 15px;
  font-weight: 700;
}

.product-status-item__top em {
  color: rgb(15 23 42 / 88%);
  font-size: 18px;
  font-style: normal;
  font-weight: 700;
}

.product-status-item__desc {
  color: rgb(100 116 139 / 94%);
  font-size: 13px;
  line-height: 1.6;
}

.product-status-item--teal .product-status-item__icon {
  background-color: rgb(240 253 250 / 96%);
  color: rgb(15 118 110 / 96%);
}

.product-status-item--slate .product-status-item__icon {
  background-color: rgb(241 245 249 / 96%);
  color: rgb(51 65 85 / 92%);
}

.product-status-item--amber .product-status-item__icon {
  background-color: rgb(255 251 235 / 96%);
  color: rgb(217 119 6 / 92%);
}

.product-status-item--rose .product-status-item__icon {
  background-color: rgb(255 241 242 / 96%);
  color: rgb(225 29 72 / 92%);
}

:deep(.product-table-card-body) {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.product-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.product-name-link {
  padding: 0;
}

@media (width <= 1280px) {
  .product-card-header,
  .product-status-item__top {
    align-items: flex-start;
    flex-direction: column;
  }
}
</style>
