'use client';
import { ChangeEvent, FunctionComponent, useEffect, useRef, useState } from 'react';
import { useTranslations } from 'next-intl';
import Box from '@mui/material/Box';
import LinearProgress from '@mui/material/LinearProgress';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import AppButton from '../common/AppButton';
import AppIcon from '../common/AppIcon';
import SurfaceCard from '../common/SurfaceCard';
import AccentCard from '../common/AccentCard';
import { ACCEPTED_DOCUMENT_TYPES, MAX_DOCUMENT_SIZE_BYTES } from '@/services/verification/constants';

/** The stored document as the uploader displays it — a name + optional size, never bytes. */
export interface UploadedDocInfo {
  name: string;
  sizeBytes?: number;
}

type UploadState = 'idle' | 'uploading' | 'success' | 'error';

export interface DocumentUploadProps {
  /** Field label (already translated). */
  label: string;
  /** Optional helper text under the label. */
  hint?: string;
  /** Accepted MIME types. Defaults to jpg/png/pdf (the b6 object-storage limits). */
  accept?: readonly string[];
  /** Max file size in bytes. Defaults to 5 MB. */
  maxSizeBytes?: number;
  /** Mobile camera hint: `environment` (rear — ID card) or `user` (front — selfie). */
  capture?: 'user' | 'environment';
  disabled?: boolean;
  /**
   * The async upload action — receives the file + a progress reporter (0–100) and resolves with the
   * stored doc. For a server step this calls the verification seam; for a local capture (B4 identity) it
   * validates + resolves locally without a round-trip.
   */
  onUpload: (file: File, onProgress: (percent: number) => void) => Promise<UploadedDocInfo>;
  /** Called after a successful upload with the resolved metadata. */
  onUploaded?: (doc: UploadedDocInfo) => void;
  /** A previously-uploaded document (drives the "already uploaded ✓" state from server metadata). */
  existingDoc?: UploadedDocInfo | null;
  /** When the step was rejected — renders the reason and a re-upload affordance (never a dead end). */
  rejected?: boolean;
  rejectionReason?: string;
}

/**
 * Reusable document uploader for every verification document step (national-ID card, license, education
 * cert, criminal record). Owns the full state machine — idle → validating → uploading (progress %) →
 * success (✓ + file name / local image preview) → error (retry) — with client-side type/size validation
 * before any upload and a re-upload affordance on reject. Returns the server's stored **metadata** only;
 * a local image preview never becomes the source of the "uploaded" truth. Its own chrome strings come
 * from the `verification` namespace; the caller passes the field `label`/`hint`.
 * @component DocumentUpload
 */
const DocumentUpload: FunctionComponent<DocumentUploadProps> = ({
  label,
  hint,
  accept = ACCEPTED_DOCUMENT_TYPES,
  maxSizeBytes = MAX_DOCUMENT_SIZE_BYTES,
  capture,
  disabled = false,
  onUpload,
  onUploaded,
  existingDoc = null,
  rejected = false,
  rejectionReason,
}) => {
  const t = useTranslations('verification');
  const inputRef = useRef<HTMLInputElement>(null);

  const [state, setState] = useState<UploadState>('idle');
  const [progress, setProgress] = useState(0);
  const [errorKey, setErrorKey] = useState<string | null>(null);
  const [fileName, setFileName] = useState<string | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);

  // A local image preview is an object URL — revoke it when it changes or the component unmounts.
  useEffect(() => {
    return () => {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
    };
  }, [previewUrl]);

  const openPicker = () => inputRef.current?.click();

  const onFileSelected = async (event: ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    event.target.value = ''; // allow re-picking the same file after an error
    if (!file) return;

    if (!accept.includes(file.type)) {
      setState('error');
      setErrorKey('upload_bad_type');
      return;
    }
    if (file.size > maxSizeBytes) {
      setState('error');
      setErrorKey('upload_too_large');
      return;
    }

    setErrorKey(null);
    setFileName(file.name);
    if (file.type.startsWith('image/')) {
      setPreviewUrl((prev) => {
        if (prev) URL.revokeObjectURL(prev);
        return URL.createObjectURL(file);
      });
    }
    setProgress(0);
    setState('uploading');

    try {
      const doc = await onUpload(file, setProgress);
      setFileName(doc.name);
      setState('success');
      onUploaded?.(doc);
    } catch {
      // 401/403/5xx are already toasted by the fetch layer; show a retryable inline error here.
      setState('error');
      setErrorKey('upload_error');
    }
  };

  const megabytes = Math.round(maxSizeBytes / (1024 * 1024));
  const acceptAttr = accept.join(',');
  // The "already uploaded" resting state is driven by server metadata (existingDoc) or a just-completed
  // upload — never by retained bytes.
  const showUploaded = state === 'success' || (state === 'idle' && existingDoc != null);
  const uploadedName = fileName ?? existingDoc?.name ?? '';

  return (
    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
      <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
        {label}
      </Typography>
      {hint ? (
        <Typography variant="caption" sx={{ color: 'text.secondary' }}>
          {hint}
        </Typography>
      ) : null}

      <input
        ref={inputRef}
        type="file"
        accept={acceptAttr}
        capture={capture}
        hidden
        disabled={disabled}
        onChange={onFileSelected}
      />

      {rejected ? (
        <AccentCard tone="error" padding="sm" data-upload-state="rejected" sx={UPLOAD_PANEL_SX}>
          <Stack direction="row" sx={{ gap: 1, alignItems: 'center' }}>
            <AppIcon icon="rejected" size={20} color="var(--bal-error)" />
            <Typography variant="body2" sx={{ fontWeight: 700 }}>
              {t('upload_rejected')}
            </Typography>
          </Stack>
          {rejectionReason ? (
            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
              {rejectionReason}
            </Typography>
          ) : null}
          <AppButton
            variant="outlined"
            color="primary"
            startIcon="upload"
            onClick={openPicker}
            disabled={disabled}
            sx={{ alignSelf: 'flex-start' }}
          >
            {t('upload_reupload')}
          </AppButton>
        </AccentCard>
      ) : state === 'uploading' ? (
        <SurfaceCard padding="sm" data-upload-state="uploading" sx={UPLOAD_PANEL_SX}>
          <Stack direction="row" sx={{ gap: 1, alignItems: 'center' }}>
            <AppIcon icon="upload" size={20} color="var(--bal-primary)" />
            <Typography variant="body2" sx={{ fontWeight: 500, flexGrow: 1, wordBreak: 'break-all' }}>
              {fileName}
            </Typography>
            <Typography variant="caption" sx={{ color: 'text.secondary' }}>
              {progress}%
            </Typography>
          </Stack>
          <LinearProgress variant="determinate" value={progress} sx={{ borderRadius: 1, height: 6 }} />
          <Typography variant="caption" sx={{ color: 'text.secondary' }}>
            {t('upload_uploading')}
          </Typography>
        </SurfaceCard>
      ) : showUploaded ? (
        <SurfaceCard padding="sm" data-upload-state="success" sx={UPLOAD_PANEL_SX}>
          <Stack direction="row" sx={{ gap: 1.5, alignItems: 'center' }}>
            {previewUrl ? (
              <Box
                component="img"
                src={previewUrl}
                alt=""
                sx={{ width: 48, height: 48, objectFit: 'cover', borderRadius: 1.5, flexShrink: 0 }}
              />
            ) : (
              <AppIcon icon="document" size={28} color="var(--bal-primary)" />
            )}
            <Stack sx={{ gap: 0.25, flexGrow: 1, minWidth: 0 }}>
              <Typography variant="body2" sx={{ fontWeight: 500, wordBreak: 'break-all' }}>
                {uploadedName}
              </Typography>
              <Stack direction="row" sx={{ gap: 0.5, alignItems: 'center' }}>
                <AppIcon icon="verified" size={16} color="var(--bal-success)" />
                <Typography variant="caption" sx={{ color: 'var(--bal-success)' }}>
                  {t('upload_success')}
                </Typography>
              </Stack>
            </Stack>
            <AppButton variant="text" color="primary" onClick={openPicker} disabled={disabled}>
              {t('upload_change')}
            </AppButton>
          </Stack>
        </SurfaceCard>
      ) : state === 'error' ? (
        <AccentCard tone="error" padding="sm" data-upload-state="error" sx={UPLOAD_PANEL_SX}>
          <Stack direction="row" sx={{ gap: 1, alignItems: 'center' }}>
            <AppIcon icon="error" size={20} color="var(--bal-error)" />
            <Typography variant="body2" sx={{ color: 'var(--bal-error)', flexGrow: 1 }}>
              {t(errorKey ?? 'upload_error', { size: megabytes })}
            </Typography>
          </Stack>
          <AppButton
            variant="outlined"
            color="primary"
            startIcon="refresh"
            onClick={openPicker}
            disabled={disabled}
            sx={{ alignSelf: 'flex-start' }}
          >
            {t('upload_retry')}
          </AppButton>
        </AccentCard>
      ) : (
        <Box
          role="button"
          tabIndex={disabled ? -1 : 0}
          data-upload-state="idle"
          onClick={disabled ? undefined : openPicker}
          onKeyDown={(event) => {
            if (!disabled && (event.key === 'Enter' || event.key === ' ')) {
              event.preventDefault();
              openPicker();
            }
          }}
          sx={{
            p: 3,
            borderRadius: 2,
            border: '1px dashed',
            borderColor: 'divider',
            textAlign: 'center',
            cursor: disabled ? 'default' : 'pointer',
            opacity: disabled ? 0.6 : 1,
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            gap: 0.75,
            transition: 'border-color 120ms',
            '&:hover': disabled ? undefined : { borderColor: 'var(--bal-primary)' },
          }}
        >
          <AppIcon icon={capture ? 'camera' : 'upload'} size={28} color="var(--bal-primary)" />
          <Typography variant="body2" sx={{ fontWeight: 700 }}>
            {capture ? t('upload_capture') : t('upload_choose')}
          </Typography>
          <Typography variant="caption" sx={{ color: 'text.secondary' }}>
            {t('upload_size_hint', { size: megabytes })}
          </Typography>
        </Box>
      )}
    </Box>
  );
};

// Shared layout on top of SurfaceCard/AccentCard for the uploading / success / error / rejected states.
const UPLOAD_PANEL_SX = {
  display: 'flex',
  flexDirection: 'column',
  gap: 1,
} as const;

export default DocumentUpload;
