<script setup lang="ts">
import { LeftOutlined } from "@ant-design/icons-vue";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { message } from "ant-design-vue";
import { computed, onBeforeUnmount, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRoute, useRouter } from "vue-router";
import { MilkdownProvider } from "@milkdown/vue";

import ArticleEditorCanvas from "@/components/ArticleEditorCanvas.vue";
import PublishPlatformSelector from "@/components/PublishPlatformSelector.vue";
import { articlesApi } from "@/lib/api";
import { formatError } from "@/lib/errors";
import { getBoundPublishPlatforms } from "@/lib/publish-platforms";

const route = useRoute();
const router = useRouter();
const queryClient = useQueryClient();
const { t } = useI18n();

const articleId = computed(() => Number(route.params.id));
const title = ref("");
const markdown = ref("");
const initialTitle = ref("");
const initialMarkdown = ref("");
const boundPublishPlatforms = getBoundPublishPlatforms();
const publishPlatforms = ref<string[]>([]);
const initialPublishPlatforms = ref<string[]>([]);
const coverEnabled = ref(true);
const coverPreviewUrl = ref("");
const leaveModalOpen = ref(false);

const detailQuery = useQuery({
  queryKey: computed(() => ["articles", "detail", articleId.value]),
  enabled: computed(() => Number.isInteger(articleId.value) && articleId.value > 0),
  queryFn: () => articlesApi.detail(articleId.value),
});

watch(
  () => detailQuery.data.value,
  (detail) => {
    if (!detail) {
      return;
    }

    const nextTitle = detail.title ?? "";
    const nextMarkdown = stripLeadingTitleHeading(nextTitle, detail.markdown_content ?? "");

    title.value = nextTitle;
    markdown.value = nextMarkdown;
    initialTitle.value = nextTitle;
    initialMarkdown.value = nextMarkdown;
    publishPlatforms.value = [...(detail.platforms ?? [])];
    initialPublishPlatforms.value = [...publishPlatforms.value];
  },
  { immediate: true },
);

const detail = computed(() => detailQuery.data.value);
const editorLocked = computed(() => detail.value?.generate_status !== "completed");
const hasChanges = computed(
  () =>
    title.value.trim() !== initialTitle.value.trim() ||
    markdown.value !== initialMarkdown.value ||
    serializePlatformSelection(publishPlatforms.value) !== serializePlatformSelection(initialPublishPlatforms.value),
);

const saveMutation = useMutation({
  mutationFn: () =>
    articlesApi.update(articleId.value, {
      title: title.value.trim(),
      markdown_content: markdown.value,
      platforms: publishPlatforms.value,
    }),
  onSuccess: async (data) => {
    message.success(t("article.editor.messages.saved"));
    title.value = data.title ?? "";
    markdown.value = data.markdown_content ?? "";
    initialTitle.value = title.value;
    initialMarkdown.value = markdown.value;
    publishPlatforms.value = [...(data.platforms ?? [])];
    initialPublishPlatforms.value = [...publishPlatforms.value];
    await Promise.all([
      queryClient.invalidateQueries({ queryKey: ["articles"] }),
      queryClient.invalidateQueries({ queryKey: ["workspace"] }),
      queryClient.invalidateQueries({ queryKey: ["templates"] }),
      queryClient.invalidateQueries({ queryKey: ["articles", "detail", articleId.value] }),
    ]);
  },
  onError: (error) => {
    message.error(formatError(error));
  },
});

const saveDisabled = computed(
  () => editorLocked.value || saveMutation.isPending.value || title.value.trim() === "" || !hasChanges.value,
);
const leaveSaveDisabled = computed(
  () => editorLocked.value || saveMutation.isPending.value || title.value.trim() === "",
);

async function saveArticle(): Promise<boolean> {
  if (leaveSaveDisabled.value || !hasChanges.value) {
    return false;
  }

  try {
    await saveMutation.mutateAsync();
    return true;
  } catch {
    return false;
  }
}

async function handleSave(): Promise<void> {
  if (saveDisabled.value) {
    return;
  }

  await saveArticle();
}

async function handlePublish(): Promise<void> {
  if (editorLocked.value) {
    return;
  }

  if (hasChanges.value && !saveMutation.isPending.value) {
    try {
      await saveMutation.mutateAsync();
    } catch {
      return;
    }
  }

  message.warning(t("article.editor.messages.publishPending"));
}

function handleBack(): void {
  if (saveMutation.isPending.value) {
    return;
  }

  if (hasChanges.value) {
    leaveModalOpen.value = true;
    return;
  }

  navigateBack();
}

function navigateBack(): void {
  if (window.history.length > 1) {
    void router.back();
    return;
  }

  void router.push("/articles/templates");
}

function handleStay(): void {
  leaveModalOpen.value = false;
}

function handleDiscardLeave(): void {
  leaveModalOpen.value = false;
  navigateBack();
}

async function handleSaveAndLeave(): Promise<void> {
  const saved = await saveArticle();
  if (!saved) {
    return;
  }

  leaveModalOpen.value = false;
  navigateBack();
}

function handleCoverChange(event: Event): void {
  const input = event.target as HTMLInputElement;
  const file = input.files?.[0];
  if (!file) {
    return;
  }

  if (coverPreviewUrl.value) {
    URL.revokeObjectURL(coverPreviewUrl.value);
  }

  coverPreviewUrl.value = URL.createObjectURL(file);
}

onBeforeUnmount(() => {
  if (coverPreviewUrl.value) {
    URL.revokeObjectURL(coverPreviewUrl.value);
  }
});

function stripLeadingTitleHeading(titleValue: string, markdownValue: string): string {
  const normalizedTitle = titleValue.trim();
  if (!normalizedTitle) {
    return markdownValue;
  }

  const normalizedMarkdown = markdownValue.replace(/^\uFEFF/, "");
  const match = normalizedMarkdown.match(/^#\s+(.+?)\s*(\r?\n|$)/);
  if (!match) {
    return markdownValue;
  }

  if (match[1].trim() !== normalizedTitle) {
    return markdownValue;
  }

  return normalizedMarkdown.slice(match[0].length).replace(/^\s*\r?\n/, "");
}

function serializePlatformSelection(platformIds: string[]): string {
  return [...platformIds]
    .map((platformId) => platformId.trim())
    .filter(Boolean)
    .sort()
    .join(",");
}
</script>

<template>
  <div class="article-editor-view">
    <header class="article-editor-view__topbar">
      <button class="article-editor-view__back" type="button" @click="handleBack">
        <LeftOutlined />
        <span>{{ t("common.back") }}</span>
      </button>

      <div class="article-editor-view__actions">
      <a-button :disabled="saveDisabled" :loading="saveMutation.isPending.value" @click="handleSave">
          {{ t("common.save") }}
        </a-button>
        <a-button type="primary" :disabled="editorLocked" @click="handlePublish">
          {{ t("article.editor.publish") }}
        </a-button>
      </div>
    </header>

    <div v-if="detailQuery.isPending.value" class="article-editor-view__loading">
      <a-skeleton active :paragraph="{ rows: 14 }" />
    </div>

    <template v-else-if="detail">
      <a-alert
        v-if="editorLocked"
        class="article-editor-view__alert"
        type="info"
        show-icon
        :message="t('article.editor.messages.locked')"
      />
      <div class="article-editor-view__layout">
        <section class="article-editor-view__main">
          <MilkdownProvider>
            <ArticleEditorCanvas
              :key="String(articleId)"
              v-model:title="title"
              v-model="markdown"
              :disabled="editorLocked"
            />
          </MilkdownProvider>
        </section>

        <aside class="article-editor-view__rail">
          <section class="article-editor-view__card">
            <h3>{{ t("article.editor.platformsTitle") }}</h3>
            <p>{{ t("article.editor.platformsHint") }}</p>
            <PublishPlatformSelector
              v-model="publishPlatforms"
              class="article-editor-view__platforms"
              :platforms="boundPublishPlatforms"
              :disabled="editorLocked"
            />
          </section>

          <section class="article-editor-view__card">
            <div class="article-editor-view__card-head">
              <div>
                <h3>{{ t("article.editor.coverTitle") }}</h3>
                <p>{{ t("article.editor.coverHint") }}</p>
              </div>
              <a-switch v-model:checked="coverEnabled" />
            </div>

            <label
              class="article-editor-view__cover-dropzone"
              :class="{ 'article-editor-view__cover-dropzone--disabled': !coverEnabled }"
            >
              <input
                type="file"
                accept="image/*"
                :disabled="!coverEnabled"
                hidden
                @change="handleCoverChange"
              />
              <template v-if="coverPreviewUrl">
                <img :src="coverPreviewUrl" alt="cover preview" />
              </template>
              <template v-else>
                <span class="article-editor-view__cover-plus">+</span>
                <span>{{ t("article.editor.coverUpload") }}</span>
              </template>
            </label>
          </section>
        </aside>
      </div>
    </template>

    <a-empty v-else :description="t('common.noData')" />

    <a-modal
      :open="leaveModalOpen"
      :title="t('article.editor.leaveConfirm.title')"
      :closable="false"
      :mask-closable="false"
      @cancel="handleStay"
    >
      <p class="article-editor-view__leave-copy">
        {{ t("article.editor.leaveConfirm.description") }}
      </p>

      <template #footer>
        <a-button @click="handleStay">
          {{ t("common.cancel") }}
        </a-button>
        <a-button @click="handleDiscardLeave">
          {{ t("article.editor.leaveConfirm.discard") }}
        </a-button>
        <a-button
          type="primary"
          :loading="saveMutation.isPending.value"
          :disabled="leaveSaveDisabled"
          @click="handleSaveAndLeave"
        >
          {{ t("article.editor.leaveConfirm.saveAndLeave") }}
        </a-button>
      </template>
    </a-modal>
  </div>
</template>

<style scoped>
.article-editor-view {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.article-editor-view__topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.article-editor-view__back {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  border: 0;
  background: transparent;
  color: #111827;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.article-editor-view__back :deep(.anticon) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid rgba(144, 157, 181, 0.24);
}

.article-editor-view__actions {
  display: flex;
  gap: 12px;
}

.article-editor-view__main {
  height: calc(100vh - 180px);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid rgba(216, 225, 237, 0.9);
  border-radius: 28px;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.05);
  backdrop-filter: blur(14px);
}

.article-editor-view__loading,
.article-editor-view__card {
  padding: 24px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid rgba(216, 225, 237, 0.9);
  border-radius: 28px;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.05);
  backdrop-filter: blur(14px);
}

.article-editor-view__alert {
  border-radius: 20px;
}

.article-editor-view__leave-copy {
  margin: 0;
  color: #475467;
  line-height: 1.75;
}

.article-editor-view__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 18px;
  align-items: start;
}

.article-editor-view__main {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.article-editor-view__card h3 {
  margin: 0;
  color: #101828;
  font-size: 15px;
  font-weight: 700;
}

.article-editor-view__rail {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.article-editor-view__card p {
  margin: 6px 0 0;
  color: #667085;
  font-size: 13px;
  line-height: 1.7;
}

.article-editor-view__platforms {
  margin-top: 18px;
}

.article-editor-view__card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.article-editor-view__cover-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 176px;
  margin-top: 18px;
  border: 1px dashed #d3dceb;
  border-radius: 20px;
  background:
    radial-gradient(circle at top, rgba(70, 102, 255, 0.08), transparent 45%),
    #fbfcff;
  color: #475467;
  cursor: pointer;
  overflow: hidden;
}

.article-editor-view__cover-dropzone img {
  width: 100%;
  height: 176px;
  object-fit: cover;
}

.article-editor-view__cover-dropzone--disabled {
  opacity: 0.48;
  cursor: not-allowed;
}

.article-editor-view__cover-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: #eef4ff;
  color: #355dff;
  font-size: 28px;
  line-height: 1;
}

@media (max-width: 1200px) {
  .article-editor-view__layout {
    grid-template-columns: 1fr;
  }

  .article-editor-view__rail {
    order: -1;
  }
}

@media (max-width: 768px) {
  .article-editor-view__topbar {
    flex-direction: column;
    align-items: stretch;
  }

  .article-editor-view__actions {
    width: 100%;
  }

  .article-editor-view__actions :deep(.ant-btn) {
    flex: 1;
  }
}
</style>
