import React, { useEffect, useRef, useState } from 'react'
import { api, WhoAmI } from './api'
import * as tus from 'tus-js-client'

type FileRow = { name: string; path: string; is_dir: boolean; size: number; mtime: number }
type Sel = { file: File; desc: string; date: string; finalName: string; progress?: number; err?: string }

// Simple bundle heartbeat so you can confirm the loaded JS is current
console.log('[Pegasus] FE bundle activated at', new Date().toISOString())

function sanitizeDesc(s:string){
  s = s.trim().replace(/[^A-Za-z0-9 _-]+/g,'_').replace(/\s+/g,'_')
  if(!s) s = 'upload'
  return s.slice(0,64)
}
function extOf(n:string){ const i=n.lastIndexOf('.'); return i>-1 ? n.slice(i+1).toLowerCase() : 'bin' }
function composeName(date:string, desc:string, orig:string){
  const d = date || new Date().toISOString().slice(0,10) // YYYY-MM-DD
  const [Y,M,D] = d.split('-'); return `${Y}.${M}.${D}.${sanitizeDesc(desc)}.${extOf(orig)}`
}

// Type guard to narrow tus error
function isDetailedError(e: unknown): e is tus.DetailedError {
  return typeof e === 'object' && e !== null && ('originalRequest' in (e as any) || 'originalResponse' in (e as any))
}

export default function Uploader(){
  const [me, setMe] = useState<WhoAmI|undefined>()
  const [cwd, setCwd] = useState<string>('')                 // relative to user's mapped root
  const [rows, setRows] = useState<FileRow[]>([])
  const destPath = `/${[me?.root, cwd].filter(Boolean).join('/')}`
  const [status, setStatus] = useState<string>('')
  const [globalDate, setGlobalDate] = useState<string>(new Date().toISOString().slice(0,10))
  const [uploading, setUploading] = useState<boolean>(false)
  const [sel, setSel] = useState<Sel[]>([])
  const folderInputRef = useRef<HTMLInputElement>(null)       // to set webkitdirectory

  // Enable directory selection on the folder picker (non-standard attr)
  useEffect(() => {
    const el = folderInputRef.current
    if (el) {
      el.setAttribute('webkitdirectory','')
      el.setAttribute('directory','')
    }
  }, [])

  async function refresh(path=''){
    try {
      const m = await api<WhoAmI>('/api/whoami'); setMe(m)
      const list = await api<FileRow[]>('/api/list?path='+encodeURIComponent(path))
      setRows(list); setCwd(path)
      setStatus(`Ready · Destination: /${[m.root, path].filter(Boolean).join('/')}`)
      console.log('[Pegasus] list ok', { path, count: list.length })
    } catch (e:any) {
      const msg = String(e?.message || e || '')
      console.error('[Pegasus] list error', e)
      setStatus(`List error: ${msg}`)
      if (msg.toLowerCase().includes('no mapping')) {
        alert('Your account is not linked to any upload library yet. Please contact the admin to be granted access.')
        try { await api('/api/logout', { method:'POST' }) } catch {}
        location.replace('/') // back to login
      }
    }
  }
  useEffect(()=>{ setStatus('Loading profile & folder list…'); refresh('') }, [])

  function handleChoose(files: FileList){
    const arr = Array.from(files).map(f=>{
      const base = (f as any).webkitRelativePath || f.name
      const name = base.split('/').pop() || f.name
      const desc = '' // start empty; user must fill before upload
      return { file:f, desc, date: globalDate, finalName: composeName(globalDate, sanitizeDesc(desc), name), progress: 0 }
    })
    console.log('[Pegasus] selected files', arr.map(a=>a.file.name))
    setSel(arr)
  }

  // When the global date changes, recompute per-file finalName (leave per-file date in place)
  useEffect(()=>{
    setSel(old => old.map(x=> ({...x, date: globalDate, finalName: composeName(globalDate, x.desc, x.file.name)})))
  }, [globalDate])

  // Warn before closing mid-upload
  useEffect(()=>{
    const handler = (e: BeforeUnloadEvent) => { if (uploading) { e.preventDefault(); e.returnValue = '' } }
    window.addEventListener('beforeunload', handler); return () => window.removeEventListener('beforeunload', handler)
  }, [uploading])

  async function doUpload(){
    if(!me) { setStatus('Not signed in'); return }
    if(sel.some(s => !s.desc.trim())){ alert('Please provide a brief description for every file.'); return }
    setStatus('Starting upload…')
    setUploading(true)
    try{
      for(const s of sel){
        // eslint-disable-next-line no-await-in-loop
        await new Promise<void>((resolve,reject)=>{
          const opts: tus.UploadOptions & { withCredentials?: boolean } = {
            endpoint: '/tus/',
            chunkSize: 5*1024*1024,
            retryDelays: [0, 1000, 3000, 5000, 10000],
            resume: false,                     // ignore any old http:// resume URLs
            removeFingerprintOnSuccess: true,  // keep storage clean going forward
            metadata: {
              filename: s.file.name,
              subdir: cwd || "",
              date: s.date,                  // per-file YYYY-MM-DD
              desc: s.desc                   // server composes final
            },
            onError: (err: Error | tus.DetailedError)=>{
              let msg = String(err)
              if (isDetailedError(err)) {
                const status = (err.originalRequest as any)?.status
                const statusText = (err.originalRequest as any)?.statusText
                if (status) msg = `HTTP ${status} ${statusText || ''}`.trim()
              }
              console.error('[Pegasus] tus error', s.file.name, err)
              setSel(old => old.map(x => x.file===s.file ? ({...x, err: msg}) : x))
              setStatus(`✖ ${s.file.name}: ${msg}`)
              alert(`Upload failed: ${s.file.name}\n\n${msg}`)
              reject(err as any)
            },
            onProgress: (sent: number, total: number)=>{
              const pct = Math.floor(sent/Math.max(total,1)*100)
              setSel(old => old.map(x => x.file===s.file ? ({...x, progress: pct}) : x))
              setStatus(`⬆ ${s.finalName}: ${pct}%`)
            },
            onSuccess: ()=>{
              setSel(old => old.map(x => x.file===s.file ? ({...x, progress: 100, err: undefined}) : x))
              setStatus(`✔ ${s.finalName} uploaded`)
              console.log('[Pegasus] tus success', s.file.name)
              resolve()
            },
          }
          // Ensure cookie is sent even if the tus typings don’t list this field
          opts.withCredentials = true

          const up = new tus.Upload(s.file, opts)
          console.log('[Pegasus] tus.start', { name: s.file.name, size: s.file.size, type: s.file.type, cwd })
          up.start()
        })
      }
      setStatus('All uploads complete')
      setSel([])
      await refresh(cwd)
    } finally {
      setUploading(false)
    }
  }

  async function rename(oldp:string){
    const name = prompt('New name (YYYY.MM.DD.description.ext):', oldp.split('/').pop()||''); if(!name) return
    try{
      await api('/api/rename', {
        method:'POST',
        headers:{'content-type':'application/json'},
        body: JSON.stringify({from:oldp, to: (oldp.split('/').slice(0,-1).concat(name)).join('/')})
      })
      await refresh(cwd)
    } catch(e:any){
      console.error('[Pegasus] rename error', e)
      alert(`Rename failed:\n${e?.message || e}`)
    }
  }

  async function del(p:string, recursive:boolean){
    if(!confirm(`Delete ${p}${recursive?' (recursive)':''}?`)) return
    try{
      await api('/api/file?path='+encodeURIComponent(p)+'&recursive='+(recursive?'true':'false'), { method:'DELETE' })
      await refresh(cwd)
    } catch(e:any){
      console.error('[Pegasus] delete error', e)
      alert(`Delete failed:\n${e?.message || e}`)
    }
  }

  function goUp(){
    const up = cwd.split('/').slice(0,-1).join('/')
    setCwd(up); void refresh(up)
  }

  return (<>
    <section className="card">
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', gap:8, flexWrap:'wrap'}}>
        <div><b>Signed in:</b> {me?.username} · <span className="meta">root: /{me?.root}</span></div>
        <div className="meta">Destination: <b>{destPath || '/(unknown)'}</b></div>
      </div>

      <div className="grid" style={{alignItems:'end'}}>
        <div>
          <label className="meta">Default date (applied to new selections)</label>
          <input type="date" value={globalDate} onChange={e=> setGlobalDate(e.target.value)} />
        </div>

        <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
          <div>
            <label className="meta">Gallery/Photos</label>
            <input
              type="file"
              multiple
              accept="image/*,video/*"
              capture="environment"
              onChange={e=> e.target.files && handleChoose(e.target.files)}
            />
          </div>
          <div className="hide-on-mobile">
            <label className="meta">Files/Folders</label>
            {/* set webkitdirectory/directory via ref to satisfy TS */}
            <input
              type="file"
              multiple
              ref={folderInputRef}
              onChange={e=> e.target.files && handleChoose(e.target.files)}
            />
          </div>
        </div>

        {(() => {
          const uploadDisabled = !sel.length || uploading || sel.some(s=>!s.desc.trim())
          let disabledReason = ''
          if (!sel.length) disabledReason = 'Select at least one file.'
          else if (uploading) disabledReason = 'Upload in progress…'
          else if (sel.some(s=>!s.desc.trim())) disabledReason = 'Add a short description for every file.'

          return (
            <>
              <button
                type="button"
                className="btn"
                onClick={()=>{
                  console.log('[Pegasus] Upload click', { files: sel.length, uploading, disabled: uploadDisabled })
                  setStatus('Upload button clicked')
                  if (!uploadDisabled) { void doUpload() }
                }}
                disabled={uploadDisabled}
                aria-disabled={uploadDisabled}
              >
                Upload {sel.length? `(${sel.length})` : ''}
              </button>
              {disabledReason && <div className="meta bad">{disabledReason}</div>}
            </>
          )
        })()}
      </div>

      {sel.length>0 && (
        <div className="card">
          <h4>Ready to upload</h4>
          <div className="grid">
            {sel.map((s,i)=>(
              <div key={i} className="item">
                <div className="meta">{s.file.name}</div>
                <input
                  value={s.desc}
                  placeholder="Short description (required)"
                  onChange={e=> {
                    const v=e.target.value
                    setSel(old => old.map((x,idx)=> idx===i
                      ? ({...x, desc:v, finalName: composeName(globalDate, v, x.file.name)})
                      : x ))
                  }}
                />
                <div style={{display:'grid', gridTemplateColumns:'auto 1fr', gap:8, alignItems:'center', marginTop:6}}>
                  <span className="meta">Date</span>
                  <input
                    type="date"
                    value={s.date}
                    onChange={e=>{
                      const v = e.target.value
                      setSel(old => old.map((x,idx)=> idx===i ? ({...x, date:v, finalName: composeName(v, x.desc, x.file.name)}) : x))
                    }}
                  />
                </div>
                <div className="meta">→ {s.finalName}</div>
                {typeof s.progress === 'number' && (
                  <>
                    <progress max={100} value={s.progress}></progress>
                    {s.err && <div className="meta bad">Error: {s.err}</div>}
                  </>
                )}
              </div>
            ))}
          </div>
        </div>
      )}

      <div className="meta" style={{marginTop:8}}>{status}</div>
    </section>

    <section className="card">
      <h3>Folder</h3>
      {rows.length === 0 ? (
        <div className="item">
          <div className="meta">
            No items to show here. You’ll upload into <b>{destPath}</b>.
          </div>
          <CreateFolder cwd={cwd} onCreate={(p)=>{ setCwd(p); void refresh(p) }} />
        </div>
      ) : (
      <div className="grid">
        {cwd && (
          <div className="item">
            <a className="name" href="#" onClick={(e)=>{e.preventDefault(); goUp()}}>⬆ Up</a>
          </div>
        )}
        {rows.sort((a,b)=> (Number(b.is_dir)-Number(a.is_dir)) || a.name.localeCompare(b.name)).map(f=>
          <div key={f.path} className="item">
            <div className="name">{f.is_dir?'📁':'🎞️'} {f.name}</div>
            <div className="meta">{f.is_dir?'folder':fmt(f.size)} · {new Date(f.mtime*1000).toLocaleString()}</div>
            <div className="meta">
              {f.is_dir
                ? (<><a href="#" onClick={(e)=>{e.preventDefault(); setCwd(f.path); void refresh(f.path)}}>Open</a> · <a href="#" onClick={(e)=>{e.preventDefault(); void rename(f.path)}}>Rename</a> · <a className="bad" href="#" onClick={(e)=>{e.preventDefault(); void del(f.path,true)}}>Delete</a></>)
                : (<><a href="#" onClick={(e)=>{e.preventDefault(); void rename(f.path)}}>Rename</a> · <a className="bad" href="#" onClick={(e)=>{e.preventDefault(); void del(f.path,false)}}>Delete</a></>)
              }
            </div>
          </div>
        )}
      </div>
      )}
    </section>
  </>)
}

function fmt(n:number){ if(n<1024) return n+' B'; const u=['KB','MB','GB','TB']; let i=-1; do{ n/=1024; i++ }while(n>=1024&&i<u.length-1); return n.toFixed(1)+' '+u[i] }

function CreateFolder({ cwd, onCreate }:{ cwd:string; onCreate:(p:string)=>void }) {
  const [name, setName] = React.useState('');
  async function submit() {
    const clean = name.trim().replace(/[\/]+/g,'/').replace(/^\//,'').replace(/[^\w\-\s.]/g,'_');
    if (!clean) return;
    const path = [cwd, clean].filter(Boolean).join('/');
    console.log('[Pegasus] mkdir click', { path })
    try {
      await api('/api/mkdir', { method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({ path }) });
      onCreate(path);
      setName('');
    } catch (e:any) {
      console.error('[Pegasus] mkdir error', e)
      alert(`Create folder failed:\n${e?.message || e}`)
    }
  }
  return (
    <div style={{marginTop:10, display:'flex', gap:8, alignItems:'center'}}>
      <input placeholder="New folder name" value={name} onChange={e=>setName(e.target.value)} />
      <button type="button" className="btn" onClick={submit} disabled={!name.trim()}>Create</button>
    </div>
  );
}
