import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import TagInput from '../tags/TagInput'
import ScreenshotUploader from './ScreenshotUploader'

const STEP_PRELOAD = 1
const STEP_DETAILS = 2
const STEP_PUBLISH = 3
const AUTOSAVE_INTERVAL_MS = 10_000
const STATUS_POLL_INTERVAL_MS = 3_000
const TERMINAL_PROCESSING_STEPS = new Set(['ready', 'rejected', 'published'])
const MIN_ARCHIVE_SCREENSHOTS = 1
const MAX_ARCHIVE_SCREENSHOTS = 5

function processingStepLabel(state) {
  switch (state) {
    case 'pending_scan':
      return 'Pending scan'
    case 'scanning':
      return 'Scanning'
    case 'generating_preview':
      return 'Generating preview'
    case 'analyzing_tags':
      return 'Analyzing tags'
    case 'ready':
      return 'Ready'
    case 'rejected':
      return 'Rejected'
    case 'published':
      return 'Published'
    default:
      return 'Processing'
  }
}

function isArchiveFile(file) {
  if (!file) return false
  const mime = String(file.type || '').toLowerCase()
  if (
    [
      'application/zip',
      'application/x-zip-compressed',
      'application/x-rar-compressed',
      'application/x-tar',
      'application/x-gzip',
      'application/octet-stream',
    ].includes(mime)
  ) {
    return true
  }

  return /\.(zip|rar|7z|tar|gz)$/i.test(file.name || '')
}

function flattenCategories(contentTypes) {
  const result = []

  for (const type of contentTypes || []) {
    const roots = Array.isArray(type.categories) ? type.categories : []
    for (const root of roots) {
      result.push({
        id: root.id,
        label: `${type.name} / ${root.name}`,
      })

      const children = Array.isArray(root.children) ? root.children : []
      for (const child of children) {
        result.push({
          id: child.id,
          label: `${type.name} / ${root.name} / ${child.name}`,
        })
      }
    }
  }

  return result
}

function detailsStorageKey(uploadId) {
  return `sb.upload.wizard.details.${uploadId}`
}

function makeClientSlug(title) {
  const base = String(title || '')
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')

  return base || 'artwork'
}

export default function UploadWizard({
  initialDraftId = null,
  contentTypes = [],
  suggestedTags = [],
  onPublished,
}) {
  const [step, setStep] = useState(initialDraftId ? STEP_DETAILS : STEP_PRELOAD)
  const [uploadId, setUploadId] = useState(initialDraftId)

  const [mainFile, setMainFile] = useState(null)
  const [screenshots, setScreenshots] = useState([])
  const [dragging, setDragging] = useState(false)
  const [progress, setProgress] = useState(0)

  const [details, setDetails] = useState({
    title: '',
    category_id: '',
    tags: [],
    description: '',
    license: '',
    nsfw: false,
  })

  const [autosaveDirty, setAutosaveDirty] = useState(false)
  const [lastSavedAt, setLastSavedAt] = useState(null)

  const [previewPath, setPreviewPath] = useState(null)
  const [finalPath, setFinalPath] = useState(null)

  const [loadingPreload, setLoadingPreload] = useState(false)
  const [loadingAutosave, setLoadingAutosave] = useState(false)
  const [loadingPublish, setLoadingPublish] = useState(false)
  const [processingStatus, setProcessingStatus] = useState(null)
  const [processingError, setProcessingError] = useState('')
  const [screenshotValidationMessage, setScreenshotValidationMessage] = useState('')

  const [errorMessage, setErrorMessage] = useState('')
  const [successMessage, setSuccessMessage] = useState('')

  const autosaveTimerRef = useRef(null)
  const statusPollTimerRef = useRef(null)
  const lastActionRef = useRef(null)

  const categoryOptions = useMemo(() => flattenCategories(contentTypes), [contentTypes])
  const archiveMode = useMemo(() => isArchiveFile(mainFile), [mainFile])
  const urlPreviewSlug = useMemo(() => makeClientSlug(details.title), [details.title])
  const archiveScreenshotsValid = useMemo(() => {
    if (!archiveMode) return true
    return screenshots.length >= MIN_ARCHIVE_SCREENSHOTS && screenshots.length <= MAX_ARCHIVE_SCREENSHOTS
  }, [archiveMode, screenshots.length])

  useEffect(() => {
    if (!uploadId) return

    const raw = window.localStorage.getItem(detailsStorageKey(uploadId))
    if (!raw) return

    try {
      const parsed = JSON.parse(raw)
      if (parsed && typeof parsed === 'object') {
        setDetails((prev) => ({ ...prev, ...parsed }))
      }
    } catch {
      // ignore invalid local state
    }
  }, [uploadId])

  useEffect(() => {
    if (!uploadId) return

    window.localStorage.setItem(detailsStorageKey(uploadId), JSON.stringify(details))
    setAutosaveDirty(true)
  }, [details, uploadId])

  useEffect(() => {
    if (uploadId) {
      window.localStorage.setItem('sb.upload.wizard.lastDraft', uploadId)
    }
  }, [uploadId])

  useEffect(() => {
    if (initialDraftId || uploadId) return
    const lastDraft = window.localStorage.getItem('sb.upload.wizard.lastDraft')
    if (lastDraft) {
      setUploadId(lastDraft)
      setStep(STEP_DETAILS)
      setSuccessMessage('Resumed unfinished draft.')
    }
  }, [initialDraftId, uploadId])

  const handleDrop = useCallback((event) => {
    event.preventDefault()
    setDragging(false)
    setErrorMessage('')

    const files = Array.from(event.dataTransfer?.files || [])
    if (!files.length) return

    const [first, ...rest] = files
    setMainFile(first)
    if (isArchiveFile(first)) {
      setScreenshots(rest.filter((file) => file.type?.startsWith('image/')).slice(0, MAX_ARCHIVE_SCREENSHOTS))
    } else {
      setScreenshots([])
    }
  }, [])

  const handleMainSelected = useCallback((event) => {
    const file = event.target.files?.[0]
    if (!file) return
    setErrorMessage('')
    setMainFile(file)
    if (!isArchiveFile(file)) {
      setScreenshots([])
      setScreenshotValidationMessage('')
    }
  }, [])

  const handleScreenshotsChanged = useCallback((files) => {
    const next = Array.from(files || []).slice(0, MAX_ARCHIVE_SCREENSHOTS)
    setScreenshots(next)
    if (next.length >= MIN_ARCHIVE_SCREENSHOTS) {
      setScreenshotValidationMessage('')
    }
  }, [])

  useEffect(() => {
    if (!archiveMode) {
      setScreenshotValidationMessage('')
      return
    }

    if (screenshots.length === 0) {
      setScreenshotValidationMessage(`At least ${MIN_ARCHIVE_SCREENSHOTS} screenshot is required for archives.`)
      return
    }

    if (screenshots.length > MAX_ARCHIVE_SCREENSHOTS) {
      setScreenshotValidationMessage(`Maximum ${MAX_ARCHIVE_SCREENSHOTS} screenshots allowed.`)
      return
    }

    setScreenshotValidationMessage('')
  }, [archiveMode, screenshots.length])

  const fetchProcessingStatus = useCallback(async () => {
    if (!uploadId) return

    try {
      const response = await window.axios.get(`/api/uploads/${uploadId}/status`)
      const payload = response.data || null
      setProcessingStatus(payload)
      setProcessingError('')
    } catch (error) {
      const message = error?.response?.data?.message || 'Status polling failed.'
      setProcessingError(message)
    }
  }, [uploadId])

  const runPreload = useCallback(async () => {
    if (!mainFile) {
      setErrorMessage('Please select a main file first.')
      return
    }

    if (archiveMode && !archiveScreenshotsValid) {
      setScreenshotValidationMessage(`At least ${MIN_ARCHIVE_SCREENSHOTS} screenshot is required for archives.`)
      return
    }

    setLoadingPreload(true)
    setErrorMessage('')
    setSuccessMessage('')
    lastActionRef.current = 'preload'

    try {
      const formData = new FormData()
      formData.append('main', mainFile)

      if (archiveMode) {
        screenshots.slice(0, MAX_ARCHIVE_SCREENSHOTS).forEach((file) => formData.append('screenshots[]', file))
      }

      const response = await window.axios.post('/api/uploads/preload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        onUploadProgress: (event) => {
          const total = Number(event.total || 0)
          const loaded = Number(event.loaded || 0)
          if (total > 0) {
            setProgress(Math.round((loaded / total) * 100))
          }
        },
      })

      const payload = response.data || {}
      setUploadId(payload.upload_id || null)
      setPreviewPath(payload.preview_path || null)
      setProcessingStatus(null)
      setProcessingError('')
      setStep(STEP_DETAILS)
      setSuccessMessage('Draft created. Fill in details next.')
    } catch (error) {
      const message =
        error?.response?.data?.message ||
        error?.response?.data?.errors?.main?.[0] ||
        'Preload failed. Try again.'
      setErrorMessage(message)
    } finally {
      setLoadingPreload(false)
    }
  }, [archiveMode, archiveScreenshotsValid, mainFile, screenshots])

  const runAutosave = useCallback(async () => {
    if (!uploadId || !autosaveDirty) return

    setLoadingAutosave(true)
    setErrorMessage('')
    lastActionRef.current = 'autosave'

    try {
      await window.axios.post(`/api/uploads/${uploadId}/autosave`, {
        title: details.title || null,
        category_id: details.category_id || null,
        tags: details.tags || [],
        description: details.description || null,
        license: details.license || null,
        nsfw: Boolean(details.nsfw),
      })

      setAutosaveDirty(false)
      setLastSavedAt(new Date())
    } catch (error) {
      const message = error?.response?.data?.message || 'Autosave failed. Your local draft is preserved.'
      setErrorMessage(message)
    } finally {
      setLoadingAutosave(false)
    }
  }, [autosaveDirty, details, uploadId])

  useEffect(() => {
    if (step !== STEP_DETAILS || !uploadId) return

    autosaveTimerRef.current = window.setInterval(() => {
      runAutosave()
    }, AUTOSAVE_INTERVAL_MS)

    return () => {
      if (autosaveTimerRef.current) {
        window.clearInterval(autosaveTimerRef.current)
        autosaveTimerRef.current = null
      }
    }
  }, [runAutosave, step, uploadId])

  useEffect(() => {
    if (!uploadId || step < STEP_DETAILS) return

    if (processingStatus?.processing_state && TERMINAL_PROCESSING_STEPS.has(processingStatus.processing_state)) {
      return
    }

    fetchProcessingStatus()

    statusPollTimerRef.current = window.setInterval(() => {
      fetchProcessingStatus()
    }, STATUS_POLL_INTERVAL_MS)

    return () => {
      if (statusPollTimerRef.current) {
        window.clearInterval(statusPollTimerRef.current)
        statusPollTimerRef.current = null
      }
    }
  }, [fetchProcessingStatus, processingStatus?.processing_state, step, uploadId])

  const runPublish = useCallback(async () => {
    if (!uploadId) return

    setLoadingPublish(true)
    setErrorMessage('')
    setSuccessMessage('')
    lastActionRef.current = 'publish'

    try {
      if (autosaveDirty) {
        await runAutosave()
      }

      const response = await window.axios.post(`/api/uploads/${uploadId}/publish`)
      const payload = response.data || {}

      setFinalPath(payload.final_path || null)
      setProcessingStatus((prev) => ({
        ...(prev || {}),
        id: uploadId,
        status: payload.status || 'published',
        is_scanned: true,
        preview_ready: true,
        has_tags: true,
        processing_state: 'published',
      }))
      setStep(STEP_PUBLISH)
      setSuccessMessage('Upload published successfully.')

      if (typeof onPublished === 'function') {
        onPublished(payload)
      }
    } catch (error) {
      const message = error?.response?.data?.message || 'Publish failed. Resolve issues and retry.'
      setErrorMessage(message)
    } finally {
      setLoadingPublish(false)
    }
  }, [autosaveDirty, onPublished, runAutosave, uploadId])

  const retryLastAction = useCallback(() => {
    if (lastActionRef.current === 'preload') return runPreload()
    if (lastActionRef.current === 'autosave') return runAutosave()
    if (lastActionRef.current === 'publish') return runPublish()
    return undefined
  }, [runAutosave, runPreload, runPublish])

  return (
    <div className="mx-auto w-full max-w-4xl rounded-2xl border border-white/10 bg-slate-900/60 p-4 md:p-6">
      <div className="mb-4 flex flex-wrap items-center gap-2 text-xs md:text-sm">
        <span className={`rounded-full px-3 py-1 ${step >= STEP_PRELOAD ? 'bg-emerald-500/30 text-emerald-100' : 'bg-white/10 text-white/60'}`}>1. Preload</span>
        <span className={`rounded-full px-3 py-1 ${step >= STEP_DETAILS ? 'bg-emerald-500/30 text-emerald-100' : 'bg-white/10 text-white/60'}`}>2. Details</span>
        <span className={`rounded-full px-3 py-1 ${step >= STEP_PUBLISH ? 'bg-emerald-500/30 text-emerald-100' : 'bg-white/10 text-white/60'}`}>3. Publish</span>
      </div>

      {errorMessage && (
        <div className="mb-4 rounded-xl border border-rose-400/30 bg-rose-500/10 p-3 text-sm text-rose-100">
          <div>{errorMessage}</div>
          <button type="button" onClick={retryLastAction} className="mt-2 rounded-lg border border-rose-300/40 px-2 py-1 text-xs hover:bg-rose-500/20">
            Retry last action
          </button>
        </div>
      )}

      {successMessage && (
        <div className="mb-4 rounded-xl border border-emerald-400/30 bg-emerald-500/10 p-3 text-sm text-emerald-100">
          {successMessage}
        </div>
      )}

      {processingStatus && (
        <div className="mb-4 rounded-xl border border-sky-400/30 bg-sky-500/10 p-3 text-sm text-sky-100">
          <div className="font-medium">Processing status: {processingStepLabel(processingStatus.processing_state)}</div>
          <div className="mt-1 text-xs text-sky-100/80">
            status={processingStatus.status}; scanned={processingStatus.is_scanned ? 'yes' : 'no'}; preview={processingStatus.preview_ready ? 'ready' : 'pending'}; tags={processingStatus.has_tags ? 'ready' : 'pending'}
          </div>
        </div>
      )}

      {processingError && (
        <div className="mb-4 rounded-xl border border-amber-400/30 bg-amber-500/10 p-3 text-xs text-amber-100">
          {processingError}
        </div>
      )}

      {step === STEP_PRELOAD && (
        <div className="space-y-4">
          <div
            onDrop={handleDrop}
            onDragOver={(event) => {
              event.preventDefault()
              setDragging(true)
            }}
            onDragLeave={() => setDragging(false)}
            className={`rounded-2xl border-2 border-dashed p-6 text-center transition ${dragging ? 'border-sky-300 bg-sky-500/10' : 'border-white/20 bg-white/5'}`}
          >
            <p className="text-sm text-white/80">Drag & drop main file here</p>
            <p className="mt-1 text-xs text-white/55">or choose from your device</p>
            <input aria-label="Main upload file" type="file" className="mt-4 block w-full text-xs text-white/80" onChange={handleMainSelected} />
            {mainFile && <p className="mt-2 text-xs text-emerald-200">Main: {mainFile.name}</p>}
          </div>

          {archiveMode && (
            <ScreenshotUploader
              files={screenshots}
              onChange={handleScreenshotsChanged}
              min={MIN_ARCHIVE_SCREENSHOTS}
              max={MAX_ARCHIVE_SCREENSHOTS}
              required
              error={screenshotValidationMessage}
            />
          )}

          <div className="rounded-xl border border-white/10 bg-white/5 p-3">
            <div className="mb-2 text-xs text-white/60">Upload progress</div>
            <div className="h-2 w-full overflow-hidden rounded-full bg-white/10">
              <div className="h-full bg-sky-400 transition-all" style={{ width: `${progress}%` }} />
            </div>
            <div className="mt-2 text-xs text-white/60">{progress}%</div>
          </div>

          <button
            type="button"
            onClick={runPreload}
            disabled={loadingPreload || !mainFile || (archiveMode && !archiveScreenshotsValid)}
            className="w-full rounded-xl bg-emerald-500 px-4 py-2 text-sm font-semibold text-black disabled:cursor-not-allowed disabled:opacity-60"
          >
            {loadingPreload ? 'Preloading…' : 'Start preload'}
          </button>
        </div>
      )}

      {step >= STEP_DETAILS && (
        <div className="space-y-4">
          <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
            <div>
              <label className="mb-2 block text-sm text-white/80">Title</label>
              <input
                type="text"
                value={details.title}
                onChange={(event) => setDetails((prev) => ({ ...prev, title: event.target.value }))}
                className="w-full rounded-xl border border-white/10 bg-white/10 px-3 py-2 text-sm text-white"
                placeholder="Name your artwork"
              />
            </div>

            <div>
              <label className="mb-2 block text-sm text-white/80">Category</label>
              <select
                value={details.category_id}
                onChange={(event) => setDetails((prev) => ({ ...prev, category_id: event.target.value }))}
                className="w-full rounded-xl border border-white/10 bg-white/10 px-3 py-2 text-sm text-white"
              >
                <option value="">Select category</option>
                {categoryOptions.map((option) => (
                  <option key={option.id} value={option.id}>
                    {option.label}
                  </option>
                ))}
              </select>
            </div>
          </div>

          <div>
            <label className="mb-2 block text-sm text-white/80">Tags</label>
            <TagInput
              value={details.tags}
              onChange={(value) => setDetails((prev) => ({ ...prev, tags: value }))}
              suggestedTags={suggestedTags}
            />
          </div>

          <div>
            <label className="mb-2 block text-sm text-white/80">Description</label>
            <textarea
              rows={4}
              value={details.description}
              onChange={(event) => setDetails((prev) => ({ ...prev, description: event.target.value }))}
              className="w-full rounded-xl border border-white/10 bg-white/10 px-3 py-2 text-sm text-white"
              placeholder="Tell the story behind this upload"
            />
          </div>

          <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
            <div>
              <label className="mb-2 block text-sm text-white/80">License</label>
              <input
                type="text"
                value={details.license}
                onChange={(event) => setDetails((prev) => ({ ...prev, license: event.target.value }))}
                className="w-full rounded-xl border border-white/10 bg-white/10 px-3 py-2 text-sm text-white"
                placeholder="e.g. cc-by"
              />
            </div>

            <label className="mt-7 inline-flex items-center gap-2 text-sm text-white/80">
              <input
                type="checkbox"
                checked={details.nsfw}
                onChange={(event) => setDetails((prev) => ({ ...prev, nsfw: event.target.checked }))}
              />
              NSFW
            </label>
          </div>

          <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
            <div className="text-xs text-white/60">
              {loadingAutosave ? 'Autosaving…' : lastSavedAt ? `Last saved: ${lastSavedAt.toLocaleTimeString()}` : 'Autosave every 10s'}
            </div>

            <div className="flex gap-2">
              <button
                type="button"
                onClick={runAutosave}
                disabled={loadingAutosave || !uploadId}
                className="rounded-xl border border-white/20 px-3 py-2 text-sm text-white disabled:opacity-60"
              >
                Save now
              </button>
              <button
                type="button"
                onClick={() => setStep(STEP_PUBLISH)}
                disabled={!uploadId}
                className="rounded-xl bg-sky-500 px-3 py-2 text-sm font-semibold text-black disabled:opacity-60"
              >
                Continue to publish
              </button>
            </div>
          </div>
        </div>
      )}

      {step >= STEP_PUBLISH && (
        <div className="mt-6 space-y-4 rounded-2xl border border-white/10 bg-white/5 p-4">
          <h3 className="text-base font-semibold text-white">Publish</h3>

          <div className="grid grid-cols-1 gap-3 text-sm text-white/80 md:grid-cols-2">
            <div><span className="text-white/50">Upload ID:</span> {uploadId || '—'}</div>
            <div><span className="text-white/50">Title:</span> {details.title || '—'}</div>
            <div className="md:col-span-2"><span className="text-white/50">URL preview:</span> /artwork/{urlPreviewSlug}</div>
            <div><span className="text-white/50">Category:</span> {details.category_id || '—'}</div>
            <div><span className="text-white/50">Tags:</span> {(details.tags || []).join(', ') || '—'}</div>
            <div className="md:col-span-2"><span className="text-white/50">Preview path:</span> {previewPath || 'Will be resolved by backend pipeline'}</div>
            {finalPath && <div className="md:col-span-2"><span className="text-white/50">Final path:</span> {finalPath}</div>}
          </div>

          <div className="rounded-xl border border-amber-400/20 bg-amber-500/10 p-3 text-xs text-amber-100">
            Final validation and file move happen on backend. This step only calls the publish endpoint.
          </div>

          <div className="flex flex-col gap-2 sm:flex-row sm:justify-end">
            <button
              type="button"
              onClick={() => setStep(STEP_DETAILS)}
              className="rounded-xl border border-white/20 px-3 py-2 text-sm text-white"
            >
              Back to details
            </button>
            <button
              type="button"
              onClick={runPublish}
              disabled={loadingPublish || !uploadId}
              className="rounded-xl bg-emerald-500 px-4 py-2 text-sm font-semibold text-black disabled:opacity-60"
            >
              {loadingPublish ? 'Publishing…' : 'Publish now'}
            </button>
          </div>
        </div>
      )}
    </div>
  )
}
