<script setup lang="ts">
import {
  CheckCircleOutlined,
  CloseOutlined,
  EditOutlined,
  FileTextOutlined,
  LeftOutlined,
  SearchOutlined,
  SendOutlined,
  WalletOutlined,
} from '@ant-design/icons-vue'
import type {
  ArticleDetail,
  ArticleListItem,
  ArticleListParams,
  CreateMediaSupplyOrderRequest,
} from '@geo/shared-types'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { message } from 'ant-design-vue'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'

import ArticleEditorCanvas from '@/components/ArticleEditorCanvas.vue'
import { articlesApi, imagesApi, mediaSupplyApi } from '@/lib/api'
import { formatDateTime, getGenerateStatusMeta, getSourceTypeLabel } from '@/lib/display'
import { formatError } from '@/lib/errors'
import {
  clearMediaSupplySubmitSelection,
  loadMediaSupplySubmitSelection,
  type MediaSupplySubmitResourceSnapshot,
} from '@/lib/media-supply-submit-selection'
import { useCompanyStore } from '@/stores/company'

const MODEL_ID_AUTHORITY_NEWS = 1
const ARTICLE_SOURCE_ALL = 'template,kol,custom_generation,imitation,free_create'

type SubmitMode = 'manual' | 'article'

const router = useRouter()
const queryClient = useQueryClient()
const companyStore = useCompanyStore()

const resources = ref<MediaSupplySubmitResourceSnapshot[]>(loadMediaSupplySubmitSelection())
const mode = ref<SubmitMode>('article')
const articlePage = ref(1)
const articlePageSize = ref(8)
const articleKeyword = ref('')
const articleSourceType = ref(ARTICLE_SOURCE_ALL)
const selectedArticleId = ref<number | null>(null)
const selectedArticleLoading = ref(false)
const form = reactive({
  title: '',
  content: '',
})

const sourceOptions = [
  { label: '全部来源', value: ARTICLE_SOURCE_ALL },
  { label: '模版生成', value: 'template,kol' },
  { label: '自定义生成', value: 'custom_generation' },
  { label: '仿写创作', value: 'imitation' },
  { label: '自由创作', value: 'free_create' },
]

const walletQuery = useQuery({
  queryKey: ['media-supply', 'wallet'],
  queryFn: () => mediaSupplyApi.wallet(),
})

const articleParams = computed<ArticleListParams>(() => {
  const params: ArticleListParams = {
    page: articlePage.value,
    page_size: articlePageSize.value,
    generate_status: 'completed',
    source_type: articleSourceType.value,
  }
  if (articleKeyword.value.trim()) {
    params.keyword = articleKeyword.value.trim()
  }
  return params
})

const articlesQuery = useQuery({
  queryKey: computed(() => [
    'articles',
    'media-supply-submit',
    companyStore.currentBrandId,
    articleParams.value,
  ]),
  enabled: computed(() => mode.value === 'article' && Boolean(companyStore.currentBrandId)),
  queryFn: () => articlesApi.list(articleParams.value),
})

const selectedTotal = computed(() =>
  resources.value.reduce((sum, resource) => sum + resource.sell_price_cents, 0),
)
const walletBalance = computed(() => walletQuery.data.value?.balance_cents ?? 0)
const balanceEnough = computed(() => walletBalance.value >= selectedTotal.value)
const articleRows = computed(() => articlesQuery.data.value?.items ?? [])
const articleTotal = computed(() => articlesQuery.data.value?.total ?? 0)
const canSubmit = computed(
  () =>
    resources.value.length > 0 &&
    form.title.trim() !== '' &&
    form.content.trim() !== '' &&
    balanceEnough.value,
)

const createOrderMutation = useMutation({
  mutationFn: () => mediaSupplyApi.createOrder(buildCreateOrderPayload()),
  onSuccess: async () => {
    message.success('投稿已进入队列')
    clearMediaSupplySubmitSelection()
    await Promise.all([
      queryClient.invalidateQueries({ queryKey: ['media-supply', 'orders'] }),
      queryClient.invalidateQueries({ queryKey: ['media-supply', 'wallet'] }),
      queryClient.invalidateQueries({ queryKey: ['media-supply', 'wallet-ledgers'] }),
    ])
    void router.replace({ name: 'media-supply-orders' })
  },
  onError: (error) => message.error(formatError(error)),
})

function buildCreateOrderPayload(): CreateMediaSupplyOrderRequest {
  return {
    article_id: mode.value === 'article' ? selectedArticleId.value : undefined,
    model_id: MODEL_ID_AUTHORITY_NEWS,
    title: form.title.trim(),
    content: form.content.trim(),
    items: resources.value.map((resource) => ({
      resource_id: resource.id,
      price_type: 'price',
    })),
  }
}

function setMode(nextMode: SubmitMode): void {
  mode.value = nextMode
  if (nextMode === 'manual') {
    selectedArticleId.value = null
  }
}

function applyArticleSearch(): void {
  articlePage.value = 1
  void articlesQuery.refetch()
}

function handleArticlePageChange(nextPage: number, nextPageSize: number): void {
  articlePage.value = nextPage
  articlePageSize.value = nextPageSize
}

async function selectArticle(article: ArticleListItem): Promise<void> {
  if (selectedArticleLoading.value) {
    return
  }
  selectedArticleLoading.value = true
  try {
    const detail = await articlesApi.detail(article.id)
    selectedArticleId.value = detail.id
    form.title = detail.title?.trim() || article.title?.trim() || ''
    form.content = stripLeadingTitleHeading(form.title, detail.markdown_content?.trim() || '')
    if (!form.content.trim()) {
      message.warning('这篇文章暂无正文内容')
    }
  } catch (error) {
    message.error(formatError(error))
  } finally {
    selectedArticleLoading.value = false
  }
}

async function submitOrder(): Promise<void> {
  if (resources.value.length === 0) {
    message.warning('请选择媒体')
    return
  }
  if (!form.title.trim() || !form.content.trim()) {
    message.warning('请填写标题和正文')
    return
  }

  const refreshedWallet = await walletQuery.refetch()
  const latestBalance = refreshedWallet.data?.balance_cents ?? walletBalance.value
  if (latestBalance < selectedTotal.value) {
    message.warning('媒体投稿余额不足')
    return
  }

  await createOrderMutation.mutateAsync()
}

async function uploadEditorImage(file: File): Promise<string> {
  const result = await imagesApi.upload(file)
  return result.url
}

function cancelSubmit(): void {
  void router.push({ name: 'media-supply-resources' })
}

function articleTitle(article: ArticleListItem): string {
  return article.title?.trim() || '未命名文章'
}

function formatMoney(cents?: number | null): string {
  if (typeof cents !== 'number' || !Number.isFinite(cents)) {
    return '--'
  }
  return `￥${(cents / 100).toFixed(2)}`
}

function formatSellPrice(cents?: number | null): string {
  if (typeof cents !== 'number' || !Number.isFinite(cents)) {
    return '--'
  }
  return `￥${Math.ceil(cents / 100)}`
}

function displayValue(value?: string | null): string {
  return value?.trim() || '--'
}

function sourceLabel(
  article: Pick<ArticleDetail | ArticleListItem, 'source_type' | 'generation_mode'>,
): string {
  return getSourceTypeLabel(article.source_type, article.generation_mode)
}

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 || match[1].trim() !== normalizedTitle) {
    return markdownValue
  }

  return normalizedMarkdown.slice(match[0].length).replace(/^\s*\r?\n/, '')
}
</script>

<template>
  <div class="media-supply-submit-page">
    <header class="submit-topbar">
      <button type="button" class="back-button" @click="cancelSubmit">
        <LeftOutlined />
        <span>提交投稿</span>
      </button>
      <button type="button" class="close-button" aria-label="关闭" @click="cancelSubmit">
        <CloseOutlined />
      </button>
    </header>

    <a-empty v-if="resources.length === 0" description="还没有选择媒体">
      <template #extra>
        <a-button type="primary" @click="cancelSubmit">返回选择媒体</a-button>
      </template>
    </a-empty>

    <template v-else>
      <section class="summary-grid">
        <article class="summary-tile">
          <span>媒体数量</span>
          <strong>{{ resources.length }}</strong>
        </article>
        <article class="summary-tile">
          <span>投稿金额</span>
          <strong>{{ formatSellPrice(selectedTotal) }}</strong>
        </article>
        <article class="summary-tile" :class="{ 'summary-tile--danger': !balanceEnough }">
          <span>投稿余额</span>
          <strong>{{ formatMoney(walletBalance) }}</strong>
        </article>
      </section>

      <div class="submit-layout">
        <aside class="submit-sidebar">
          <section class="selected-media">
            <div class="section-head">
              <h2>已选媒体</h2>
              <a-tag :color="balanceEnough ? 'green' : 'red'">
                {{ balanceEnough ? '余额通过' : '余额不足' }}
              </a-tag>
            </div>
            <div class="media-list">
              <article v-for="resource in resources" :key="resource.id" class="media-row">
                <div>
                  <strong>{{ resource.name }}</strong>
                  <span>
                    {{ displayValue(resource.channel_type) }} · {{ displayValue(resource.region) }}
                  </span>
                </div>
                <em>{{ formatSellPrice(resource.sell_price_cents) }}</em>
                <p v-if="resource.resource_remark">{{ resource.resource_remark }}</p>
              </article>
            </div>
          </section>

          <section class="article-picker">
            <div class="section-head">
              <h2>稿件来源</h2>
            </div>
            <div class="mode-switch" role="tablist" aria-label="稿件来源">
              <button
                type="button"
                :class="{ 'mode-switch__item--active': mode === 'article' }"
                class="mode-switch__item"
                @click="setMode('article')"
              >
                <FileTextOutlined />
                选择文章
              </button>
              <button
                type="button"
                :class="{ 'mode-switch__item--active': mode === 'manual' }"
                class="mode-switch__item"
                @click="setMode('manual')"
              >
                <EditOutlined />
                手写稿件
              </button>
            </div>

            <div v-if="mode === 'article'" class="article-picker__body">
              <div class="article-filters">
                <a-input
                  v-model:value="articleKeyword"
                  allow-clear
                  placeholder="搜索文章"
                  @press-enter="applyArticleSearch"
                >
                  <template #prefix><SearchOutlined /></template>
                </a-input>
                <a-select
                  v-model:value="articleSourceType"
                  :options="sourceOptions"
                  @change="applyArticleSearch"
                />
              </div>
              <a-spin :spinning="articlesQuery.isPending.value || selectedArticleLoading">
                <div v-if="articleRows.length" class="article-list">
                  <button
                    v-for="article in articleRows"
                    :key="article.id"
                    type="button"
                    class="article-row"
                    :class="{ 'article-row--active': selectedArticleId === article.id }"
                    @click="selectArticle(article)"
                  >
                    <span class="article-row__title">{{ articleTitle(article) }}</span>
                    <span class="article-row__meta">
                      {{ sourceLabel(article) }} · {{ formatDateTime(article.created_at) }}
                    </span>
                    <span class="article-row__foot">
                      <a-tag :color="getGenerateStatusMeta(article.generate_status).color">
                        {{ getGenerateStatusMeta(article.generate_status).label }}
                      </a-tag>
                      <span>{{ article.word_count }} 字</span>
                      <CheckCircleOutlined v-if="selectedArticleId === article.id" />
                    </span>
                  </button>
                </div>
                <a-empty v-else description="暂无可选文章" />
              </a-spin>
              <a-pagination
                size="small"
                :current="articlePage"
                :page-size="articlePageSize"
                :total="articleTotal"
                :show-size-changer="false"
                @change="handleArticlePageChange"
              />
            </div>
          </section>
        </aside>

        <main class="submit-main">
          <section class="editor-shell">
            <div class="editor-canvas-wrap">
              <ArticleEditorCanvas
                v-model:title="form.title"
                v-model="form.content"
                :article-id="null"
                :ai-optimize-enabled="false"
                :upload-image="uploadEditorImage"
              />
            </div>
          </section>
        </main>
      </div>

      <footer class="submit-footer">
        <div class="footer-balance" :class="{ 'footer-balance--danger': !balanceEnough }">
          <WalletOutlined />
          <span>
            合计 {{ formatSellPrice(selectedTotal) }}，余额 {{ formatMoney(walletBalance) }}
          </span>
        </div>
        <div class="footer-actions">
          <a-button @click="cancelSubmit">取消</a-button>
          <a-button
            type="primary"
            :disabled="!canSubmit"
            :loading="createOrderMutation.isPending.value"
            @click="submitOrder"
          >
            <template #icon><SendOutlined /></template>
            确认投稿
          </a-button>
        </div>
      </footer>
    </template>
  </div>
</template>

<style scoped>
.media-supply-submit-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: calc(100vh - 132px);
  color: #1f2937;
}

.submit-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.back-button,
.close-button,
.mode-switch__item,
.article-row {
  border: 0;
  background: transparent;
  cursor: pointer;
}

.back-button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  color: #111827;
  font-size: 18px;
  font-weight: 800;
}

.back-button :deep(.anticon) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: 1px solid rgba(144, 157, 181, 0.24);
  background: #fff;
}

.close-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  color: #475467;
  border: 1px solid #e5e7eb;
  background: #fff;
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

.summary-tile {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 92px;
  padding: 18px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: #fff;
}

.summary-tile span {
  color: #667085;
  font-size: 13px;
}

.summary-tile strong {
  color: #101828;
  font-size: 28px;
  line-height: 1;
}

.summary-tile--danger strong {
  color: #d4380d;
}

.submit-layout {
  display: grid;
  grid-template-columns: minmax(300px, 360px) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.submit-sidebar,
.submit-main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.selected-media,
.article-picker {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: #fff;
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid #eef2f7;
}

.section-head h2 {
  margin: 0;
  color: #111827;
  font-size: 15px;
  font-weight: 800;
}

.media-list {
  display: flex;
  flex-direction: column;
  max-height: 360px;
  overflow-y: auto;
}

.media-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 12px;
  padding: 13px 16px;
  border-bottom: 1px solid #f0f2f5;
}

.media-row:last-child {
  border-bottom: 0;
}

.media-row strong,
.article-row__title {
  display: block;
  overflow: hidden;
  color: #111827;
  font-size: 14px;
  font-weight: 800;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.media-row span,
.article-row__meta,
.article-row__foot {
  color: #667085;
  font-size: 12px;
}

.media-row em {
  color: #e9232f;
  font-style: normal;
  font-weight: 800;
}

.media-row p {
  grid-column: 1 / -1;
  margin: 0;
  overflow: hidden;
  color: #667085;
  font-size: 12px;
  line-height: 1.55;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mode-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 12px;
}

.mode-switch__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 36px;
  color: #475467;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  font-weight: 700;
}

.mode-switch__item--active {
  color: #e9232f;
  border-color: #ffb3ba;
  background: #fff4f4;
}

.article-picker__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 0 12px 14px;
}

.article-filters {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px;
  gap: 8px;
}

.article-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.article-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  padding: 12px;
  text-align: left;
  border: 1px solid #edf1f7;
  border-radius: 8px;
  background: #fbfcff;
}

.article-row--active {
  border-color: #ff9ca7;
  background: #fff7f7;
}

.article-row__foot {
  display: flex;
  align-items: center;
  gap: 8px;
}

.article-row__foot :deep(.anticon) {
  margin-left: auto;
  color: #16a34a;
}

.editor-shell {
  display: flex;
  flex-direction: column;
  min-height: 720px;
  overflow: hidden;
  background: transparent;
}

.editor-canvas-wrap {
  display: flex;
  flex: 1;
  min-height: 0;
  background: #fff;
}

.editor-canvas-wrap :deep(.article-editor-canvas) {
  flex: 1;
}

.editor-canvas-wrap :deep(.article-editor-canvas__surface .ProseMirror) {
  min-height: 560px;
}

.submit-footer {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding: 14px 16px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(10px);
}

.footer-balance {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: #1554ad;
  font-weight: 800;
}

.footer-balance--danger {
  color: #d4380d;
}

.footer-actions {
  display: flex;
  gap: 10px;
}

@media (max-width: 1120px) {
  .submit-layout {
    grid-template-columns: 1fr;
  }

  .media-list {
    max-height: 260px;
  }
}

@media (max-width: 720px) {
  .summary-grid {
    grid-template-columns: 1fr;
  }

  .submit-footer {
    position: static;
    flex-direction: column;
    align-items: stretch;
  }

  .footer-actions {
    width: 100%;
  }

  .footer-actions :deep(.ant-btn) {
    flex: 1;
  }

  .article-filters {
    grid-template-columns: 1fr;
  }

  .editor-shell {
    min-height: 620px;
  }

  .editor-canvas-wrap :deep(.article-editor-canvas__title-row .ant-input) {
    font-size: 24px;
  }
}
</style>
