import { useEffect, useRef, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router'
import { ArrowLongLeftIcon, ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
import { useAuth } from '~/lib/auth-context'
import { useData } from '~/lib/data-context'
import {
  deletePost,
  getBskyPostText,
  getPost,
  getPostPdsData,
  updatePost,
  uploadBlob,
  uploadContentImages,
  type AtpBlob,
  type PostDetail,
  CC_LICENSES,
} from '~/lib/api-client'
import { compressImage } from '~/lib/compress-image'
import { resolveDid } from '~/lib/atproto'
import CodeMirrorMarkdownEditor from '~/components/CodeMirrorMarkdownEditor'
import CoverImagePicker from '~/components/CoverImagePicker'
import CenteredMessage from '~/components/CenteredMessage'
import Spinner from '~/components/Spinner'
import ConfirmDeleteButton from '~/components/ConfirmDeleteButton'

export default function EditPost() {
  const { session, loading } = useAuth()
  const navigate = useNavigate()

  useEffect(() => {
    if (!loading && !session) navigate('/')
  }, [loading, session, navigate])

  if (loading || !session) {
    return (
      <div className="flex-1 flex items-center justify-center">
        <Spinner />
      </div>
    )
  }

  return <EditScreen session={session} />
}

function EditScreen({ session }: { session: NonNullable<ReturnType<typeof useAuth>['session']> }) {
  const navigate = useNavigate()
  const { invalidatePosts, removePost } = useData()
  const { userId, pubRkey, rkey } = useParams<{ userId: string; pubRkey: string; rkey: string }>()
  const [post, setPost] = useState<PostDetail | null | 'not-owner'>(null)
  const [title, setTitle] = useState('')
  const [content, setContent] = useState('')
  const [visibility, setVisibility] = useState<'published' | 'draft'>('published')
  const [pending, setPending] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [saved, setSaved] = useState(false)
  const [coverImageBlob, setCoverImageBlob] = useState<AtpBlob | null>(null)
  const [coverImagePreviewUrl, setCoverImagePreviewUrl] = useState<string | null>(null)
  const [coverImageUploading, setCoverImageUploading] = useState(false)
  const [coverImageError, setCoverImageError] = useState<string | null>(null)
  const [bskyPostText, setBskyPostText] = useState('')
  const [license, setLicense] = useState('')
  const coverPreviewUrlRef = useRef<string | null>(null)

  useEffect(() => {
    if (!userId || !rkey) return
    let cancelled = false

    async function load() {
      try {
        const ownerDid = await resolveDid(userId!)
        if (cancelled) return
        if (ownerDid !== session.sub) {
          setPost('not-owner')
          return
        }
        const postData = await getPost(session, rkey!)
        if (cancelled) return
        setPost(postData)
        setTitle(postData.title)
        setContent(postData.content ?? '')
        setVisibility(postData.visibility)
        setLicense(postData.license ?? '')

        const pdsData = await getPostPdsData(session, rkey!, postData.visibility)
        if (cancelled) return
        if (pdsData.coverImage) setCoverImageBlob(pdsData.coverImage)
        if (pdsData.license !== undefined) setLicense(pdsData.license)
        const resolvedBskyRef = pdsData.bskyPostRef ?? postData.bskyPostRef
        if (pdsData.bskyPostRef && !postData.bskyPostRef) {
          setPost((prev) => prev && prev !== 'not-owner' ? { ...prev, bskyPostRef: pdsData.bskyPostRef } : prev)
        }
        if (resolvedBskyRef) {
          const text = await getBskyPostText(session, resolvedBskyRef.uri)
          if (!cancelled) setBskyPostText(text)
        }
      } catch (err) {
        if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load post')
      }
    }

    load()
    return () => { cancelled = true }
  }, [userId, rkey, session])

  async function handleCoverImageSelect(file: File) {
    setCoverImageError(null)
    setCoverImageUploading(true)
    try {
      const compressed = await compressImage(file)
      const localUrl = URL.createObjectURL(compressed)
      if (coverPreviewUrlRef.current) URL.revokeObjectURL(coverPreviewUrlRef.current)
      coverPreviewUrlRef.current = localUrl
      setCoverImagePreviewUrl(localUrl)
      const blob = await uploadBlob(session, compressed)
      setCoverImageBlob(blob)
    } catch (err) {
      setCoverImageError(err instanceof Error ? err.message : 'Upload failed')
      setCoverImageBlob(null)
    } finally {
      setCoverImageUploading(false)
    }
  }

  function handleCoverImageRemove() {
    if (coverPreviewUrlRef.current) {
      URL.revokeObjectURL(coverPreviewUrlRef.current)
      coverPreviewUrlRef.current = null
    }
    setCoverImagePreviewUrl(null)
    setCoverImageBlob(null)
    setCoverImageError(null)
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    if (!post || post === 'not-owner' || !title.trim()) return
    setPending(true)
    setError(null)
    setSaved(false)
    try {
      const { content: finalContent, imageBlobs } = await uploadContentImages(session, content)
      const bskyPostOptions = visibility === 'published' && post.resolvedUrl
        ? { text: bskyPostText.trim(), pubUrl: post.resolvedUrl }
        : undefined
      const updatedPost = await updatePost(session, rkey!, title.trim(), finalContent, visibility, post, coverImageBlob, bskyPostOptions, imageBlobs.length > 0 ? imageBlobs : undefined, license || undefined)
      if (visibility === 'published') {
        invalidatePosts()
        const publishedRkey = updatedPost.atUri.split('/').pop()!
        navigate(`/${userId}/pub/${pubRkey}/${publishedRkey}`)
      } else {
        setSaved(true)
        setPending(false)
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to save')
      setPending(false)
    }
  }

  async function handleDelete() {
    if (!post || post === 'not-owner' || !rkey) return
    try {
      await deletePost(session, rkey, post.bskyPostRef ?? undefined, post.visibility)
      removePost(rkey)
      navigate('/')
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to delete')
    }
  }

  if (post === 'not-owner') {
    return <CenteredMessage message="You can only edit your own articles." action={{ label: 'View post', to: `/${userId}/pub/${pubRkey}/${rkey}` }} />
  }

  if (error && !post) {
    return <CenteredMessage message={error} error action={{ label: 'Back to profile', to: `/${userId}` }} />
  }

  if (!post) {
    return (
      <div className="flex-1 flex items-center justify-center">
        <Spinner />
      </div>
    )
  }

  return (
    <div className="flex-1 flex flex-col p-4 md:p-8">
      <div className="w-full max-w-sm mx-auto md:max-w-none space-y-6">
        <div className="flex items-center justify-between gap-3">
          <div className="flex items-center gap-3">
            <Link to={visibility === 'draft' ? '/' : `/${userId}/pub/${pubRkey}/${rkey}`}>
              <ArrowLongLeftIcon className="size-9" />
            </Link>
            <h1 className="font-display text-xl font-semibold text-stone-900 dark:text-white">Edit article</h1>
          </div>
          <ConfirmDeleteButton onDelete={handleDelete} disabled={pending} />
        </div>

        <form onSubmit={handleSubmit} className="space-y-3">
          <div className="flex gap-3 flex-wrap">
            <input
              type="text"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder="Title"
              autoFocus
              disabled={pending}
              className="flex-1 min-w-0 rounded-lg border border-stone-300 dark:border-stone-600 bg-white dark:bg-stone-800 px-4 py-3 text-sm text-stone-900 dark:text-white placeholder-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-900 dark:focus:ring-stone-100 disabled:opacity-50"
            />
            <div className="flex rounded-lg border border-stone-300 dark:border-stone-600 overflow-hidden text-sm font-medium">
              {(['draft', 'published'] as const).map((v) => {
                const isDisabled = pending || (v === 'draft' && post.visibility === 'published')
                return (
                  <button
                    key={v}
                    type="button"
                    disabled={isDisabled}
                    onClick={() => setVisibility(v)}
                    title={v === 'draft' && post.visibility === 'published' ? 'Published articles cannot be reverted to draft' : undefined}
                    className={
                      visibility === v
                        ? 'px-4 py-3 bg-stone-900 dark:bg-stone-100 text-white dark:text-stone-900 transition-colors'
                        : 'px-4 py-3 bg-white dark:bg-stone-800 text-stone-500 dark:text-stone-400 hover:bg-stone-50 dark:hover:bg-stone-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed'
                    }
                  >
                    {v.charAt(0).toUpperCase() + v.slice(1)}
                  </button>
                )
              })}
            </div>
            <button
              type="submit"
              disabled={pending || !title.trim()}
              className="rounded-lg bg-stone-900 dark:bg-stone-100 hover:bg-stone-700 dark:hover:bg-stone-200 disabled:opacity-50 disabled:cursor-not-allowed px-6 py-3 text-sm font-semibold text-white dark:text-stone-900 transition-colors whitespace-nowrap"
            >
              {pending ? 'Saving…' : 'Save'}
            </button>
          </div>

          {error && <p className="text-sm text-red-500">{error}</p>}
          {saved && <p className="text-sm text-green-600 dark:text-green-400">Draft saved.</p>}

          <CoverImagePicker
            previewUrl={coverImagePreviewUrl}
            imageDid={coverImageBlob && !coverImagePreviewUrl ? session.sub : undefined}
            imageCid={coverImageBlob && !coverImagePreviewUrl ? coverImageBlob.ref.$link : undefined}
            uploading={coverImageUploading}
            error={coverImageError}
            onSelect={handleCoverImageSelect}
            onRemove={handleCoverImageRemove}
          />

          <div className="space-y-1.5">
            <label className="text-sm font-medium text-stone-500 dark:text-stone-400">
              License <span className="font-normal text-stone-400 dark:text-stone-500">(optional)</span>
              {' · '}
              <a href="https://creativecommons.org/cc-licenses/" target="_blank" rel="noopener noreferrer" className="font-normal underline underline-offset-2 hover:text-stone-700 dark:hover:text-stone-200 transition-colors">
                about CC licenses <ArrowTopRightOnSquareIcon className="inline-block w-4 h-4 mb-0.5" />
              </a>
            </label>
            <select
              value={license}
              onChange={(e) => setLicense(e.target.value)}
              disabled={pending}
              className="w-full rounded-lg border border-stone-300 dark:border-stone-600 bg-white dark:bg-stone-800 px-4 py-3 text-sm text-stone-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-stone-900 dark:focus:ring-stone-100 disabled:opacity-50"
            >
              <option value="">No license specified</option>
              {CC_LICENSES.map((l) => (
                <option key={l} value={l}>{l}</option>
              ))}
            </select>
          </div>

          {visibility === 'published' && (
            <div className="space-y-1.5">
              <label className="text-sm font-medium text-stone-500 dark:text-stone-400">
                Bluesky post{' '}
                <span className="font-normal text-stone-400 dark:text-stone-500">(a Bluesky post links Comments and Likes across platforms)</span>
              </label>
              <textarea
                value={bskyPostText}
                onChange={(e) => setBskyPostText(e.target.value)}
                placeholder="Add text to your Bluesky post… (optional)"
                rows={3}
                maxLength={300}
                disabled={pending}
                className="w-full rounded-lg border border-stone-300 dark:border-stone-600 bg-white dark:bg-stone-800 px-4 py-3 text-sm text-stone-900 dark:text-white placeholder-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-900 dark:focus:ring-stone-100 disabled:opacity-50 resize-none"
              />
              <p className="text-sm text-stone-400 dark:text-stone-500 text-right">{bskyPostText.length}/300</p>
            </div>
          )}

          <CodeMirrorMarkdownEditor
            initialValue={content}
            onChange={setContent}
            displayMode="split"
            showToolbar={true}
          />
        </form>
      </div>
    </div>
  )
}
