/**
 * StepDropzone — Phase 3
 *
 * Accepts one or more .stp/.step files via react-dropzone, uploads each to
 * POST /api/uploads/step, then calls POST /api/cad/match-to-order to link
 * matched files to order items by filename.
 */
import { useState, useCallback } from 'react'
import { useDropzone } from 'react-dropzone'
import { Upload, CheckCircle, XCircle, Loader2, Link2 } from 'lucide-react'
import { toast } from 'sonner'
import api from '../../api/client'

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

interface StepUploadResponse {
  cad_file_id: string
  original_name: string
  file_hash: string
  status: string
}

interface MatchedItem {
  item_id: string
  cad_file_id: string
  item_name: string
  cad_name: string
}

interface MatchToOrderResponse {
  matched: MatchedItem[]
  unmatched_cad: string[]
  unmatched_items: string[]
}

type FileStatus = 'idle' | 'uploading' | 'done' | 'error'

interface FileEntry {
  file: File
  status: FileStatus
  errorMsg?: string
  cadFileId?: string
}

interface StepDropzoneProps {
  orderId: string
  /** Called after matching completes so the parent can refresh the order */
  onMatchComplete?: (result: MatchToOrderResponse) => void
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export default function StepDropzone({ orderId, onMatchComplete }: StepDropzoneProps) {
  const [entries, setEntries] = useState<FileEntry[]>([])
  const [matching, setMatching] = useState(false)
  const [matchResult, setMatchResult] = useState<MatchToOrderResponse | null>(null)

  // Update a single entry by index
  const updateEntry = useCallback(
    (idx: number, patch: Partial<FileEntry>) =>
      setEntries((prev) => prev.map((e, i) => (i === idx ? { ...e, ...patch } : e))),
    [],
  )

  const onDrop = useCallback(
    async (accepted: File[]) => {
      if (accepted.length === 0) return

      // Append new file entries
      const startIdx = entries.length
      const newEntries: FileEntry[] = accepted.map((f) => ({ file: f, status: 'uploading' }))
      setEntries((prev) => [...prev, ...newEntries])
      setMatchResult(null)

      // Upload each file sequentially to avoid overwhelming the server
      const uploadedIds: string[] = []
      for (let i = 0; i < accepted.length; i++) {
        const globalIdx = startIdx + i
        const file = accepted[i]
        const form = new FormData()
        form.append('file', file)
        try {
          const res = await api.post<StepUploadResponse>('/uploads/step', form, {
            headers: { 'Content-Type': 'multipart/form-data' },
          })
          const { cad_file_id } = res.data
          uploadedIds.push(cad_file_id)
          updateEntry(globalIdx, { status: 'done', cadFileId: cad_file_id })
        } catch (err: any) {
          const msg: string =
            err?.response?.data?.detail ?? err?.message ?? 'Upload failed'
          updateEntry(globalIdx, { status: 'error', errorMsg: msg })
          toast.error(`${file.name}: ${msg}`)
        }
      }

      // Collect all successful cad_file_ids from this session (including previous uploads)
      const allSuccessfulIds: string[] = [
        ...entries
          .filter((e) => e.status === 'done' && e.cadFileId)
          .map((e) => e.cadFileId as string),
        ...uploadedIds,
      ]

      if (allSuccessfulIds.length === 0) return

      // Match to order
      setMatching(true)
      try {
        const res = await api.post<MatchToOrderResponse>('/cad/match-to-order', {
          order_id: orderId,
          cad_file_ids: allSuccessfulIds,
        })
        setMatchResult(res.data)
        const { matched, unmatched_cad } = res.data
        if (matched.length > 0) {
          toast.success(`Matched ${matched.length} file(s) to order items`)
        }
        if (unmatched_cad.length > 0) {
          toast.warning(`${unmatched_cad.length} file(s) could not be matched to any item`)
        }
        onMatchComplete?.(res.data)
      } catch (err: any) {
        const msg: string =
          err?.response?.data?.detail ?? err?.message ?? 'Matching failed'
        toast.error(`CAD matching error: ${msg}`)
      } finally {
        setMatching(false)
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [entries, orderId, onMatchComplete, updateEntry],
  )

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: { 'application/octet-stream': ['.stp', '.step'] },
    multiple: true,
  })

  const hasEntries = entries.length > 0

  return (
    <div className="space-y-4">
      {/* Drop target */}
      <div
        {...getRootProps()}
        className={[
          'border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-colors',
          isDragActive
            ? 'border-green-500 bg-status-success-bg'
            : 'border-border-default hover:border-border-default bg-surface-alt',
        ].join(' ')}
      >
        <input {...getInputProps()} />
        <Upload size={32} className="mx-auto mb-3 text-content-muted" />
        {isDragActive ? (
          <p className="text-green-600 font-medium">Drop STEP files here</p>
        ) : (
          <>
            <p className="text-content-secondary font-medium">
              Drag and drop .stp / .step files here
            </p>
            <p className="text-sm text-content-muted mt-1">or click to browse</p>
          </>
        )}
      </div>

      {/* Per-file status list */}
      {hasEntries && (
        <ul className="divide-y divide-border-light rounded-lg border border-border-default bg-surface overflow-hidden">
          {entries.map((entry, idx) => (
            <li key={idx} className="flex items-center gap-3 px-4 py-3">
              <FileStatusIcon status={entry.status} />
              <div className="flex-1 min-w-0">
                <p className="text-sm font-medium text-content truncate">
                  {entry.file.name}
                </p>
                {entry.status === 'error' && (
                  <p className="text-xs text-red-500 mt-0.5">{entry.errorMsg}</p>
                )}
                {entry.status === 'done' && (
                  <p className="text-xs text-content-muted mt-0.5">
                    ID: {entry.cadFileId}
                  </p>
                )}
              </div>
              <StatusLabel status={entry.status} />
            </li>
          ))}
        </ul>
      )}

      {/* Matching spinner */}
      {matching && (
        <div className="flex items-center gap-2 text-sm text-content-secondary">
          <Loader2 size={15} className="animate-spin" />
          Matching files to order items...
        </div>
      )}

      {/* Match result summary */}
      {matchResult && !matching && (
        <div className="rounded-lg border border-border-default bg-surface p-4 space-y-3">
          <div className="flex items-center gap-2 text-sm font-semibold text-content-secondary">
            <Link2 size={15} />
            Matching Results
          </div>

          {matchResult.matched.length > 0 && (
            <div>
              <p className="text-xs font-medium text-status-success-text mb-1">
                Matched ({matchResult.matched.length})
              </p>
              <ul className="space-y-1">
                {matchResult.matched.map((m) => (
                  <li key={m.item_id} className="flex items-center gap-2 text-xs">
                    <CheckCircle size={13} className="text-green-500 shrink-0" />
                    <span className="font-mono text-content-secondary truncate">{m.cad_name}</span>
                    <span className="text-content-muted">→</span>
                    <span className="text-content-secondary truncate">{m.item_name}</span>
                  </li>
                ))}
              </ul>
            </div>
          )}

          {matchResult.unmatched_cad.length > 0 && (
            <div>
              <p className="text-xs font-medium text-status-warning-text mb-1">
                Unmatched CAD files ({matchResult.unmatched_cad.length})
              </p>
              <ul className="space-y-0.5">
                {matchResult.unmatched_cad.map((id) => (
                  <li key={id} className="text-xs text-content-secondary font-mono truncate">
                    {entries.find((e) => e.cadFileId === id)?.file.name ?? id}
                  </li>
                ))}
              </ul>
            </div>
          )}

          {matchResult.matched.length === 0 && matchResult.unmatched_cad.length === 0 && (
            <p className="text-xs text-content-muted">No files were processed.</p>
          )}
        </div>
      )}
    </div>
  )
}

// ---------------------------------------------------------------------------
// Sub-components
// ---------------------------------------------------------------------------

function FileStatusIcon({ status }: { status: FileStatus }) {
  if (status === 'uploading') return <Loader2 size={16} className="animate-spin text-blue-500 shrink-0" />
  if (status === 'done') return <CheckCircle size={16} className="text-green-500 shrink-0" />
  if (status === 'error') return <XCircle size={16} className="text-red-500 shrink-0" />
  return <div className="w-4 h-4 rounded-full bg-surface-muted shrink-0" />
}

function StatusLabel({ status }: { status: FileStatus }) {
  if (status === 'uploading') return <span className="text-xs text-blue-500">Uploading...</span>
  if (status === 'done') return <span className="text-xs text-green-600">Uploaded</span>
  if (status === 'error') return <span className="text-xs text-red-500">Failed</span>
  return null
}
