<script setup lang="ts">
import {
  DeleteOutlined,
  EditOutlined,
  FolderAddOutlined,
  FolderOpenOutlined,
  MoreOutlined,
  PlusOutlined,
  SearchOutlined,
  UploadOutlined,
  ArrowRightOutlined,
  ExclamationCircleOutlined,
} from "@ant-design/icons-vue";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { message, Modal } from "ant-design-vue";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";

import { imagesApi } from "@/lib/api";
import { formatDateTime, formatBytes, formatPercentage } from "@/lib/display";
import { formatError } from "@/lib/errors";
import { IMAGE_UPLOAD_ACCEPT, useImageUploadProgress } from "@/lib/image-webp";
import type { ImageAssetItem, ImageFolder } from "@geo/shared-types";

const { t } = useI18n();
const queryClient = useQueryClient();
const imageUploadProgress = useImageUploadProgress();

// State
const selectedFolderId = ref<number | null>(null); // null means "All" or is handled by folders query
const searchKeyword = ref("");
const debouncedSearch = ref("");
const currentPage = ref(1);
const pageSize = ref(20);

const folderModalOpen = ref(false);
const editingFolder = ref<ImageFolder | null>(null);
const folderFormName = ref("");

const moveModalOpen = ref(false);
const movingImage = ref<ImageAssetItem | null>(null);
const UNCATEGORIZED_MOVE_TARGET = "__uncategorized__";
type MoveTargetValue = number | typeof UNCATEGORIZED_MOVE_TARGET;
const targetFolderValue = ref<MoveTargetValue | undefined>();
const previewModalOpen = ref(false);
const previewingImage = ref<ImageAssetItem | null>(null);

const renameImageModalOpen = ref(false);
const renamingImage = ref<ImageAssetItem | null>(null);
const imageFormName = ref("");

const fileInput = ref<HTMLInputElement | null>(null);

// Queries
const foldersQuery = useQuery({
  queryKey: ["images", "folders"],
  queryFn: () => imagesApi.listFolders(),
});

const imagesQuery = useQuery({
  queryKey: computed(() => ["images", "list", {
    folder_id: debouncedSearch.value ? undefined : selectedFolderId.value,
    q: debouncedSearch.value,
    page: currentPage.value,
    page_size: pageSize.value,
  }]),
  queryFn: () => imagesApi.list({
    folder_id: debouncedSearch.value ? undefined : (selectedFolderId.value ?? undefined),
    q: debouncedSearch.value,
    page: currentPage.value,
    page_size: pageSize.value,
  }),
});

const allImagesCountQuery = useQuery({
  queryKey: ["images", "list", "all-count"],
  queryFn: () => imagesApi.list({
    page: 1,
    page_size: 1,
  }),
});

const storageQuery = useQuery({
  queryKey: ["images", "storage-usage"],
  queryFn: () => imagesApi.storageUsage(),
});

// Mutations
const createFolderMutation = useMutation({
  mutationFn: (name: string) => imagesApi.createFolder(name),
  onSuccess: () => {
    message.success(t("images.folderCreated"));
    folderModalOpen.value = false;
    queryClient.invalidateQueries({ queryKey: ["images", "folders"] });
  },
  onError: (error) => message.error(formatError(error)),
});

const updateFolderMutation = useMutation({
  mutationFn: (payload: { id: number; name: string }) => imagesApi.updateFolder(payload.id, payload.name),
  onSuccess: () => {
    message.success(t("images.folderRenamed"));
    folderModalOpen.value = false;
    queryClient.invalidateQueries({ queryKey: ["images", "folders"] });
  },
  onError: (error) => message.error(formatError(error)),
});

const deleteFolderMutation = useMutation({
  mutationFn: (payload: { id: number; force?: boolean }) => imagesApi.deleteFolder(payload.id, payload.force),
  onSuccess: () => {
    message.success(t("images.folderDeleted"));
    if (selectedFolderId.value === editingFolder.value?.id) {
      selectedFolderId.value = null;
    }
    queryClient.invalidateQueries({ queryKey: ["images"] });
  },
  onError: (error) => message.error(formatError(error)),
});

const uploadMutation = useMutation({
  mutationFn: (file: File) => imagesApi.upload(file, selectedFolderId.value),
  onSuccess: (result) => {
    message.success(t(result.reused ? "images.uploadReusedSuccess" : "images.uploadSuccess"));
    queryClient.invalidateQueries({ queryKey: ["images"] });
  },
  onError: (error) => message.error(formatError(error)),
});

const updateImageMutation = useMutation({
  mutationFn: (payload: { id: number; name?: string; folder_id?: number | null }) => 
    imagesApi.update(payload.id, { name: payload.name, folder_id: payload.folder_id }),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["images"] });
  },
  onError: (error) => message.error(formatError(error)),
});

const deleteImageMutation = useMutation({
  mutationFn: (payload: { id: number; force?: boolean }) => imagesApi.delete(payload.id, payload.force),
  onSuccess: () => {
    message.success(t("images.deleteSuccess"));
    queryClient.invalidateQueries({ queryKey: ["images"] });
  },
  onError: (error) => message.error(formatError(error)),
});

// Computed
const folders = computed(() => foldersQuery.data.value ?? []);
const images = computed(() => imagesQuery.data.value?.items ?? []);
const totalImages = computed(() => imagesQuery.data.value?.total ?? 0);
const allImagesCount = computed(
  () => allImagesCountQuery.data.value?.total ?? folders.value.reduce((acc, folder) => acc + folder.image_count, 0),
);
const storageUsage = computed(() => storageQuery.data.value);

const sidebarFolders = computed(() => [
  { id: null, name: t("images.allFolders"), image_count: allImagesCount.value },
  ...folders.value,
]);

const moveTargetOptions = computed(() => buildMoveTargetOptions(movingImage.value));
const imageUploadBusy = computed(() => imageUploadProgress.visible);

// Actions
let searchTimer: ReturnType<typeof setTimeout> | null = null;
watch(searchKeyword, () => {
  if (searchTimer) clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    debouncedSearch.value = searchKeyword.value;
    currentPage.value = 1;
  }, 300);
});

function handleFolderSelect(id: number | null) {
  if (debouncedSearch.value) return;
  selectedFolderId.value = id;
  currentPage.value = 1;
}

function openCreateFolder() {
  editingFolder.value = null;
  folderFormName.value = "";
  folderModalOpen.value = true;
}

function openRenameFolder(folder: ImageFolder) {
  editingFolder.value = folder;
  folderFormName.value = folder.name;
  folderModalOpen.value = true;
}

async function handleFolderSubmit() {
  if (!folderFormName.value.trim()) return;
  if (editingFolder.value) {
    await updateFolderMutation.mutateAsync({ id: editingFolder.value.id, name: folderFormName.value.trim() });
  } else {
    await createFolderMutation.mutateAsync(folderFormName.value.trim());
  }
}

async function confirmDeleteFolder(folder: ImageFolder) {
  editingFolder.value = folder;
  const preview = await imagesApi.folderDeletePreview(folder.id);

  const hasImages = preview.image_count > 0;
  const refSummary = hasImages
    ? t("images.folderHasRefs", { count: preview.image_count })
    : "";
  Modal.confirm({
    title: t("images.confirmDeleteTitle"),
    content: hasImages
      ? `${refSummary}\n\n${t("images.confirmDeleteFolder", { name: folder.name })}`
      : t("images.confirmDeleteFolder", { name: folder.name }),
    okText: hasImages ? t("images.forceDelete") : t("common.confirm"),
    okType: "danger",
    cancelText: t("common.cancel"),
    onOk: () => deleteFolderMutation.mutateAsync({ id: folder.id, force: hasImages }),
  });
}

function triggerUpload() {
  if (imageUploadBusy.value) {
    return;
  }
  fileInput.value?.click();
}

async function handleFileChange(e: Event) {
  if (imageUploadBusy.value) {
    (e.target as HTMLInputElement).value = "";
    return;
  }

  const files = (e.target as HTMLInputElement).files;
  if (!files?.length) return;
  
  for (let i = 0; i < files.length; i++) {
    try {
      await uploadMutation.mutateAsync(files[i]);
    } catch {
      // surfaced by mutation onError so remaining files can continue
    }
  }
  (e.target as HTMLInputElement).value = "";
}

function openRenameImage(image: ImageAssetItem) {
  renamingImage.value = image;
  imageFormName.value = image.name;
  renameImageModalOpen.value = true;
}

async function handleRenameImage() {
  if (!renamingImage.value || !imageFormName.value.trim()) return;
  await updateImageMutation.mutateAsync({ id: renamingImage.value.id, name: imageFormName.value.trim() });
  message.success(t("images.imageRenamed"));
  renameImageModalOpen.value = false;
}

function openMoveImage(image: ImageAssetItem) {
  const options = buildMoveTargetOptions(image);
  if (options.length === 0) {
    return;
  }
  movingImage.value = image;
  targetFolderValue.value = options[0]?.value;
  moveModalOpen.value = true;
}

async function handleMoveImage() {
  if (!movingImage.value || targetFolderValue.value == null) return;

  const nextFolderId = normalizeMoveTargetValue(targetFolderValue.value);
  if (nextFolderId === movingImage.value.folder_id) {
    moveModalOpen.value = false;
    return;
  }

  await updateImageMutation.mutateAsync({ id: movingImage.value.id, folder_id: nextFolderId });
  message.success(t("images.imageMoved"));
  moveModalOpen.value = false;
}

function buildMoveTargetOptions(image: ImageAssetItem | null) {
  if (!image) {
    return [];
  }

  const options: Array<{ value: MoveTargetValue; label: string }> = [];

  folders.value
    .filter((folder) => folder.id !== image.folder_id)
    .forEach((folder) => {
      options.push({
        value: folder.id,
        label: folder.name,
      });
    });

  if (image.folder_id !== null) {
    options.push({
      value: UNCATEGORIZED_MOVE_TARGET,
      label: t("images.uncategorized"),
    });
  }

  return options;
}

function normalizeMoveTargetValue(value: MoveTargetValue) {
  return value === UNCATEGORIZED_MOVE_TARGET ? null : value;
}

function canMoveImage(image: ImageAssetItem) {
  return buildMoveTargetOptions(image).length > 0;
}

function getSelectPopupContainer(triggerNode: HTMLElement) {
  return triggerNode.parentElement ?? triggerNode;
}

function openImagePreview(image: ImageAssetItem) {
  previewingImage.value = image;
  previewModalOpen.value = true;
}

async function confirmDeleteImage(image: ImageAssetItem) {
  const refs = await imagesApi.getReferences(image.id);
  
  if (refs.total > 0) {
    Modal.confirm({
      title: t("images.imageInUse"),
      content: `${t("images.refArticleBody")}: ${refs.article_body}, ${t("images.refArticleCover")}: ${refs.article_cover}\n\n${t("images.confirmDeleteImage")}`,
      okText: t("images.forceDelete"),
      okType: "danger",
      cancelText: t("common.cancel"),
      onOk: () => deleteImageMutation.mutateAsync({ id: image.id, force: true }),
    });
  } else {
    Modal.confirm({
      title: t("images.confirmDeleteTitle"),
      content: t("images.confirmDeleteImage"),
      okText: t("common.confirm"),
      okType: "danger",
      cancelText: t("common.cancel"),
      onOk: () => deleteImageMutation.mutateAsync({ id: image.id }),
    });
  }
}
</script>

<template>
  <div class="images-page">
    <section class="page-title-card">
      <div class="page-title-card__header">
        <div class="page-title-card__copy">
          <h2>{{ t("images.title") }}</h2>
          <p>{{ t("images.description") }}</p>
        </div>
        <div class="page-title-card__actions">
          <a-button type="primary" :disabled="imageUploadBusy" @click="triggerUpload">
            <template #icon><UploadOutlined /></template>
            {{ t("images.uploadBtn") }}
          </a-button>
          <input
            ref="fileInput"
            type="file"
            hidden
            multiple
            :accept="IMAGE_UPLOAD_ACCEPT"
            :disabled="imageUploadBusy"
            @change="handleFileChange"
          />
        </div>
      </div>
    </section>

    <div class="images-main-card">
      <div class="images-toolbar">
        <div class="images-toolbar__left">
          <a-input-search
            v-model:value="searchKeyword"
            :placeholder="t('images.searchPlaceholder')"
            style="width: 300px"
            allow-clear
          />
        </div>
      </div>

      <div class="images-layout-inner">
        <aside class="images-sidebar">
          <div class="images-sidebar__header">
            <div class="images-sidebar__title">{{ t("images.folders") }}</div>
            <div class="images-sidebar__add" @click="openCreateFolder">
              <PlusOutlined /> {{ t("images.newFolder") }}
            </div>
          </div>

          <div class="folder-list">
            <div
              v-for="folder in sidebarFolders"
              :key="folder.id ?? 'all'"
              class="folder-item"
              :class="{ 
                'folder-item--active': selectedFolderId === folder.id && !debouncedSearch,
                'folder-item--disabled': !!debouncedSearch
              }"
              @click="handleFolderSelect(folder.id)"
            >
              <div class="folder-item__icon">
                <FolderOpenOutlined v-if="selectedFolderId === folder.id" />
                <FolderAddOutlined v-else />
              </div>
              <div class="folder-item__name">{{ folder.name }}</div>
              <div class="folder-item__count">{{ folder.image_count }}</div>
              
              <div v-if="folder.id !== null" class="folder-item__actions" @click.stop>
                <a-dropdown :trigger="['click']">
                  <MoreOutlined />
                  <template #overlay>
                    <a-menu>
                      <a-menu-item @click="openRenameFolder(folder)">
                        <EditOutlined /> {{ t("images.rename") }}
                      </a-menu-item>
                      <a-menu-item danger @click="confirmDeleteFolder(folder)">
                        <DeleteOutlined /> {{ t("images.deleteFolder") }}
                      </a-menu-item>
                    </a-menu>
                  </template>
                </a-dropdown>
              </div>
            </div>
          </div>

          <div v-if="storageUsage" class="storage-usage">
            <div class="storage-usage__header">
              <span>{{ t("images.storageUsage") }}</span>
              <span>{{ t("images.storageUsageDetail", { 
                used: formatBytes(storageUsage.used_bytes), 
                total: formatBytes(storageUsage.quota_bytes),
                percent: formatPercentage(storageUsage.used_pct)
              }) }}</span>
            </div>
            <a-progress 
              :percent="storageUsage.used_pct" 
              :show-info="false" 
              stroke-color="#1677ff"
              size="small"
            />
          </div>
        </aside>

        <section class="images-content">
          <div v-if="images.length > 0" class="image-grid">
            <div
              v-for="image in images"
              :key="image.id"
              class="image-card image-card--clickable"
              @click="openImagePreview(image)"
            >
              <div class="image-card__preview">
                <img :src="image.url" :alt="image.name" loading="lazy" />
                <div class="image-card__actions">
                  <a-tooltip :title="t('images.rename')">
                    <a-button type="text" shape="circle" size="small" @click.stop="openRenameImage(image)">
                      <EditOutlined />
                    </a-button>
                  </a-tooltip>
                  <a-tooltip v-if="canMoveImage(image)" :title="t('images.move')">
                    <a-button
                      type="text"
                      shape="circle"
                      size="small"
                      @click.stop="openMoveImage(image)"
                    >
                      <ArrowRightOutlined />
                    </a-button>
                  </a-tooltip>
                  <a-tooltip :title="t('images.deleteImage')">
                    <a-button type="text" shape="circle" size="small" danger @click.stop="confirmDeleteImage(image)">
                      <DeleteOutlined />
                    </a-button>
                  </a-tooltip>
                </div>
              </div>
              <div class="image-card__info">
                <div class="image-card__name" :title="image.name">{{ image.name }}</div>
                <div class="image-card__meta">
                  {{ formatBytes(image.size_bytes) }} · {{ formatDateTime(image.created_at) }}
                </div>
              </div>
            </div>
          </div>
          <div v-else-if="!imagesQuery.isPending.value" class="empty-state">
            <a-empty :description="t('images.noImages')" />
          </div>

          <div v-if="totalImages > pageSize" class="pagination-wrapper">
            <a-pagination
              v-model:current="currentPage"
              v-model:page-size="pageSize"
              :total="totalImages"
              show-size-changer
              size="small"
            />
          </div>
        </section>
      </div>
    </div>

    <!-- Folder Modal -->
    <a-modal
      v-model:open="folderModalOpen"
      :title="editingFolder ? t('images.rename') : t('images.newFolder')"
      @ok="handleFolderSubmit"
    >
      <div style="padding: 12px 0;">
        <div style="margin-bottom: 8px;">{{ t("images.folderName") }}</div>
        <a-input v-model:value="folderFormName" :placeholder="t('images.folderName')" />
      </div>
    </a-modal>

    <!-- Rename Image Modal -->
    <a-modal
      v-model:open="renameImageModalOpen"
      :title="t('images.rename')"
      @ok="handleRenameImage"
    >
      <div style="padding: 12px 0;">
        <div style="margin-bottom: 8px;">{{ t("common.title") }}</div>
        <a-input v-model:value="imageFormName" :placeholder="t('common.title')" />
      </div>
    </a-modal>

    <!-- Move Image Modal -->
    <a-modal
      v-model:open="moveModalOpen"
      :title="t('images.move')"
      :ok-button-props="{ disabled: moveTargetOptions.length === 0 }"
      @ok="handleMoveImage"
    >
      <div style="padding: 12px 0;">
        <div style="margin-bottom: 8px;">{{ t("images.folders") }}</div>
        <a-select
          v-model:value="targetFolderValue"
          style="width: 100%"
          :loading="foldersQuery.isPending.value"
          :get-popup-container="getSelectPopupContainer"
        >
          <a-select-option
            v-for="option in moveTargetOptions"
            :key="String(option.value)"
            :value="option.value"
          >
            {{ option.label }}
          </a-select-option>
        </a-select>
      </div>
    </a-modal>

    <a-modal
      v-model:open="previewModalOpen"
      :title="previewingImage?.name"
      :footer="null"
      width="min(92vw, 1200px)"
      centered
    >
      <div v-if="previewingImage" class="image-preview-modal">
        <img :src="previewingImage.url" :alt="previewingImage.name" class="image-preview-modal__image" />
        <div class="image-preview-modal__meta">
          {{ formatBytes(previewingImage.size_bytes) }} · {{ formatDateTime(previewingImage.created_at) }}
        </div>
      </div>
    </a-modal>
  </div>
</template>

<style scoped>
.images-page {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.images-main-card {
  background: #fff;
  border-radius: 12px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03);
}

.images-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.images-layout-inner {
  display: flex;
  gap: 24px;
  min-height: 600px;
}

.images-sidebar {
  border: 1px solid #f0f0f0;
  width: 260px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.images-sidebar__header {
  display: flex;
  padding-top: 16px;
  padding-left: 16px;
  padding-right: 16px;
  justify-content: space-between;
  align-items: center;
}

.images-sidebar__title {
  color: #111827;
  font-weight: 600;
  font-size: 14px;
}

.images-sidebar__add {
  color: #1677ff;
  font-size: 13px;
  cursor: pointer;
}

.folder-list {
  display: flex;
  padding: 0 16px;
  flex-direction: column;
  gap: 4px;
}

.folder-item {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s;
  color: #4b5563;
  gap: 10px;
}

.folder-item:hover {
  background: #f3f4f6;
}

.folder-item--active {
  background: #e6f4ff;
  color: #1677ff;
  font-weight: 500;
}

.folder-item--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.folder-item__icon {
  font-size: 16px;
}

.folder-item__name {
  flex: 1;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.folder-item__count {
  font-size: 12px;
  color: #9ca3af;
}

.folder-item__actions {
  visibility: hidden;
  color: #9ca3af;
}

.folder-item:hover .folder-item__actions {
  visibility: visible;
}

.storage-usage {
  margin-top: auto;
  padding: 16px;
  background: #f9fafb;
  border-radius: 8px;
}

.storage-usage__header {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  margin-bottom: 8px;
  color: #6b7280;
}

.images-content {
  border: 1px solid #f0f0f0;
  padding: 16px;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 20px;
}

.image-card {
  border-radius: 8px;
  border: 1px solid #f0f0f0;
  overflow: hidden;
  transition: all 0.2s;
}

.image-card--clickable {
  cursor: zoom-in;
}

.image-card:hover {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  transform: translateY(-2px);
}

.image-card__preview {
  position: relative;
  aspect-ratio: 4 / 3;
  background: #f9fafb;
  display: flex;
  align-items: center;
  justify-content: center;
}

.image-card__preview img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.image-card__actions {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  opacity: 0;
  transition: opacity 0.2s;
}

.image-card:hover .image-card__actions {
  opacity: 1;
}

.image-card__actions :deep(.ant-btn) {
  background: #fff;
}

.image-card__info {
  padding: 12px;
}

.image-card__name {
  font-size: 13px;
  font-weight: 500;
  color: #111827;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 4px;
}

.image-card__meta {
  font-size: 11px;
  color: #9ca3af;
}

.image-preview-modal {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.image-preview-modal__image {
  display: block;
  max-width: 100%;
  max-height: min(72vh, 900px);
  margin: 0 auto;
  object-fit: contain;
  border-radius: 12px;
  background: #f8fafc;
}

.image-preview-modal__meta {
  font-size: 13px;
  color: #64748b;
  text-align: center;
}

.empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 400px;
}

.pagination-wrapper {
  display: flex;
  justify-content: flex-end;
  margin-top: auto;
}
</style>
