import { useEffect, useRef, useState } from 'react'
import { useCamera } from '../media/useCamera'
import { useRecorder } from '../media/useRecorder'
import { fileToDataUri, validateAudioFile, validateImageFile } from '../media/validate'
import { useI18n } from '../i18n'
import { Button } from './ui'
import type { MessageKey } from '../i18n/en'

export type Modality = 'face' | 'fingerprint' | 'voice'

function toImagePreview(v: string): string {
  return v.startsWith('data:') ? v : `data:image/jpeg;base64,${v}`
}

interface Props {
  modality: Modality
  id: string
  value: string | null
  onChange: (dataUri: string | null) => void
  demoSample?: string
}

/** One modality's sample control. Face = camera + upload + demo; Fingerprint =
 *  upload + demo (optical IMAGE, never a device sensor); Voice = record + upload + demo.
 *  Media tracks are always stopped on capture / replace / unmount. */
export function SampleCapture({ modality, id, value, onChange, demoSample }: Props) {
  const { t } = useI18n()
  const cam = useCamera()
  const rec = useRecorder()
  const [error, setError] = useState<MessageKey | null>(null)
  const [camOpen, setCamOpen] = useState(false)
  const fileRef = useRef<HTMLInputElement | null>(null)

  // Push recorded audio up to the parent when it becomes available.
  useEffect(() => {
    if (modality === 'voice' && rec.dataUri) { onChange(rec.dataUri); setError(null) }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rec.dataUri])

  async function onFile(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]
    if (!file) return
    const err = modality === 'voice' ? validateAudioFile(file) : validateImageFile(file)
    if (err) { setError(err); return }
    try { onChange(await fileToDataUri(file)); setError(null) } catch { setError('bio.err.readFailed') }
  }

  function useDemo() {
    if (demoSample) { onChange(demoSample); setError(null); setCamOpen(false); cam.stop() }
  }

  function openCamera() { setCamOpen(true); setError(null); void cam.start() }
  function doCapture() {
    const shot = cam.capture()
    if (shot) { onChange(shot); setError(null) }
    setCamOpen(false); cam.stop() // stop tracks immediately after the still is taken
  }
  function clear() { onChange(null); rec.reset(); setError(null) }

  const label = t(`bio.modality.${modality}` as MessageKey)

  return (
    <fieldset className="sample" aria-labelledby={`${id}-lbl`}>
      <legend id={`${id}-lbl`}>{label}</legend>

      {/* Existing sample preview */}
      {value && modality !== 'voice' && (
        <div className="sample-preview">
          <img src={toImagePreview(value)} alt={t('bio.preview', { m: label })} />
          <Button onClick={clear}>{t('bio.retake')}</Button>
        </div>
      )}
      {value && modality === 'voice' && (
        <div className="sample-preview">
          {rec.playbackUrl && <audio src={rec.playbackUrl} controls aria-label={t('bio.playback')} />}
          <Button onClick={clear}>{t('bio.delete')}</Button>
        </div>
      )}

      {/* Capture controls (hidden once a sample exists) */}
      {!value && (
        <div className="sample-controls">
          {modality === 'face' && !camOpen && (
            <Button onClick={openCamera}>{t('bio.useCamera')}</Button>
          )}
          {modality === 'face' && camOpen && (
            <div className="cam">
              {cam.status === 'live' && (
                <>
                  <video ref={cam.videoRef} playsInline muted aria-label={t('bio.cameraPreview')} />
                  <div className="row">
                    <Button onClick={doCapture}>{t('bio.capture')}</Button>
                    <Button onClick={() => { setCamOpen(false); cam.stop() }}>{t('bio.cancel')}</Button>
                    {cam.devices.length > 1 && (
                      <select aria-label={t('bio.switchCamera')} value={cam.activeDeviceId ?? ''}
                              onChange={(e) => cam.switchTo(e.target.value)}>
                        {cam.devices.map((d, i) => <option key={d.deviceId} value={d.deviceId}>{d.label || `Camera ${i + 1}`}</option>)}
                      </select>
                    )}
                  </div>
                </>
              )}
              {cam.status === 'requesting' && <p className="hint">{t('bio.cameraRequesting')}</p>}
              {cam.status === 'denied' && <p className="field-error" role="alert">{t('bio.cameraDenied')}</p>}
              {cam.status === 'nocamera' && <p className="field-error" role="alert">{t('bio.cameraNone')}</p>}
              {cam.status === 'inuse' && <p className="field-error" role="alert">{t('bio.cameraInUse')}</p>}
              {cam.status === 'error' && <p className="field-error" role="alert">{t('bio.cameraError')}</p>}
            </div>
          )}

          {modality === 'voice' && (
            <div className="rec">
              {rec.status !== 'recording'
                ? <Button onClick={() => void rec.start()}>{t('bio.record')}</Button>
                : <Button onClick={rec.stop}>{t('bio.stop')} ({rec.elapsed}s)</Button>}
              {rec.status === 'denied' && <p className="field-error" role="alert">{t('bio.micDenied')}</p>}
              {rec.status === 'unsupported' && <p className="field-error" role="alert">{t('bio.micUnsupported')}</p>}
            </div>
          )}

          <label className="upload-btn">
            {modality === 'voice' ? t('bio.uploadAudio') : t('bio.uploadImage')}
            <input ref={fileRef} type="file" hidden
                   accept={modality === 'voice' ? 'audio/*' : 'image/*'} onChange={onFile} />
          </label>

          {demoSample && <Button onClick={useDemo}>{t('bio.useDemo')}</Button>}
        </div>
      )}

      {modality === 'fingerprint' && (
        <p className="hint">{t('bio.fingerprintNote')}</p>
      )}
      {error && <p className="field-error" role="alert">{t(error)}</p>}
    </fieldset>
  )
}
