'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import { Camera, Loader2, X } from 'lucide-react';
import { DocumentPhotoGallery } from '@/components/documents/document-photo-gallery';
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { useToast } from '@/components/id/toast-provider';
import {
  buildDocumentNumber,
  DOCUMENT_TYPES,
  DocumentTypeDef,
  LICENSE_CATEGORIES,
  parseDocumentPhotos,
  parseMetadata,
  withDocumentPhotos,
  type DocumentTypeCode
} from '@/lib/document-catalog';
import { apiFetch, getApiErrorMessage, uploadMediaObject, UserDocument } from '@/lib/api';
import { cn } from '@/lib/utils';

interface PresignedUploadResponse {
  uploadUrl: string;
  storageKey: string;
  expiresAt: string;
}

function FieldLabel({ children }: { children: React.ReactNode }) {
  return <label className="block text-sm font-medium text-[#1f2430]">{children}</label>;
}

function GenderToggle({ value, onChange }: { value: string; onChange: (value: string) => void }) {
  return (
    <div className="flex rounded-2xl bg-[#f4f5f8] p-1">
      {[
        { id: 'male', label: 'Мужской' },
        { id: 'female', label: 'Женский' }
      ].map((option) => (
        <button
          key={option.id}
          type="button"
          onClick={() => onChange(option.id)}
          className={cn(
            'flex-1 rounded-xl px-3 py-2 text-sm transition',
            value === option.id ? 'bg-white font-medium shadow-sm' : 'text-[#667085]'
          )}
        >
          {option.label}
        </button>
      ))}
    </div>
  );
}

function CategoryPicker({ value, onChange }: { value: string; onChange: (value: string) => void }) {
  const selected = new Set(value.split(',').map((item) => item.trim()).filter(Boolean));
  return (
    <div className="grid grid-cols-4 gap-2">
      {LICENSE_CATEGORIES.map((category) => {
        const active = selected.has(category);
        return (
          <button
            key={category}
            type="button"
            onClick={() => {
              const next = new Set(selected);
              if (active) next.delete(category);
              else next.add(category);
              onChange(Array.from(next).join(','));
            }}
            className={cn(
              'rounded-xl border px-2 py-2 text-sm transition',
              active ? 'border-[#20212b] bg-[#20212b] text-white' : 'border-[#eceef4] bg-white text-[#667085]'
            )}
          >
            {category}
          </button>
        );
      })}
    </div>
  );
}

function buildInitialValues(config: DocumentTypeDef, existing?: UserDocument | null) {
  const metadata = parseMetadata(existing?.metadataJson);
  const initial: Record<string, string> = {};
  for (const field of config.fields) {
    const raw = metadata[field.key];
    initial[field.key] = typeof raw === 'string' ? raw : '';
    if (field.latinKey) {
      const latinRaw = metadata[field.latinKey];
      initial[field.latinKey] = typeof latinRaw === 'string' ? latinRaw : '';
    }
  }
  if (existing?.issuedAt) initial.issuedAt = existing.issuedAt.slice(0, 10);
  if (existing?.expiresAt) initial.expiresAt = existing.expiresAt.slice(0, 10);
  return initial;
}

export function DocumentFormDialog({
  open,
  onOpenChange,
  documentType,
  userId,
  token,
  existing,
  onSaved,
  readOnly
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  documentType: DocumentTypeCode;
  userId: string;
  token: string | null;
  existing?: UserDocument | null;
  onSaved: () => void | Promise<void>;
  readOnly?: boolean;
}) {
  const config = useMemo(() => DOCUMENT_TYPES.find((item) => item.type === documentType), [documentType]);
  const { showToast } = useToast();
  const photoInputRef = useRef<HTMLInputElement>(null);
  const formRef = useRef<HTMLFormElement>(null);
  const fieldValuesRef = useRef<Record<string, string>>({});
  const draftDocumentIdRef = useRef<string | undefined>(undefined);
  const photoStorageKeysRef = useRef<string[]>([]);
  const wasOpenRef = useRef(false);
  const [formInstanceKey, setFormInstanceKey] = useState(0);
  const [pickerValues, setPickerValues] = useState<Record<string, string>>({});
  const [photoStorageKeys, setPhotoStorageKeys] = useState<string[]>([]);
  const [draftDocumentId, setDraftDocumentId] = useState<string | undefined>();
  const [isSaving, setIsSaving] = useState(false);
  const [isPhotoUploading, setIsPhotoUploading] = useState(false);

  useEffect(() => {
    if (!open) {
      wasOpenRef.current = false;
      fieldValuesRef.current = {};
      draftDocumentIdRef.current = undefined;
      photoStorageKeysRef.current = [];
      return;
    }
    if (!config || wasOpenRef.current) return;

    wasOpenRef.current = true;
    const initial = buildInitialValues(config, existing);
    const photos = parseDocumentPhotos(parseMetadata(existing?.metadataJson));
    fieldValuesRef.current = { ...initial };
    photoStorageKeysRef.current = photos;
    draftDocumentIdRef.current = existing?.id;
    setPickerValues(initial);
    setPhotoStorageKeys(photos);
    setDraftDocumentId(existing?.id);
    setFormInstanceKey((current) => current + 1);
  }, [open, config, existing?.id, existing?.metadataJson, existing?.issuedAt, existing?.expiresAt]);

  useEffect(() => {
    if (!open || !existing?.id || draftDocumentIdRef.current) return;
    draftDocumentIdRef.current = existing.id;
    setDraftDocumentId(existing.id);
  }, [open, existing?.id]);

  function syncField(key: string, value: string) {
    fieldValuesRef.current[key] = value;
  }

  function setPickerField(key: string, value: string) {
    syncField(key, value);
    setPickerValues((current) => ({ ...current, [key]: value }));
  }

  function collectFormValues() {
    const values = { ...fieldValuesRef.current, ...pickerValues };

    if (formRef.current) {
      for (const element of formRef.current.elements) {
        if (element instanceof HTMLInputElement && element.name) {
          if (element.type === 'checkbox') {
            values[element.name] = element.checked ? 'true' : '';
          } else if (element.type !== 'radio') {
            values[element.name] = element.value;
          }
        } else if (element instanceof HTMLTextAreaElement && element.name) {
          values[element.name] = element.value;
        }
      }
    }

    fieldValuesRef.current = values;
    return values;
  }

  async function commitFocusedField() {
    const activeElement = document.activeElement;
    if (activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement) {
      activeElement.blur();
    }
    await new Promise<void>((resolve) => {
      requestAnimationFrame(() => resolve());
    });
  }

  function buildMetadataPayload(source: Record<string, string>, photos: string[]) {
    const metadata: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(source)) {
      if (key !== 'issuedAt' && key !== 'expiresAt') metadata[key] = value;
    }
    return JSON.stringify(withDocumentPhotos(metadata, photos));
  }

  function buildSaveBody(currentValues: Record<string, string>, photos: string[]) {
    return {
      number: buildDocumentNumber(config!, currentValues),
      issuedAt: currentValues.issuedAt ? new Date(currentValues.issuedAt).toISOString() : undefined,
      expiresAt: currentValues.expiresAt ? new Date(currentValues.expiresAt).toISOString() : undefined,
      metadataJson: buildMetadataPayload(currentValues, photos)
    };
  }

  async function patchDocument(documentId: string, currentValues: Record<string, string>, photos: string[]) {
    if (!token || !config) return;
    await apiFetch(
      `/documents/users/${userId}/${documentId}`,
      { method: 'PATCH', body: JSON.stringify(buildSaveBody(currentValues, photos)) },
      token
    );
  }

  async function ensureDocumentId(currentValues: Record<string, string>, photos: string[]) {
    if (draftDocumentIdRef.current) {
      return { id: draftDocumentIdRef.current, created: false };
    }
    if (!token || !config) {
      return { id: undefined, created: false };
    }

    const created = await apiFetch<UserDocument>(
      `/documents/users/${userId}`,
      {
        method: 'POST',
        body: JSON.stringify({
          type: documentType,
          ...buildSaveBody(currentValues, photos)
        })
      },
      token
    );
    draftDocumentIdRef.current = created.id;
    setDraftDocumentId(created.id);
    return { id: created.id, created: true };
  }

  async function uploadPhoto(file: File) {
    if (!token || readOnly) return;
    setIsPhotoUploading(true);
    try {
      await commitFocusedField();
      const currentValues = collectFormValues();
      const currentPhotos = photoStorageKeysRef.current;
      const { id: targetDocumentId, created } = await ensureDocumentId(currentValues, currentPhotos);
      if (!targetDocumentId) throw new Error('Не удалось создать документ для загрузки фото');

      const presigned = await apiFetch<PresignedUploadResponse>(
        `/media/documents/${targetDocumentId}/photo/upload-url`,
        { method: 'POST', body: JSON.stringify({ contentType: file.type }) },
        token
      );

      await uploadMediaObject(presigned, file, file.type);

      const nextPhotos = [...currentPhotos, presigned.storageKey];
      photoStorageKeysRef.current = nextPhotos;
      setPhotoStorageKeys(nextPhotos);
      await patchDocument(targetDocumentId, currentValues, nextPhotos);

      if (created) {
        await onSaved();
      }

      showToast('Фото добавлено');
    } catch (error) {
      const message = getApiErrorMessage(error, 'Не удалось загрузить фото');
      if (message) showToast(message);
    } finally {
      setIsPhotoUploading(false);
      if (photoInputRef.current) photoInputRef.current.value = '';
    }
  }

  async function handlePhotoChange(event: React.ChangeEvent<HTMLInputElement>) {
    const files = Array.from(event.target.files ?? []);
    for (const file of files) {
      await uploadPhoto(file);
    }
  }

  function removePhoto(storageKey: string) {
    const nextPhotos = photoStorageKeysRef.current.filter((item) => item !== storageKey);
    photoStorageKeysRef.current = nextPhotos;
    setPhotoStorageKeys(nextPhotos);
  }

  async function handleSave() {
    if (!token || !config || readOnly) return;
    setIsSaving(true);
    try {
      await commitFocusedField();
      const currentValues = collectFormValues();
      const photos = photoStorageKeysRef.current;
      const documentId = draftDocumentIdRef.current ?? existing?.id;

      if (documentId) {
        await patchDocument(documentId, currentValues, photos);
        showToast('Документ обновлён');
      } else {
        const created = await apiFetch<UserDocument>(
          `/documents/users/${userId}`,
          { method: 'POST', body: JSON.stringify({ type: documentType, ...buildSaveBody(currentValues, photos) }) },
          token
        );
        draftDocumentIdRef.current = created.id;
        setDraftDocumentId(created.id);
        showToast('Документ сохранён');
      }

      await onSaved();
      onOpenChange(false);
    } catch (error) {
      const message = getApiErrorMessage(error, 'Не удалось сохранить документ');
      if (message) showToast(message);
    } finally {
      setIsSaving(false);
    }
  }

  if (!config) return null;

  const initialValues = fieldValuesRef.current;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="max-h-[90vh] overflow-y-auto rounded-[28px] p-0 sm:max-w-[420px]">
        <DialogHeader className="sticky top-0 z-10 border-b border-[#eceef4] bg-white px-5 py-4">
          <div className="flex items-center justify-between">
            <DialogTitle className="text-xl font-semibold">{config.label}</DialogTitle>
            <button type="button" onClick={() => onOpenChange(false)} className="rounded-full p-2 hover:bg-[#f4f5f8]">
              <X className="h-4 w-4" />
            </button>
          </div>
        </DialogHeader>

        <div className="space-y-4 px-5 pb-5">
          {photoStorageKeys.length > 0 ? (
            <DocumentPhotoGallery
              userId={userId}
              token={token}
              storageKeys={photoStorageKeys}
              readOnly={readOnly}
              onRemove={readOnly ? undefined : removePhoto}
            />
          ) : null}

          {!readOnly ? (
            <>
              <button
                type="button"
                onClick={() => photoInputRef.current?.click()}
                disabled={isPhotoUploading}
                className="flex w-full items-center justify-center gap-2 rounded-[20px] bg-[#f4f5f8] px-4 py-4 text-sm font-medium transition hover:bg-[#eceef4]"
              >
                {isPhotoUploading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Camera className="h-4 w-4" />}
                {photoStorageKeys.length > 0 ? 'Добавить ещё фото' : 'Добавить фото'}
              </button>
              <input ref={photoInputRef} type="file" accept="image/*" multiple className="hidden" onChange={handlePhotoChange} />
            </>
          ) : null}

          <form
            key={formInstanceKey}
            ref={formRef}
            className="space-y-3 rounded-[24px] bg-[#f4f5f8] p-4"
            onSubmit={(event) => {
              event.preventDefault();
              void handleSave();
            }}
          >
            {config.fields.map((field) => {
              if (readOnly) {
                const value = initialValues[field.key];
                if (!value) return null;
                return (
                  <div key={field.key}>
                    <FieldLabel>{field.label}</FieldLabel>
                    <p className="mt-1 text-sm text-[#1f2430]">{value}</p>
                  </div>
                );
              }

              if (field.kind === 'checkbox') {
                return (
                  <label key={field.key} className="flex items-center gap-2 text-sm">
                    <input
                      type="checkbox"
                      name={field.key}
                      defaultChecked={initialValues[field.key] === 'true'}
                      onChange={(event) => syncField(field.key, event.target.checked ? 'true' : '')}
                    />
                    {field.label}
                  </label>
                );
              }

              if (field.kind === 'gender') {
                return (
                  <div key={field.key} className="space-y-2">
                    <FieldLabel>{field.label}</FieldLabel>
                    <input type="hidden" name={field.key} value={pickerValues[field.key] ?? ''} readOnly />
                    <GenderToggle value={pickerValues[field.key] ?? ''} onChange={(value) => setPickerField(field.key, value)} />
                  </div>
                );
              }

              if (field.kind === 'categories') {
                return (
                  <div key={field.key} className="space-y-2">
                    <FieldLabel>{field.label}</FieldLabel>
                    <input type="hidden" name={field.key} value={pickerValues[field.key] ?? ''} readOnly />
                    <CategoryPicker value={pickerValues[field.key] ?? ''} onChange={(value) => setPickerField(field.key, value)} />
                  </div>
                );
              }

              if (field.kind === 'textarea') {
                return (
                  <div key={field.key} className="space-y-2">
                    <FieldLabel>{field.label}</FieldLabel>
                    <textarea
                      name={field.key}
                      defaultValue={initialValues[field.key] ?? ''}
                      onChange={(event) => syncField(field.key, event.target.value)}
                      onBlur={(event) => syncField(field.key, event.target.value)}
                      onCompositionEnd={(event) => syncField(field.key, event.currentTarget.value)}
                      placeholder={field.placeholder ?? field.label}
                      className="min-h-[96px] w-full rounded-2xl border border-[#eceef4] bg-white px-4 py-3 text-sm outline-none focus:border-[#20212b]"
                    />
                  </div>
                );
              }

              return (
                <div key={field.key} className="space-y-2">
                  <FieldLabel>{field.label}</FieldLabel>
                  <Input
                    type={field.kind === 'date' ? 'date' : 'text'}
                    name={field.key}
                    defaultValue={initialValues[field.key] ?? ''}
                    onChange={(event) => syncField(field.key, event.target.value)}
                    onBlur={(event) => syncField(field.key, event.target.value)}
                    onCompositionEnd={(event) => syncField(field.key, event.currentTarget.value)}
                    placeholder={field.placeholder ?? field.label}
                    readOnly={readOnly}
                  />
                  {field.latinKey ? (
                    <div className="space-y-2">
                      <FieldLabel>{field.latinLabel ?? field.latinKey}</FieldLabel>
                      <Input
                        name={field.latinKey}
                        defaultValue={initialValues[field.latinKey] ?? ''}
                        onChange={(event) => syncField(field.latinKey!, event.target.value)}
                        onBlur={(event) => syncField(field.latinKey!, event.target.value)}
                        onCompositionEnd={(event) => syncField(field.latinKey!, event.currentTarget.value)}
                        placeholder={field.latinLabel ?? field.latinKey}
                        readOnly={readOnly}
                      />
                    </div>
                  ) : null}
                </div>
              );
            })}
          </form>

          {!readOnly ? (
            <Button
              type="button"
              className="w-full rounded-[18px] py-6"
              onMouseDown={(event) => event.preventDefault()}
              onClick={() => void handleSave()}
              disabled={isSaving}
            >
              {isSaving ? 'Сохраняем...' : 'Сохранить'}
            </Button>
          ) : null}
        </div>
      </DialogContent>
    </Dialog>
  );
}
