import { useCallback, useEffect, useRef, useState } from 'react'
import { apiFetch } from '../api'

type OwnerType = 'persona' | 'advisor_panel'

type DocRecord = {
  id: string
  filename: string
  size_bytes: number
  created_at: string | null
}

interface Props {
  ownerType: OwnerType
  ownerClientId: string | null
  ownerLabel: string
  maxTotalBytes: number
  /** Called once to auto-save the draft and obtain an ownerClientId when one doesn't exist yet. */
  onEnsureOwner?: () => string | null
}

const MAX_FILE_BYTES = 1 * 1024 * 1024 * 1024
const ACCEPTED = '.txt,.md'

function humanSize(b: number): string {
  for (const unit of ['bytes', 'KB', 'MB', 'GB']) {
    if (Math.abs(b) < 1024) return `${b.toFixed(unit === 'bytes' ? 0 : 1)} ${unit}`
    b /= 1024
  }
  return `${b.toFixed(1)} TB`
}

export function DocumentUploadSection({
  ownerType,
  ownerClientId,
  ownerLabel,
  maxTotalBytes,
  onEnsureOwner,
}: Props) {
  const [docs, setDocs] = useState<DocRecord[]>([])
  const [uploading, setUploading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [dragOver, setDragOver] = useState(false)
  const inputRef = useRef<HTMLInputElement>(null)
  const resolvedIdRef = useRef(ownerClientId)
  resolvedIdRef.current = ownerClientId

  const totalBytes = docs.reduce((s, d) => s + d.size_bytes, 0)

  const ensureId = useCallback((): string | null => {
    if (resolvedIdRef.current) return resolvedIdRef.current
    if (onEnsureOwner) {
      const id = onEnsureOwner()
      if (id) resolvedIdRef.current = id
      return id
    }
    return null
  }, [onEnsureOwner])

  const loadDocs = useCallback(async () => {
    const id = resolvedIdRef.current
    if (!id) {
      setDocs([])
      return
    }
    try {
      const r = await apiFetch(
        `/api/users/me/documents?owner_type=${ownerType}&owner_client_id=${encodeURIComponent(id)}`,
      )
      if (r.ok) {
        const list: DocRecord[] = await r.json()
        setDocs(list)
      }
    } catch {
      /* silent */
    }
  }, [ownerType, ownerClientId]) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    void loadDocs()
  }, [loadDocs])

  const doUpload = useCallback(
    async (files: FileList | File[]) => {
      setError(null)
      const ownerId = ensureId()
      if (!ownerId) {
        setError('Could not save draft. Please fill in at least a name, then try again.')
        return
      }

      const fileArray = Array.from(files)
      for (const file of fileArray) {
        const ext = file.name.slice(file.name.lastIndexOf('.')).toLowerCase()
        if (ext !== '.txt' && ext !== '.md') {
          setError(`"${file.name}" is not a supported file type. Only .txt and .md files are accepted.`)
          return
        }
        if (file.size > MAX_FILE_BYTES) {
          setError(`"${file.name}" is too large (${humanSize(file.size)}). Maximum is ${humanSize(MAX_FILE_BYTES)} per file.`)
          return
        }
        if (totalBytes + file.size > maxTotalBytes) {
          setError(
            `Adding "${file.name}" would exceed the ${humanSize(maxTotalBytes)} limit for ${ownerLabel} (currently using ${humanSize(totalBytes)}).`,
          )
          return
        }
      }

      setUploading(true)
      try {
        for (const file of fileArray) {
          const fd = new FormData()
          fd.append('owner_type', ownerType)
          fd.append('owner_client_id', ownerId)
          fd.append('file', file)
          const r = await apiFetch('/api/users/me/documents', {
            method: 'POST',
            body: fd,
          })
          if (!r.ok) {
            const body = await r.json().catch(() => null)
            throw new Error(body?.detail || `Upload failed (${r.status})`)
          }
        }
        await loadDocs()
      } catch (e: unknown) {
        setError(e instanceof Error ? e.message : 'Upload failed')
      } finally {
        setUploading(false)
        if (inputRef.current) inputRef.current.value = ''
      }
    },
    [ownerType, ensureId, ownerLabel, totalBytes, maxTotalBytes, loadDocs],
  )

  const handleFileInput = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      if (e.target.files?.length) void doUpload(e.target.files)
    },
    [doUpload],
  )

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault()
      setDragOver(false)
      if (e.dataTransfer.files?.length) void doUpload(e.dataTransfer.files)
    },
    [doUpload],
  )

  const handleDelete = useCallback(
    async (docId: string) => {
      try {
        const r = await apiFetch(`/api/users/me/documents/${docId}`, { method: 'DELETE' })
        if (r.ok) {
          setDocs((prev) => prev.filter((d) => d.id !== docId))
          setError(null)
        }
      } catch {
        /* silent */
      }
    },
    [],
  )

  return (
    <div className="doc-upload-section">
      <h3>RAG Documents</h3>
      <p className="field-hint" style={{ marginBottom: '0.75rem' }}>
        Upload <code>.txt</code> or <code>.md</code> reference documents for {ownerLabel}.
        These will be available as context during conversations.
      </p>

      {/* ── Drop zone ─────────────────────────────────────────────────── */}
      <div
        className={`doc-upload-dropzone${dragOver ? ' doc-upload-dropzone--active' : ''}`}
        onDragOver={(e) => {
          e.preventDefault()
          setDragOver(true)
        }}
        onDragLeave={() => setDragOver(false)}
        onDrop={handleDrop}
      >
        <input
          ref={inputRef}
          type="file"
          accept={ACCEPTED}
          multiple
          style={{ display: 'none' }}
          onChange={handleFileInput}
        />

        <div className="doc-upload-dropzone-icon">&#128196;</div>

        {uploading ? (
          <p className="doc-upload-dropzone-text">Uploading…</p>
        ) : (
          <>
            <p className="doc-upload-dropzone-text">
              Drag &amp; drop <code>.txt</code> or <code>.md</code> files here
            </p>
            <span className="doc-upload-dropzone-or">or</span>
            <button
              type="button"
              className="btn btn-secondary doc-upload-browse-btn"
              onClick={() => inputRef.current?.click()}
            >
              Choose Files
            </button>
            <p className="doc-upload-dropzone-limits">
              Up to {humanSize(MAX_FILE_BYTES)} per file&ensp;·&ensp;{humanSize(maxTotalBytes)} total
            </p>
          </>
        )}
      </div>

      {error && <p className="doc-upload-error">{error}</p>}

      {/* ── Uploaded file list ────────────────────────────────────────── */}
      {docs.length > 0 && (
        <>
          <div className="doc-upload-usage">
            {docs.length} document{docs.length !== 1 ? 's' : ''}&ensp;·&ensp;{humanSize(totalBytes)} of {humanSize(maxTotalBytes)} used
          </div>
          <ul className="doc-upload-list">
            {docs.map((d) => (
              <li key={d.id} className="doc-upload-item">
                <span className="doc-upload-item-icon">&#128196;</span>
                <span className="doc-upload-name" title={d.filename}>
                  {d.filename}
                </span>
                <span className="doc-upload-size">{humanSize(d.size_bytes)}</span>
                <button
                  type="button"
                  className="doc-upload-delete"
                  title="Remove document"
                  onClick={() => handleDelete(d.id)}
                >
                  ×
                </button>
              </li>
            ))}
          </ul>
        </>
      )}
    </div>
  )
}
