import { useState, useCallback, useEffect, useRef } from 'react'
import { useDropzone } from 'react-dropzone'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import api from '../utils/api'

function UploadZone({ title, description, accept, endpoint, icon }) {
  const [tasks, setTasks] = useState([])
  const queryClient = useQueryClient()
  const intervalsRef = useRef({})

  const pollTask = useCallback((taskId) => {
    if (intervalsRef.current[taskId]) return
    const intervalId = setInterval(async () => {
      try {
        const { data } = await api.get(`/upload/task/${taskId}`)
        if (data.status === 'SUCCESS' || data.status === 'FAILURE') {
          clearInterval(intervalsRef.current[taskId])
          delete intervalsRef.current[taskId]
          // A successful task may still have skipped the file (e.g. a duplicate or
          // an activity that looks like vehicle travel) — surface the reason.
          const skipped = data.status === 'SUCCESS' && data.result?.status === 'skipped'
          setTasks(ts => ts.map(t =>
            t.task_id === taskId
              ? { ...t,
                  status: data.status === 'FAILURE' ? 'failed' : skipped ? 'skipped' : 'done',
                  reason: skipped ? data.result?.reason : t.reason }
              : t
          ))
          if (data.status === 'SUCCESS' && !skipped) {
            queryClient.invalidateQueries({ queryKey: ['activities'] })
            queryClient.invalidateQueries({ queryKey: ['health-summary'] })
            queryClient.invalidateQueries({ queryKey: ['health-metrics'] })
          }
        }
      } catch { /* ignore transient poll errors */ }
    }, 2000)
    intervalsRef.current[taskId] = intervalId
  }, [queryClient])

  useEffect(() => {
    return () => { Object.values(intervalsRef.current).forEach(clearInterval) }
  }, [])

  const upload = useMutation({
    mutationFn: async (file) => {
      const form = new FormData()
      form.append('file', file)
      const { data } = await api.post(endpoint, form, {
        headers: { 'Content-Type': 'multipart/form-data' },
      })
      return { file: file.name, ...data }
    },
    onSuccess: (data) => {
      const task = { ...data, status: data.task_id ? 'processing' : 'queued' }
      setTasks(t => [...t, task])
      if (data.task_id) {
        pollTask(data.task_id)
      }
    },
    onError: (err, file) => {
      const reason = err.response?.data?.detail || 'Upload failed'
      setTasks(t => [...t, { file: file?.name || String(file), status: 'failed', reason }])
    },
  })

  const onDrop = useCallback((accepted) => {
    accepted.forEach(file => upload.mutate(file))
  }, [upload])

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept,
    multiple: true,
  })

  function StatusBadge({ status }) {
    if (status === 'processing') return <span className="ml-2 text-blue-400 animate-pulse">⏳ Processing</span>
    if (status === 'done') return <span className="ml-2 text-green-400">✓ Done</span>
    if (status === 'skipped') return <span className="ml-2 text-amber-400">⚠ Skipped</span>
    if (status === 'failed') return <span className="ml-2 text-red-400">✗ Failed</span>
    return <span className="ml-2 text-green-400">✓ Queued</span>
  }

  return (
    <div className="bg-gray-900 rounded-xl border border-gray-800 p-5">
      <div className="flex items-center gap-3 mb-3">
        <span className="text-2xl">{icon}</span>
        <div>
          <h3 className="font-semibold text-white">{title}</h3>
          <p className="text-xs text-gray-500">{description}</p>
        </div>
      </div>

      <div
        {...getRootProps()}
        className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-colors ${
          isDragActive
            ? 'border-blue-500 bg-blue-950/30'
            : 'border-gray-700 hover:border-gray-500 hover:bg-gray-800/30'
        }`}
      >
        <input {...getInputProps()} />
        {isDragActive ? (
          <p className="text-blue-400 text-sm">Drop files here…</p>
        ) : (
          <div>
            <p className="text-gray-400 text-sm">Drag & drop files here, or click to browse</p>
            <p className="text-gray-600 text-xs mt-1">
              {Object.values(accept).flat().join(', ')}
            </p>
          </div>
        )}
      </div>

      {upload.isPending && (
        <p className="text-xs text-blue-400 mt-2 animate-pulse">Uploading…</p>
      )}

      {tasks.length > 0 && (
        <div className="mt-4 space-y-2">
          {tasks.map((task, i) => (
            <div key={i} className="bg-gray-800 rounded-lg px-3 py-2">
              <div className="flex items-center justify-between text-xs">
                <span className="text-gray-300 truncate flex-1">{task.file}</span>
                {task.activity_tasks !== undefined && (
                  <span className="text-gray-500 ml-2">{task.activity_tasks} activities queued</span>
                )}
                <StatusBadge status={task.status} />
              </div>
              {task.reason && (task.status === 'skipped' || task.status === 'failed') && (
                <p className={`text-xs mt-1 ${task.status === 'skipped' ? 'text-amber-400/80' : 'text-red-400/80'}`}>
                  {task.reason}
                </p>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

// Strava bulk export with a dry-run preview: upload → analyse (new vs. already
// present, Garmin preferred) → confirm import of just the new ones. Avoids
// re-uploading by importing the files the preview already extracted (via token).
function StravaExportZone() {
  const qc = useQueryClient()
  const [phase, setPhase] = useState('idle')  // idle|uploading|analyzing|report|importing|done|error
  const [report, setReport] = useState(null)
  const [token, setToken] = useState(null)
  const [error, setError] = useState('')
  const pollRef = useRef(null)

  const reset = () => { setReport(null); setToken(null); setError('') }
  useEffect(() => () => { if (pollRef.current) clearInterval(pollRef.current) }, [])

  const pollAnalyze = useCallback((taskId) => {
    pollRef.current = setInterval(async () => {
      try {
        const { data } = await api.get(`/upload/task/${taskId}`)
        if (data.status === 'SUCCESS') {
          clearInterval(pollRef.current)
          setReport(data.result)
          setPhase('report')
        } else if (data.status === 'FAILURE') {
          clearInterval(pollRef.current)
          setError('Analysis failed — please try again')
          setPhase('error')
        }
      } catch { /* ignore transient poll errors */ }
    }, 2000)
  }, [])

  const onDrop = useCallback(async (accepted) => {
    const file = accepted[0]
    if (!file) return
    reset()
    setPhase('uploading')
    try {
      const form = new FormData()
      form.append('file', file)
      const { data } = await api.post('/upload/strava-export?dry_run=true', form, {
        headers: { 'Content-Type': 'multipart/form-data' },
      })
      setToken(data.token)
      setPhase('analyzing')
      pollAnalyze(data.task_id)
    } catch (e) {
      setError(e.response?.data?.detail || 'Upload failed')
      setPhase('error')
    }
  }, [pollAnalyze])

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop, accept: { 'application/zip': ['.zip'] }, multiple: false,
  })

  const doImport = async () => {
    setPhase('importing')
    try {
      await api.post('/upload/strava-export/confirm', { token })
      setPhase('done')
      qc.invalidateQueries({ queryKey: ['activities'] })
    } catch (e) {
      setError(e.response?.data?.detail || 'Import failed')
      setPhase('error')
    }
  }

  return (
    <div className="bg-gray-900 rounded-xl border border-gray-800 p-5">
      <div className="flex items-center gap-3 mb-3">
        <span className="text-2xl">🚴</span>
        <div>
          <h3 className="font-semibold text-white">Strava bulk export</h3>
          <p className="text-xs text-gray-500">Preview duplicates before importing — Garmin data is kept on a match</p>
        </div>
      </div>

      {(phase === 'idle' || phase === 'uploading') && (
        <div
          {...getRootProps()}
          className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-colors ${
            isDragActive ? 'border-blue-500 bg-blue-950/30' : 'border-gray-700 hover:border-gray-500 hover:bg-gray-800/30'
          }`}
        >
          <input {...getInputProps()} />
          {phase === 'uploading'
            ? <p className="text-blue-400 text-sm animate-pulse">Uploading…</p>
            : <div>
                <p className="text-gray-400 text-sm">Drag & drop your Strava archive .zip, or click to browse</p>
                <p className="text-gray-600 text-xs mt-1">We’ll scan it and show what’s new before importing</p>
              </div>}
        </div>
      )}

      {phase === 'analyzing' && (
        <p className="text-sm text-blue-400 animate-pulse py-4 text-center">Scanning archive for duplicates…</p>
      )}

      {phase === 'report' && report && (
        <div className="space-y-3">
          <div className="grid grid-cols-3 gap-2 text-center">
            <div className="bg-green-950/30 border border-green-900/40 rounded-lg py-2">
              <div className="text-xl font-bold text-green-400">{report.new}</div>
              <div className="text-xs text-gray-500">new</div>
            </div>
            <div className="bg-gray-800 border border-gray-700 rounded-lg py-2">
              <div className="text-xl font-bold text-gray-300">{report.duplicate}</div>
              <div className="text-xs text-gray-500">already have</div>
            </div>
            <div className="bg-amber-950/20 border border-amber-900/40 rounded-lg py-2">
              <div className="text-xl font-bold text-amber-400">{report.unreadable}</div>
              <div className="text-xs text-gray-500">unreadable</div>
            </div>
          </div>
          {report.new_samples?.length > 0 && (
            <p className="text-xs text-gray-500">
              New e.g.: <span className="text-gray-400">{report.new_samples.slice(0, 4).join(' · ')}</span>
              {report.new > 4 ? ' …' : ''}
            </p>
          )}
          <div className="flex items-center gap-3 flex-wrap">
            <button
              onClick={doImport}
              disabled={report.new === 0}
              className="bg-blue-600 hover:bg-blue-700 disabled:opacity-40 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors">
              {report.new === 0 ? 'Nothing new to import' : `Import ${report.new} new ${report.new === 1 ? 'activity' : 'activities'}`}
            </button>
            <button onClick={() => setPhase('idle')} className="text-gray-400 hover:text-gray-200 text-sm transition-colors">
              Choose another file
            </button>
          </div>
          <p className="text-xs text-gray-600">Duplicates are skipped automatically — your existing Garmin activities are kept.</p>
        </div>
      )}

      {phase === 'importing' && (
        <p className="text-sm text-blue-400 animate-pulse py-4 text-center">Queuing import…</p>
      )}

      {phase === 'done' && (
        <div className="py-3 space-y-2">
          <p className="text-sm text-green-400">✓ Import queued — new activities will appear shortly as they process.</p>
          <button onClick={() => setPhase('idle')} className="text-gray-400 hover:text-gray-200 text-sm transition-colors">
            Import another export
          </button>
        </div>
      )}

      {phase === 'error' && (
        <div className="py-3 space-y-2">
          <p className="text-sm text-red-400">{error || 'Something went wrong'}</p>
          <button onClick={() => setPhase('idle')} className="text-gray-400 hover:text-gray-200 text-sm transition-colors">
            Try again
          </button>
        </div>
      )}
    </div>
  )
}

export default function UploadPage() {
  return (
    <div className="p-4 md:p-6 space-y-6">
      <div>
        <h1 className="text-2xl font-bold text-white">Import Data</h1>
        <p className="text-gray-500 text-sm mt-1">
          Import activities from Garmin or Strava. Large exports are processed in the background.
        </p>
      </div>

      {/* How to export guides */}
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
        <div className="bg-blue-950/30 border border-blue-900/50 rounded-xl p-4 text-sm">
          <h3 className="font-semibold text-blue-300 mb-2">📥 How to export from Garmin Connect</h3>
          <ol className="text-gray-400 space-y-1 list-decimal list-inside text-xs">
            <li>Go to Garmin Connect → Profile → Account</li>
            <li>Scroll to Data Management → Export Your Data</li>
            <li>Request export and wait for the email</li>
            <li>Download and upload the ZIP file below</li>
          </ol>
        </div>
        <div className="bg-orange-950/20 border border-orange-900/40 rounded-xl p-4 text-sm">
          <h3 className="font-semibold text-orange-300 mb-2">📥 How to export from Strava</h3>
          <ol className="text-gray-400 space-y-1 list-decimal list-inside text-xs">
            <li>Go to strava.com → Settings → My Account</li>
            <li>Scroll to Download or Delete Your Account</li>
            <li>Click "Request Your Archive"</li>
            <li>Download and upload the ZIP file below</li>
          </ol>
          <p className="text-gray-500 text-xs mt-2">Handles the gzipped <code>.fit.gz</code>/<code>.gpx.gz</code>/<code>.tcx.gz</code> files Strava puts in the archive. For ongoing sync, connect Strava on the Profile page instead.</p>
        </div>
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
        {/* Single FIT/GPX/TCX */}
        <UploadZone
          title="Single activity"
          description="Upload a .fit, .gpx or .tcx file"
          icon="🏃"
          endpoint="/upload/activity"
          accept={{
            'application/octet-stream': ['.fit'],
            'application/gpx+xml': ['.gpx'],
            'text/xml': ['.gpx', '.tcx'],
          }}
        />

        {/* Garmin full export */}
        <UploadZone
          title="Garmin Connect export"
          description="Upload your full Garmin data export ZIP"
          icon="⌚"
          endpoint="/upload/garmin-export"
          accept={{ 'application/zip': ['.zip'] }}
        />

        {/* Strava export — preview duplicates, then import */}
        <StravaExportZone />

        {/* Ongoing FIT files */}
        <div className="bg-gray-900 rounded-xl border border-gray-800 p-5">
          <div className="flex items-center gap-3 mb-3">
            <span className="text-2xl">🔄</span>
            <div>
              <h3 className="font-semibold text-white">Ongoing sync</h3>
              <p className="text-xs text-gray-500">Automatically import new Garmin watch files</p>
            </div>
          </div>
          <div className="space-y-3 text-xs text-gray-500">
            <p>After each activity, sync your Garmin watch via USB or Garmin Express. New FIT files appear in:</p>
            <code className="block bg-gray-800 rounded px-3 py-2 text-green-400 font-mono">
              GARMIN/Activity/*.fit
            </code>
            <p>Upload individual FIT files above using the "Single activity" uploader, or set up a folder-watch script:</p>
            <code className="block bg-gray-800 rounded px-3 py-2 text-green-400 font-mono whitespace-pre">
{`# Example: auto-upload new FIT files
inotifywait -m ~/Garmin/Activity/ -e create \\
  --format '%f' | while read file; do
    curl -X POST /api/upload/activity \\
      -H "Authorization: Bearer TOKEN" \\
      -F "file=@$file"
  done`}
            </code>
          </div>
        </div>
      </div>
    </div>
  )
}
