<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import GlassCard from '@/components/ui/GlassCard.vue'
import DataTable from '@/components/ui/DataTable.vue'
import StatusBadge from '@/components/ui/StatusBadge.vue'
import EmptyState from '@/components/ui/EmptyState.vue'
import { reviewsApi } from '@/api'
import type { ApiReviewLlmStatus, ApiReviewSentiment, ReviewDto } from '@/api/types'

type DataTableColumn = { key: string; label: string; align?: 'left' | 'center' | 'right' }

const columns: DataTableColumn[] = [
  { key: 'id', label: 'ID' },
  { key: 'lecture', label: 'Лекция' },
  { key: 'student', label: 'Студент' },
  { key: 'rating', label: 'Оценка', align: 'center' },
  { key: 'text', label: 'Текст' },
  { key: 'date', label: 'Дата' },
  { key: 'analysis', label: 'Результат нейронки' },
  { key: 'actions', label: 'Действия', align: 'right' },
]

const statusMap: Record<string, string> = {
  Pending: 'pending',
  Analyzed: 'done',
  Rejected: 'rejected',
}

const ratingLabel: Record<string, string> = {
  Like: '👍 Like',
  Neutral: '😐 Neutral',
  Dislike: '👎 Dislike',
}

const statusFilters: Array<{ label: string; value: ApiReviewLlmStatus | 'All' }> = [
  { label: 'Все', value: 'All' },
  { label: 'На проверке', value: 'Pending' },
  { label: 'Проверены', value: 'Analyzed' },
  { label: 'Отклонены', value: 'Rejected' },
]

const sentimentLabel: Record<ApiReviewSentiment, string> = {
  Positive: 'Позитивный',
  Neutral: 'Нейтральный',
  Negative: 'Негативный',
}

const sentimentClass: Record<ApiReviewSentiment, string> = {
  Positive: 'badge-green',
  Neutral: 'badge-orange',
  Negative: 'badge-red',
}

const reviews = ref<ReviewDto[]>([])
const loading = ref(false)
const page = ref(1)
const pageSize = ref(20)
const totalCount = ref(0)
const totalPages = ref(0)
const statusFilter = ref<ApiReviewLlmStatus | 'All'>('All')
const reanalyzingId = ref<number | null>(null)
const error = ref('')
const promptText = ref('')
const savedPromptText = ref('')
const promptUpdatedAt = ref<string | null>(null)
const promptLoading = ref(false)
const promptSaving = ref(false)
const promptError = ref('')
const promptSuccess = ref('')

const rows = computed(() =>
  reviews.value.map((review) => {
    const analysisReady = review.llmStatus === 'Analyzed'
    const sentiment = analysisReady ? review.sentiment : null
    const tags = analysisReady ? normalizeTags(review.llmTags) : []

    return {
      id: review.id,
      lecture: review.lectureTitle || `#${review.lectureId}`,
      student: review.userName || `#${review.userId}`,
      rating: ratingLabel[review.rating] ?? review.rating,
      text: review.text || '—',
      date: new Date(review.createdAt).toLocaleString('ru-RU'),
      status: statusMap[review.llmStatus] ?? review.llmStatus,
      rawStatus: review.llmStatus,
      analysis: review.llmStatus,
      analysisReady,
      analysisMessage: getAnalysisMessage(review),
      sentiment,
      sentimentLabel: sentiment ? sentimentLabel[sentiment] : '—',
      sentimentClass: sentiment ? sentimentClass[sentiment] : 'badge-gray',
      quality: analysisReady ? review.qualityScore : null,
      qualityLabel: analysisReady ? formatQuality(review.qualityScore) : '—',
      qualityClass: getQualityClass(analysisReady ? review.qualityScore : null),
      informative: analysisReady ? review.isInformative : null,
      informativeLabel: analysisReady ? formatInformative(review.isInformative) : '—',
      informativeClass: getInformativeClass(analysisReady ? review.isInformative : null),
      tags,
      llmRawOutput: analysisReady ? formatRawOutput(review.llmRawOutput) : '',
    }
  }),
)

const pageStart = computed(() =>
  totalCount.value === 0 ? 0 : (page.value - 1) * pageSize.value + 1,
)
const pageEnd = computed(() => Math.min(page.value * pageSize.value, totalCount.value))
const canGoPrev = computed(() => page.value > 1)
const canGoNext = computed(() => page.value < totalPages.value)
const promptStatusLabel = computed(() => {
  if (promptLoading.value) return 'Загрузка...'
  if (!promptUpdatedAt.value) return 'Базовый промпт'
  return `Обновлён ${new Date(promptUpdatedAt.value).toLocaleString('ru-RU')}`
})
const canSavePrompt = computed(
  () =>
    !promptLoading.value &&
    !promptSaving.value &&
    promptText.value.trim().length > 0 &&
    promptText.value !== savedPromptText.value,
)

function normalizeTags(tags: string[] | null | undefined) {
  return Array.from(new Set((tags ?? []).map((tag) => tag.trim()).filter(Boolean)))
}

function formatQuality(value: number | null | undefined) {
  if (value === null || value === undefined) return '—'
  const numeric = Number(value)
  if (!Number.isFinite(numeric)) return '—'
  const score = Math.max(0, Math.min(1, numeric))
  return `${Math.round(score * 100)}%`
}

function getQualityClass(value: number | null | undefined) {
  if (value === null || value === undefined) return 'badge-gray'
  if (!Number.isFinite(Number(value))) return 'badge-gray'
  if (value >= 0.7) return 'badge-green'
  if (value >= 0.4) return 'badge-orange'
  return 'badge-red'
}

function formatInformative(value: boolean | null | undefined) {
  if (value === null || value === undefined) return '—'
  return value ? 'Информативный' : 'Неинформативный'
}

function getInformativeClass(value: boolean | null | undefined) {
  if (value === null || value === undefined) return 'badge-gray'
  return value ? 'badge-green' : 'badge-red'
}

function getAnalysisMessage(review: ReviewDto) {
  if (review.llmStatus === 'Pending') return 'Ожидает обработки'
  if (review.llmStatus === 'Rejected') return 'Отзыв отклонён нейронкой'
  return 'Анализ завершён, данных нет'
}

function formatRawOutput(value: string | null | undefined) {
  return value?.trim() || ''
}

async function fetchPrompt() {
  promptLoading.value = true
  promptError.value = ''
  promptSuccess.value = ''
  try {
    const result = await reviewsApi.getPrompt()
    promptText.value = result.prompt
    savedPromptText.value = result.prompt
    promptUpdatedAt.value = result.updatedAt ?? null
  } catch (err) {
    promptError.value = err instanceof Error ? err.message : 'Не удалось загрузить промпт.'
  } finally {
    promptLoading.value = false
  }
}

async function savePrompt() {
  promptError.value = ''
  promptSuccess.value = ''

  if (!promptText.value.trim()) {
    promptError.value = 'Промпт не должен быть пустым.'
    return
  }

  promptSaving.value = true
  try {
    const result = await reviewsApi.updatePrompt({ prompt: promptText.value })
    promptText.value = result.prompt
    savedPromptText.value = result.prompt
    promptUpdatedAt.value = result.updatedAt ?? null
    promptSuccess.value = 'Промпт сохранён.'
  } catch (err) {
    promptError.value = err instanceof Error ? err.message : 'Не удалось сохранить промпт.'
  } finally {
    promptSaving.value = false
  }
}

async function fetchReviews() {
  loading.value = true
  error.value = ''
  try {
    const result = await reviewsApi.listPage({
      Page: page.value,
      PageSize: pageSize.value,
      ...(statusFilter.value === 'All' ? {} : { LlmStatus: statusFilter.value }),
    })
    reviews.value = result.items
    totalCount.value = result.totalCount
    totalPages.value = result.totalPages
  } catch (err) {
    error.value = err instanceof Error ? err.message : 'Не удалось загрузить отзывы.'
  } finally {
    loading.value = false
  }
}

async function selectStatusFilter(nextStatus: ApiReviewLlmStatus | 'All') {
  if (statusFilter.value === nextStatus) return
  statusFilter.value = nextStatus
  page.value = 1
  await fetchReviews()
}

async function goToPage(nextPage: number) {
  if (nextPage < 1 || (totalPages.value && nextPage > totalPages.value)) return
  page.value = nextPage
  await fetchReviews()
}

async function reanalyze(id: number) {
  reanalyzingId.value = id
  try {
    await reviewsApi.reanalyze(id)
    await fetchReviews()
  } catch (err) {
    error.value = err instanceof Error ? err.message : 'Не удалось запустить повторный анализ.'
  } finally {
    reanalyzingId.value = null
  }
}

onMounted(() => {
  void fetchPrompt()
  void fetchReviews()
})
</script>

<template>
  <div class="admin-reviews page-content">
    <div class="header">
      <div>
        <h1 class="page-title">Отзывы</h1>
        <p class="page-subtitle">Все отзывы студентов и результаты LLM-анализа.</p>
      </div>
      <button class="btn-primary" :disabled="loading" @click="fetchReviews">
        {{ loading ? 'Загрузка...' : 'Обновить отзывы' }}
      </button>
    </div>

    <GlassCard>
      <div class="review-toolbar">
        <div class="status-filters" aria-label="Фильтр статуса LLM">
          <button
            v-for="filter in statusFilters"
            :key="filter.value"
            type="button"
            :class="{ active: statusFilter === filter.value }"
            :disabled="loading"
            @click="selectStatusFilter(filter.value)"
          >
            {{ filter.label }}
          </button>
        </div>
      </div>

      <EmptyState v-if="error" title="Не удалось загрузить отзывы" :subtitle="error" />
      <EmptyState
        v-else-if="!rows.length && !loading"
        title="Отзывов нет"
        subtitle="Пока нет ни одного отзыва."
      />
      <div v-else class="table-section">
        <div class="pagination-bar">
          <span> Показаны {{ pageStart }}–{{ pageEnd }} из {{ totalCount }} отзывов </span>
          <div class="pagination-actions">
            <button class="btn-ghost" :disabled="loading || !canGoPrev" @click="goToPage(page - 1)">
              Назад
            </button>
            <span>Страница {{ page }} из {{ totalPages || 1 }}</span>
            <button class="btn-ghost" :disabled="loading || !canGoNext" @click="goToPage(page + 1)">
              Вперёд
            </button>
          </div>
        </div>
        <DataTable :columns="columns" :rows="rows">
          <template #text="{ row }">
            <span class="review-text" :title="row.text">{{ row.text }}</span>
          </template>
          <template #analysis="{ row }">
            <div class="analysis-cell">
              <div v-if="!row.analysisReady" class="analysis-head">
                <StatusBadge :status="row.status" />
              </div>
              <div v-if="row.analysisReady" class="analysis-result">
                <div class="analysis-metrics">
                  <span class="badge" :class="row.qualityClass">
                    Качество {{ row.qualityLabel }}
                  </span>
                  <span class="badge" :class="row.sentimentClass">{{ row.sentimentLabel }}</span>
                  <span class="badge" :class="row.informativeClass">
                    {{ row.informativeLabel }}
                  </span>
                </div>
                <div v-if="row.tags.length" class="analysis-tags">
                  <span v-for="tag in row.tags" :key="tag" class="badge badge-blue">{{ tag }}</span>
                </div>
                <span v-else class="analysis-note">Теги не найдены</span>
                <details v-if="row.llmRawOutput" class="raw-output">
                  <summary>Текст ответа нейронки</summary>
                  <pre>{{ row.llmRawOutput }}</pre>
                </details>
                <span v-else class="analysis-note">Текст ответа нейронки не сохранён</span>
              </div>
            </div>
          </template>
          <template #actions="{ row }">
            <button
              class="btn-ghost"
              :disabled="reanalyzingId === row.id"
              @click="reanalyze(row.id)"
            >
              {{ reanalyzingId === row.id ? 'Запускаем...' : 'Повторить анализ' }}
            </button>
          </template>
        </DataTable>
      </div>
    </GlassCard>

    <GlassCard>
      <div class="prompt-header">
        <div>
          <div class="section-title">Промпт LLM-анализа</div>
          <p class="prompt-subtitle">
            Шаблон применяется к новым проверкам и ручному повторному анализу отзывов.
          </p>
        </div>
        <span class="prompt-status">{{ promptStatusLabel }}</span>
      </div>

      <form class="prompt-form" @submit.prevent="savePrompt">
        <textarea
          v-model="promptText"
          class="glass-input prompt-textarea"
          rows="9"
          :disabled="promptLoading || promptSaving"
          placeholder="Загрузка промпта..."
        ></textarea>
        <div class="prompt-footer">
          <div class="prompt-messages">
            <span class="prompt-hint"
              >Обязательные плейсхолдеры: {lectureContext}, {reviewText}</span
            >
            <span v-if="promptError" class="prompt-error">{{ promptError }}</span>
            <span v-else-if="promptSuccess" class="prompt-success">{{ promptSuccess }}</span>
          </div>
          <button class="btn-primary" type="submit" :disabled="!canSavePrompt">
            {{ promptSaving ? 'Сохраняем...' : 'Сохранить промпт' }}
          </button>
        </div>
      </form>
    </GlassCard>
  </div>
</template>

<style scoped>
.admin-reviews {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.page-subtitle {
  margin: 4px 0 0;
  color: var(--color-text-secondary);
}
.section-title {
  font-size: 16px;
  font-weight: 700;
}
.prompt-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.prompt-subtitle {
  margin: 4px 0 0;
  color: var(--color-text-secondary);
  font-size: 13px;
}
.prompt-status {
  flex: 0 0 auto;
  border: 1px solid var(--color-border-glass);
  border-radius: 999px;
  padding: 5px 10px;
  background: var(--color-white-a72);
  color: var(--color-text-secondary);
  font-size: 12px;
}
.prompt-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.prompt-textarea {
  min-height: 250px;
  resize: vertical;
  line-height: 1.45;
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
    monospace;
}
.prompt-textarea:disabled {
  color: var(--color-text-secondary);
  cursor: wait;
}
.prompt-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.prompt-messages {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 240px;
  color: var(--color-text-secondary);
  font-size: 12px;
}
.prompt-error {
  color: var(--color-error);
}
.prompt-success {
  color: var(--color-success-text);
}
.review-text {
  display: inline-block;
  max-width: 320px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  vertical-align: middle;
}
.table-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.review-toolbar {
  display: flex;
  justify-content: flex-start;
  margin-bottom: 12px;
}
.status-filters {
  display: inline-flex;
  border: 1px solid var(--color-border-glass);
  border-radius: 10px;
  overflow: hidden;
}
.status-filters button {
  background: var(--color-white-a70);
  border: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  font-size: 13px;
  padding: 8px 14px;
}
.status-filters button.active {
  background: var(--color-primary-a18);
  color: var(--color-primary-dark);
  font-weight: 600;
}
.status-filters button:disabled {
  cursor: wait;
  opacity: 0.7;
}
.pagination-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  color: var(--color-text-secondary);
  font-size: 13px;
}
.pagination-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.analysis-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 260px;
}
.analysis-head,
.analysis-metrics,
.analysis-tags {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.analysis-result {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.analysis-note {
  color: var(--color-text-secondary);
  font-size: 12px;
}
.raw-output {
  max-width: 640px;
}
.raw-output summary {
  color: var(--color-primary-dark);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
}
.raw-output pre {
  margin: 6px 0 0;
  max-height: 220px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  border: 1px solid var(--color-border-glass);
  border-radius: 8px;
  background: var(--color-white-a60);
  padding: 10px;
  color: var(--color-text);
  font-size: 12px;
  line-height: 1.45;
}
</style>
