'use client'

import { ChangeEvent, useEffect, useMemo, useState } from 'react'
import JSZip from 'jszip'
import { FileArchive, Loader2, Pencil, RefreshCw, X } from 'lucide-react'

import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { generateCertificateAsset, loadImageSource, type ImageSource } from '@/lib/certificate-browser'
import { sanitizeFileName } from '@/lib/certificate-types'
import {
  type ClassItem,
  type GeneratedCertificate,
  blobFromSource,
  createResultReport,
  downloadBlob,
  hydrateCertificate,
  hydrateClassMetadata,
  loadAppState,
  persistGeneratedCertificate,
  saveAppState
} from '@/lib/certificate-page-utils'
import type { PersistedAppState } from '@/lib/storage-types'

export default function GeneratedPage() {
  const [storedState, setStoredState] = useState<PersistedAppState | null>(null)
  const [classes, setClasses] = useState<ClassItem[]>([])
  const [selectedClassId, setSelectedClassId] = useState('')
  const [generatedCertificates, setGeneratedCertificates] = useState<GeneratedCertificate[]>([])
  const [countsByClassId, setCountsByClassId] = useState<Record<string, number>>({})
  const [isWorking, setIsWorking] = useState(false)
  const [editingCertificateId, setEditingCertificateId] = useState('')
  const [editingName, setEditingName] = useState('')
  const [editingPhoto, setEditingPhoto] = useState<ImageSource | null>(null)

  const selectedClass = classes.find((classItem) => classItem.id === selectedClassId) ?? classes[0]
  const editingCertificate = generatedCertificates.find(
    (certificate) => certificate.id === editingCertificateId
  )
  const currentCertificates = useMemo(
    () => generatedCertificates.filter((certificate) => certificate.classId === selectedClass?.id),
    [generatedCertificates, selectedClass?.id]
  )

  useEffect(() => {
    async function loadGeneratedIndex() {
      const state = await loadAppState()
      const restoredClasses = (state.classes ?? []).map(hydrateClassMetadata)
      const counts = (state.certificates ?? []).reduce<Record<string, number>>((result, certificate) => {
        result[certificate.classId] = (result[certificate.classId] ?? 0) + 1
        return result
      }, {})
      const classIdFromUrl = new URLSearchParams(window.location.search).get('classId') ?? ''
      const initialClassId =
        classIdFromUrl ||
        state.activeClassId ||
        restoredClasses[0]?.id ||
        state.certificates?.[0]?.classId ||
        ''

      setStoredState(state)
      setClasses(restoredClasses)
      setCountsByClassId(counts)
      setSelectedClassId(initialClassId)
    }

    loadGeneratedIndex()
  }, [])

  useEffect(() => {
    async function loadSelectedClassCertificates() {
      if (!storedState || !selectedClassId) {
        return
      }

      if (generatedCertificates.some((certificate) => certificate.classId === selectedClassId)) {
        return
      }

      setIsWorking(true)

      try {
        const restoredCertificates = await Promise.all(
          (storedState.certificates ?? [])
            .filter((certificate) => certificate.classId === selectedClassId)
            .map(hydrateCertificate)
        )
        setGeneratedCertificates((current) => [
          ...current.filter((certificate) => certificate.classId !== selectedClassId),
          ...restoredCertificates
        ])
      } finally {
        setIsWorking(false)
      }
    }

    loadSelectedClassCertificates()
  }, [generatedCertificates, selectedClassId, storedState])

  async function persistCertificates(nextCertificates: GeneratedCertificate[]) {
    if (!storedState || !selectedClass) {
      return
    }

    const persistedCurrentClass = await Promise.all(
      nextCertificates
        .filter((certificate) => certificate.classId === selectedClass.id)
        .map(persistGeneratedCertificate)
    )
    const nextState: PersistedAppState = {
      ...storedState,
      activeClassId: selectedClass.id,
      certificates: [
        ...(storedState.certificates ?? []).filter(
          (certificate) => certificate.classId !== selectedClass.id
        ),
        ...persistedCurrentClass
      ]
    }

    await saveAppState(nextState)
    setStoredState(nextState)
    setCountsByClassId((current) => ({
      ...current,
      [selectedClass.id]: persistedCurrentClass.length
    }))
  }

  async function downloadClassCertificates() {
    if (!selectedClass || currentCertificates.length === 0) {
      return
    }

    const zip = new JSZip()

    for (const certificate of currentCertificates) {
      const fileName = sanitizeFileName(certificate.studentName)
      zip.file(`jpg/${fileName}.jpg`, certificate.asset.jpg ?? await blobFromSource(certificate.jpgUrl))
    }
    zip.file('生成结果.csv', createResultReport(currentCertificates, selectedClass))

    downloadBlob(
      await zip.generateAsync({ type: 'blob', compression: 'DEFLATE' }),
      `${sanitizeFileName(selectedClass.name)}毕业证书.zip`
    )
  }

  function startEdit(certificate: GeneratedCertificate) {
    setEditingCertificateId(certificate.id)
    setEditingName(certificate.studentName)
    setEditingPhoto(certificate.photo)
  }

  function closeEdit() {
    setEditingCertificateId('')
    setEditingName('')
    setEditingPhoto(null)
  }

  async function handleEditPhoto(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0]
    setEditingPhoto(file ? await loadImageSource(file, file.name) : null)
  }

  async function saveEditedCertificate() {
    if (!editingCertificate || !editingPhoto || !editingName.trim()) {
      return
    }

    setIsWorking(true)

    try {
      const asset = await generateCertificateAsset(
        editingCertificate.template.image,
        editingPhoto,
        editingName.trim(),
        editingCertificate.template.config
      )
      const nextCertificate: GeneratedCertificate = {
        ...editingCertificate,
        studentName: editingName.trim(),
        photo: editingPhoto,
        asset,
        jpgUrl: URL.createObjectURL(asset.jpg),
        updatedAt: new Date().toISOString()
      }
      const nextCertificates = generatedCertificates.map((certificate) =>
        certificate.id === editingCertificate.id ? nextCertificate : certificate
      )

      if (editingCertificate.jpgUrl.startsWith('blob:')) {
        URL.revokeObjectURL(editingCertificate.jpgUrl)
      }
      setGeneratedCertificates(nextCertificates)
      await persistCertificates(nextCertificates)
      closeEdit()
    } finally {
      setIsWorking(false)
    }
  }

  return (
    <div className="space-y-5">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div>
          <p className="text-sm text-muted-foreground">Generated Certificates</p>
          <h2 className="text-2xl font-semibold tracking-normal">生成完成的毕业证书</h2>
        </div>
        <div className="flex flex-wrap gap-2">
          <select
            className="h-10 rounded-md border bg-background px-3 text-sm"
            value={selectedClass?.id ?? ''}
            onChange={(event) => setSelectedClassId(event.target.value)}
          >
            {classes.map((classItem) => (
              <option key={classItem.id} value={classItem.id}>
                {classItem.name} ({countsByClassId[classItem.id] ?? 0})
              </option>
            ))}
          </select>
          <Button type="button" variant="outline" onClick={downloadClassCertificates}>
            <FileArchive />
            下载当前班级全部证书
          </Button>
        </div>
      </div>

      {isWorking && currentCertificates.length === 0 ? (
        <div className="rounded-md border border-dashed bg-card p-10 text-center text-muted-foreground">
          正在加载当前班级证书...
        </div>
      ) : currentCertificates.length > 0 ? (
        <div className="columns-1 gap-4 md:columns-2 2xl:columns-3">
          {currentCertificates.map((certificate) => (
            <Card key={certificate.id} className="mb-4 break-inside-avoid overflow-hidden">
              <div className="bg-muted p-2">
                <img
                  src={certificate.jpgUrl}
                  alt={`${certificate.studentName}的毕业证书`}
                  className="block w-full rounded-sm border bg-white object-contain"
                />
              </div>
              <CardContent className="space-y-3 p-4">
                <div className="space-y-1">
                  <div className="flex items-center justify-between gap-3">
                    <h3 className="text-base font-semibold">{certificate.studentName}</h3>
                    <span className="rounded-sm bg-accent px-2 py-1 text-xs text-accent-foreground">
                      {certificate.className}
                    </span>
                  </div>
                  <p className="text-sm text-muted-foreground">模板：{certificate.template.name}</p>
                </div>
                <div className="flex flex-wrap gap-2">
                  <Button asChild size="sm" variant="outline">
                    <a href={certificate.jpgUrl} download={`${sanitizeFileName(certificate.studentName)}.jpg`}>
                      <FileArchive />
                      下载 JPG
                    </a>
                  </Button>
                  <Button type="button" size="sm" variant="secondary" onClick={() => startEdit(certificate)}>
                    <Pencil />
                    编辑
                  </Button>
                </div>
              </CardContent>
            </Card>
          ))}
        </div>
      ) : (
        <div className="rounded-md border border-dashed bg-card p-10 text-center text-muted-foreground">
          当前班级还没有生成证书。
        </div>
      )}

      {editingCertificate ? (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center bg-foreground/35 p-4"
          role="dialog"
          aria-modal="true"
          aria-labelledby="edit-certificate-title"
        >
          <Card className="max-h-[90vh] w-full max-w-xl overflow-auto shadow-xl">
            <CardHeader className="flex-row items-center justify-between space-y-0">
              <CardTitle id="edit-certificate-title">编辑证书</CardTitle>
              <Button type="button" size="icon" variant="ghost" aria-label="关闭编辑弹窗" onClick={closeEdit}>
                <X />
              </Button>
            </CardHeader>
            <CardContent className="space-y-4">
              <div className="grid gap-4 sm:grid-cols-[1fr_180px]">
                <div className="space-y-3">
                  <div className="space-y-2">
                    <Label htmlFor="edit-name">姓名</Label>
                    <Input
                      id="edit-name"
                      value={editingName}
                      onChange={(event) => setEditingName(event.target.value)}
                    />
                  </div>
                  <div className="space-y-2">
                    <Label htmlFor="edit-photo">替换照片</Label>
                    <Input id="edit-photo" type="file" accept="image/jpeg,image/png" onChange={handleEditPhoto} />
                  </div>
                </div>
                <div className="rounded-md border bg-muted p-2">
                  {editingPhoto ? (
                    <img
                      src={editingPhoto.dataUrl}
                      alt={editingName || '替换照片预览'}
                      className="mx-auto max-h-52 rounded-sm object-contain"
                    />
                  ) : null}
                </div>
              </div>
              <div className="flex justify-end gap-2">
                <Button type="button" variant="outline" onClick={closeEdit}>
                  取消
                </Button>
                <Button type="button" disabled={isWorking} onClick={saveEditedCertificate}>
                  {isWorking ? <Loader2 className="animate-spin" /> : <RefreshCw />}
                  提交并重新生成
                </Button>
              </div>
            </CardContent>
          </Card>
        </div>
      ) : null}
    </div>
  )
}
