'use client'

import { useState, useRef } from 'react'
import type { Destination } from '@/lib/constants'
import { DESTINATIONS } from '@/lib/constants'
import type { FolderEntry } from '@/lib/client-types'
import type { FileDiff } from '@/lib/types'
import { useSecret } from '@/hooks/useSecret'
import { useFolderEntries } from '@/hooks/useFolderEntries'
import SecretInput from './upload/SecretInput'
import DestinationPicker from './upload/DestinationPicker'
import FolderDropzone from './upload/FolderDropzone'
import FolderCard from './upload/FolderCard'
import ActionButtons from './upload/ActionButtons'
import OverwriteConfirmModal from './upload/OverwriteConfirmModal'

// ---------------------------------------------------------------------------
// Client-side SHA computation (same as `git hash-object`)
// ---------------------------------------------------------------------------

async function computeGitBlobSha(file: File): Promise<string> {
  const buffer = await file.arrayBuffer()
  const content = new Uint8Array(buffer)
  const header = new TextEncoder().encode(`blob ${content.length}\0`)
  const store = new Uint8Array(header.length + content.length)
  store.set(header)
  store.set(content, header.length)
  const hashBuffer = await crypto.subtle.digest('SHA-1', store)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
  return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
}

// ---------------------------------------------------------------------------
// API helpers
// ---------------------------------------------------------------------------

interface CheckResult {
  exists: boolean
  diffs: FileDiff[]
}

async function checkFolderDiffs(
  folder: FolderEntry,
  destination: string,
  secret: string,
  signal?: AbortSignal,
): Promise<CheckResult> {
  try {
    const params = new URLSearchParams({ folderName: folder.folderName, destination })
    const res = await fetch(`/api/upload/check?${params}`, {
      headers: { 'x-upload-secret': secret.trim() },
      signal,
    })
    const data = await res.json()
    if (!data.success || !data.exists) {
      return { exists: false, diffs: [] }
    }

    const remoteFiles: { name: string; sha: string }[] = data.files || []
    const remoteMap = new Map(remoteFiles.map((f) => [f.name.toLowerCase(), f.sha]))

    const localFiles: { name: string; sha: string }[] = []
    localFiles.push({
      name: folder.modelFile.name,
      sha: await computeGitBlobSha(folder.modelFile),
    })
    for (const tex of folder.textures) {
      localFiles.push({
        name: tex.name,
        sha: await computeGitBlobSha(tex.file),
      })
    }

    const diffs: FileDiff[] = []
    const localNames = new Set<string>()

    for (const local of localFiles) {
      const key = local.name.toLowerCase()
      localNames.add(key)
      const remoteSha = remoteMap.get(key)
      if (!remoteSha) {
        diffs.push({ name: local.name, status: 'new' })
      } else if (remoteSha !== local.sha) {
        diffs.push({ name: local.name, status: 'changed' })
      }
    }

    for (const [name] of remoteMap) {
      if (!localNames.has(name)) {
        diffs.push({ name, status: 'deleted' })
      }
    }

    return { exists: true, diffs }
  } catch {
    return { exists: false, diffs: [] }
  }
}

async function uploadFolder(
  folder: FolderEntry,
  secret: string,
  destination: string,
  onProgress: (pct: number) => void,
  signal?: AbortSignal,
): Promise<{ success: boolean; filename?: string; error?: string }> {
  const formData = new FormData()
  formData.append('folderName', folder.folderName)
  formData.append('destination', destination)

  formData.append('files', folder.modelFile)
  formData.append('fileTypes', 'model')
  formData.append('textureNames', '')

  for (const tex of folder.textures) {
    formData.append('files', tex.file)
    formData.append('fileTypes', 'texture')
    formData.append('textureNames', tex.name)
  }

  onProgress(10)

  try {
    const res = await fetch('/api/upload/git', {
      method: 'POST',
      headers: { 'x-upload-secret': secret.trim() },
      body: formData,
      signal,
    })

    onProgress(80)
    const data = await res.json()

    if (!data.success) {
      return { success: false, error: data.error }
    }

    onProgress(100)
    return { success: true, filename: folder.folderName }
  } catch (err) {
    if (err instanceof DOMException && err.name === 'AbortError') {
      return { success: false, error: 'Upload annule' }
    }
    return { success: false, error: 'Erreur reseau' }
  }
}

// ---------------------------------------------------------------------------
// UploadZone — orchestrator
// ---------------------------------------------------------------------------

export default function UploadZone() {
  const {
    secret,
    secretError,
    secretVisible,
    isSecretEmpty,
    setSecretError,
    handleSecretChange,
    toggleSecretVisible,
  } = useSecret()

  const {
    entries,
    setEntries,
    updateEntry,
    removeEntry,
    resetEntries,
    allDone,
    hasErrors,
  } = useFolderEntries()

  const [isUploading, setIsUploading] = useState(false)
  const [globalError, setGlobalError] = useState<string | null>(null)
  const [destination, setDestination] = useState<Destination>(DESTINATIONS[0].value)
  const [overwriteConfirm, setOverwriteConfirm] = useState<{
    folderName: string
    diffs: FileDiff[]
  } | null>(null)
  const abortRef = useRef<AbortController | null>(null)

  // -- Handlers --

  const handleFolderSelected = (entry: FolderEntry) => {
    setGlobalError(null)
    setEntries([entry])
  }

  const handleToggleViewer = (index: number) => {
    const entry = entries[index]
    if (entry?.modelUrl) {
      updateEntry(index, { viewerOpen: !entry.viewerOpen })
    }
  }

  const handleUpload = async () => {
    if (!secret.trim()) {
      setSecretError("La cle d'acces est requise")
      return
    }
    if (entries.length === 0) return

    setSecretError(null)
    setGlobalError(null)

    const folder = entries[0]
    const check = await checkFolderDiffs(folder, destination, secret, abortRef.current?.signal)
    if (check.exists) {
      if (check.diffs.length === 0) {
        setGlobalError('Aucun fichier modifie — le dossier distant est identique.')
        return
      }
      setOverwriteConfirm({ folderName: folder.folderName, diffs: check.diffs })
      return
    }

    await proceedUpload()
  }

  const proceedUpload = async () => {
    setOverwriteConfirm(null)
    setIsUploading(true)
    setGlobalError(null)

    const controller = new AbortController()
    abortRef.current = controller

    for (let i = 0; i < entries.length; i++) {
      if (entries[i].status === 'success') continue
      if (controller.signal.aborted) break

      updateEntry(i, { status: 'uploading', progress: 0, error: undefined })

      const result = await uploadFolder(
        entries[i],
        secret,
        destination,
        (pct) => updateEntry(i, { progress: pct }),
        controller.signal,
      )

      updateEntry(i, {
        status: result.success ? 'success' : 'error',
        progress: result.success ? 100 : 0,
        error: result.success ? undefined : result.error,
        filename: result.filename,
      })
    }

    abortRef.current = null
    setIsUploading(false)
  }

  const handleCancel = () => {
    abortRef.current?.abort()
    abortRef.current = null
    setIsUploading(false)
  }

  const handleReset = () => {
    resetEntries()
    setGlobalError(null)
    setIsUploading(false)
  }

  const hasPendingOrErrors = entries.some((f) => f.status === 'pending' || f.status === 'error')

  // -- Render --

  return (
    <div className="w-full max-w-2xl space-y-4">
      <SecretInput
        secret={secret}
        secretVisible={secretVisible}
        secretError={secretError}
        disabled={isUploading}
        onChange={handleSecretChange}
        onToggleVisible={toggleSecretVisible}
      />

      <DestinationPicker
        destination={destination}
        disabled={isUploading}
        onChange={setDestination}
      />

      {entries.length === 0 && (
        <FolderDropzone
          isUploading={isUploading}
          onFolderSelected={handleFolderSelected}
          onError={setGlobalError}
        />
      )}

      {globalError && (
        <p className="text-xs text-red-400 text-center">{globalError}</p>
      )}

      {entries.length > 0 && (
        <div className="space-y-2">
          {entries.map((entry, i) => (
            <FolderCard
              key={i}
              entry={entry}
              index={i}
              onToggleViewer={handleToggleViewer}
              onRemove={removeEntry}
            />
          ))}
        </div>
      )}

      <ActionButtons
        isUploading={isUploading}
        isSecretEmpty={isSecretEmpty}
        hasPendingOrErrors={hasPendingOrErrors}
        allDone={allDone}
        hasErrors={hasErrors}
        onUpload={handleUpload}
        onCancel={handleCancel}
        onReset={handleReset}
      />

      {overwriteConfirm && (
        <OverwriteConfirmModal
          destination={destination}
          folderName={overwriteConfirm.folderName}
          diffs={overwriteConfirm.diffs}
          onCancel={() => setOverwriteConfirm(null)}
          onConfirm={proceedUpload}
        />
      )}
    </div>
  )
}
