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

import { computed } from 'vue';

import { useAccess } from '@easyflow/access';

import { MoreFilled } from '@element-plus/icons-vue';
import {
  ElAvatar,
  ElButton,
  ElCard,
  ElDropdown,
  ElDropdownItem,
  ElDropdownMenu,
  ElIcon,
  ElTag,
  ElText,
} from 'element-plus';

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

export type ActionPlacement = 'inline' | 'menu';
export type ActionTone = 'danger' | 'default';

export interface ActionButton {
  icon?: any;
  text: ((row: any) => string) | string;
  className?: string;
  permission?: string;
  placement?: ActionPlacement;
  tone?: ActionTone;
  visible?: ((row: any) => boolean) | boolean;
  onClick: (row: any) => void;
}

export interface CardPrimaryAction {
  icon?: any;
  text: string;
  permission?: string;
  onClick: (row: any) => void;
}

interface ResolvedActionButton extends ActionButton {
  placement: ActionPlacement;
  tone: ActionTone;
}

export interface CardListProps {
  iconField?: string;
  titleField?: string;
  descField?: string;
  actions?: ActionButton[];
  cornerTagField?: string;
  cornerTagMap?: Record<string, string>;
  cornerTagTypeMap?: Record<string, TagProps['type']>;
  defaultIcon: any;
  data: any[];
  primaryAction?: CardPrimaryAction;
  tagField?: string;
  tagMap?: Record<string, string>;
}

const props = withDefaults(defineProps<CardListProps>(), {
  iconField: 'icon',
  titleField: 'title',
  descField: 'description',
  actions: () => [],
  cornerTagField: '',
  cornerTagMap: () => ({}),
  cornerTagTypeMap: () => ({}),
  primaryAction: undefined,
  tagField: '',
  tagMap: () => ({}),
});

const { hasAccessByCodes } = useAccess();

function hasPermission(permission?: string) {
  return !permission || hasAccessByCodes([permission]);
}

function isActionVisible(action: ActionButton, row: any) {
  if (typeof action.visible === 'function') {
    return action.visible(row);
  }
  return action.visible !== false;
}

const resolvedPrimaryAction = computed(() => {
  if (!props.primaryAction || !hasPermission(props.primaryAction.permission)) {
    return undefined;
  }
  return props.primaryAction;
});

function resolveActionPlacement(
  action: ActionButton,
  index: number,
): ActionPlacement {
  if (action.placement) {
    return action.placement;
  }
  if (resolvedPrimaryAction.value) {
    return 'menu';
  }
  return index < 3 ? 'inline' : 'menu';
}

const resolvedActions = computed<ResolvedActionButton[]>(() => {
  return props.actions
    .filter((action) => hasPermission(action.permission))
    .map((action, index) => ({
      ...action,
      placement: resolveActionPlacement(action, index),
      tone:
        action.tone ||
        (action.className?.includes('danger') ? 'danger' : 'default'),
    }));
});

function handlePrimaryAction(item: any) {
  resolvedPrimaryAction.value?.onClick(item);
}

function handleActionClick(event: Event, action: ActionButton, item: any) {
  event.stopPropagation();
  action.onClick(item);
}

function resolveActionText(action: ActionButton, item: any) {
  return typeof action.text === 'function' ? action.text(item) : action.text;
}

function resolveInlineActions(item: any) {
  return resolvedActions.value.filter(
    (action) => action.placement === 'inline' && isActionVisible(action, item),
  );
}

function resolveMenuActions(item: any) {
  return resolvedActions.value.filter(
    (action) => action.placement === 'menu' && isActionVisible(action, item),
  );
}

function hasVisibleActions(item: any) {
  return (
    resolveInlineActions(item).length > 0 || resolveMenuActions(item).length > 0
  );
}

function resolveMetaItems(item: any) {
  const metaItems: Array<{ label: string; value: string }> = [];
  if (item.createdByName) {
    metaItems.push({
      label: $t('aiResource.createdBy'),
      value: String(item.createdByName),
    });
  }
  if (item.created) {
    metaItems.push({
      label: $t('aiResource.created'),
      value: String(item.created),
    });
  }
  return metaItems;
}
</script>

<template>
  <div class="card-grid">
    <ElCard
      v-for="(item, index) in props.data"
      :key="item.id ?? index"
      shadow="never"
      class="card-item"
      :class="[{ 'card-item--interactive': resolvedPrimaryAction }]"
      :role="resolvedPrimaryAction ? 'button' : undefined"
      :tabindex="resolvedPrimaryAction ? 0 : undefined"
      @click="handlePrimaryAction(item)"
      @keydown.enter.prevent="handlePrimaryAction(item)"
      @keydown.space.prevent="handlePrimaryAction(item)"
    >
      <div class="card-content">
        <div class="card-header">
          <ElAvatar
            class="card-avatar shrink-0"
            :src="item[iconField] || defaultIcon"
            :size="44"
          />
          <div class="card-meta">
            <div class="title-row">
              <ElText truncated size="large" class="card-title">
                {{ item[titleField] }}
              </ElText>
              <ElTag
                v-if="tagField && item[tagField]"
                size="small"
                effect="plain"
                type="info"
              >
                {{ tagMap[item[tagField]] || item[tagField] }}
              </ElTag>
            </div>
            <ElText line-clamp="2" class="item-desc w-full">
              {{ item[descField] }}
            </ElText>
          </div>
          <div
            v-if="$slots.corner || (cornerTagField && item[cornerTagField])"
            class="card-corner-tag"
          >
            <slot name="corner" :item="item">
              <ElTag
                size="small"
                effect="plain"
                :type="cornerTagTypeMap[item[cornerTagField]] || 'info'"
                round
              >
                {{ cornerTagMap[item[cornerTagField]] || item[cornerTagField] }}
              </ElTag>
            </slot>
          </div>
          <div
            v-if="resolveMetaItems(item).length > 0"
            class="card-meta-row"
          >
            <span
              v-for="meta in resolveMetaItems(item)"
              :key="meta.label"
              class="card-meta-item"
            >
              <span class="card-meta-label">{{ meta.label }}：</span>
              <span class="card-meta-value">{{ meta.value }}</span>
            </span>
          </div>
        </div>
      </div>

      <template v-if="resolvedPrimaryAction || hasVisibleActions(item)" #footer>
        <div class="card-footer">
          <div v-if="resolvedPrimaryAction" class="card-primary-hint">
            <div class="primary-label">
              <ElIcon v-if="resolvedPrimaryAction.icon" class="primary-icon">
                <IconifyIcon
                  v-if="typeof resolvedPrimaryAction.icon === 'string'"
                  :icon="resolvedPrimaryAction.icon"
                />
                <component v-else :is="resolvedPrimaryAction.icon" />
              </ElIcon>
              <span class="primary-text">{{ resolvedPrimaryAction.text }}</span>
            </div>
          </div>

          <div
            v-if="hasVisibleActions(item)"
            class="card-actions"
            @click.stop
          >
            <ElButton
              v-for="(action, actionIndex) in resolveInlineActions(item)"
              :key="`${item.id ?? index}-inline-${actionIndex}`"
              :icon="typeof action.icon === 'string' ? undefined : action.icon"
              size="small"
              class="card-action-btn"
              :class="{ 'card-action-btn--danger': action.tone === 'danger' }"
              link
              @click.stop="handleActionClick($event, action, item)"
            >
              <template v-if="typeof action.icon === 'string'" #icon>
                <IconifyIcon :icon="action.icon" />
              </template>
              {{ resolveActionText(action, item) }}
            </ElButton>

            <ElDropdown
              v-if="resolveMenuActions(item).length > 0"
              trigger="click"
              placement="bottom-end"
            >
              <ElButton
                class="card-action-btn card-action-btn--menu"
                :icon="MoreFilled"
                link
                @click.stop
              />
              <template #dropdown>
                <ElDropdownMenu>
                  <ElDropdownItem
                    v-for="(action, actionIndex) in resolveMenuActions(item)"
                    :key="`${item.id ?? index}-menu-${actionIndex}`"
                    :class="{
                      'card-menu-item--danger': action.tone === 'danger',
                    }"
                    @click="action.onClick(item)"
                  >
                    <div class="menu-action-content">
                      <ElIcon v-if="action.icon">
                        <IconifyIcon
                          v-if="typeof action.icon === 'string'"
                          :icon="action.icon"
                        />
                        <component v-else :is="action.icon" />
                      </ElIcon>
                      <span>{{ resolveActionText(action, item) }}</span>
                    </div>
                  </ElDropdownItem>
                </ElDropdownMenu>
              </template>
            </ElDropdown>
          </div>
        </div>
      </template>
    </ElCard>
  </div>
</template>

<style scoped>
@media (max-width: 1024px) {
  .card-grid {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }
}

@media (max-width: 768px) {
  .card-grid {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    min-width: 100%;
  }
}

@media (max-width: 480px) {
  .card-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  min-width: max(100%, 600px);
}

.card-item {
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  border-radius: 18px;
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease,
    border-color 0.18s ease,
    background-color 0.18s ease;
}

.card-item--interactive {
  cursor: pointer;
}

.card-item--interactive:hover {
  border-color: hsl(var(--primary) / 20%);
  box-shadow: var(--shadow-subtle);
  transform: translateY(-2px);
}

.card-item--interactive:focus-visible {
  outline: none;
  border-color: hsl(var(--primary) / 32%);
  box-shadow:
    0 0 0 4px hsl(var(--primary) / 12%),
    var(--shadow-subtle);
}

:deep(.el-card__body) {
  padding: 18px;
}

:deep(.el-card__footer) {
  padding: 0 18px 18px;
  border-top: none;
}

.card-content {
  display: flex;
  min-height: 116px;
}

.card-header {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  column-gap: 14px;
  row-gap: 10px;
  align-items: flex-start;
  width: 100%;
}

.card-avatar {
  grid-column: 1;
  grid-row: 1;
  background: hsl(var(--surface-subtle));
  border: 1px solid hsl(var(--line-subtle));
}

.card-meta {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 10px;
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
}

.title-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  min-width: 0;
}

.card-title {
  font-weight: 600;
  color: hsl(var(--text-strong));
}

.item-desc {
  min-height: 44px;
  font-size: 13px;
  line-height: 22px;
  color: hsl(var(--text-muted));
}

.card-meta-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  grid-column: 1 / -1;
  width: 100%;
  font-size: 12px;
  line-height: 18px;
  color: hsl(var(--text-muted));
}

.card-meta-item {
  display: flex;
  gap: 4px;
  align-items: flex-start;
  justify-content: flex-start;
  width: 100%;
  min-width: 0;
}

.card-meta-label {
  flex-shrink: 0;
}

.card-meta-value {
  min-width: 0;
  text-align: left;
  word-break: break-all;
}

.card-corner-tag {
  display: flex;
  grid-column: 3;
  grid-row: 1;
  flex-shrink: 0;
  align-items: flex-start;
  justify-content: flex-end;
  min-height: 28px;
  margin-left: auto;
}

.card-corner-tag :deep(.el-tag) {
  --el-tag-border-radius: 999px;
  --el-tag-font-size: 12px;
  --el-tag-border-color: transparent;

  padding: 0 10px;
  font-weight: 600;
  letter-spacing: 0.01em;
  backdrop-filter: blur(6px);
}

.card-footer {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding-top: 14px;
  border-top: 1px solid hsl(var(--divider-faint) / 85%);
}

.card-primary-hint {
  display: flex;
  flex: 1;
  min-width: 0;
}

.primary-label {
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 0;
}

.primary-icon {
  color: hsl(var(--primary));
}

.primary-text {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13px;
  font-weight: 500;
  color: hsl(var(--text-strong));
  white-space: nowrap;
}

.card-actions {
  display: flex;
  flex-shrink: 0;
  gap: 4px;
  align-items: center;
  margin-left: auto;
}

.card-action-btn {
  --el-button-font-weight: 500;
  --el-button-text-color: hsl(var(--text-muted));
  --el-button-hover-text-color: hsl(var(--text-strong));
  --el-button-active-text-color: hsl(var(--text-strong));

  padding: 0 4px;
}

.card-action-btn--danger {
  --el-button-text-color: hsl(var(--destructive));
  --el-button-hover-text-color: hsl(var(--destructive));
  --el-button-active-text-color: hsl(var(--destructive));
}

.card-action-btn--menu {
  padding: 0;
}

.menu-action-content {
  display: flex;
  gap: 8px;
  align-items: center;
}

:deep(.card-menu-item--danger) {
  color: hsl(var(--destructive));
}
</style>
