'use client';

import { useEffect, useRef, useState } from 'react';
import { useEscapeToClose } from '@/lib/hooks/use-escape-to-close';
import { api } from '@/lib/api';
import { useCreateReview, useUpdateReview } from '@/lib/queries/use-reviews';
import { StarPicker } from './star-picker';
import type {
  Review,
  ReviewAttachmentUploadResponse,
  EditReviewInput,
  NewReviewInput,
} from '@alqove/api-client';

export type DimensionKey =
  | 'rating_item_as_described'
  | 'rating_shipping_speed'
  | 'rating_communication'
  | 'rating_packaging';

interface OrderItemSummary {
  id: string;
  title_snapshot: string;
  image_url_snapshot?: string | null;
}

interface Props {
  orderItem: OrderItemSummary;
  storeName: string;
  mode: 'create' | 'edit';
  initial?: Review;
  onClose: () => void;
  onSubmitted?: (review: Review) => void;
}

const DIMENSIONS: { key: DimensionKey; label: string }[] = [
  { key: 'rating_item_as_described', label: 'Item as described' },
  { key: 'rating_shipping_speed', label: 'Shipping speed' },
  { key: 'rating_communication', label: 'Communication' },
  { key: 'rating_packaging', label: 'Packaging' },
];

const BODY_MIN = 20;
const BODY_MAX = 2000;
const TITLE_MAX = 120;
const PHOTO_MAX = 4;

export function ReviewModal({
  orderItem,
  storeName,
  mode,
  initial,
  onClose,
  onSubmitted,
}: Props) {
  const isEdit = mode === 'edit';
  const [overall, setOverall] = useState<number | null>(initial?.rating ?? null);
  const [dims, setDims] = useState<Record<DimensionKey, number | null>>({
    rating_item_as_described: initial?.rating_item_as_described ?? null,
    rating_shipping_speed: initial?.rating_shipping_speed ?? null,
    rating_communication: initial?.rating_communication ?? null,
    rating_packaging: initial?.rating_packaging ?? null,
  });
  // In edit mode, treat all dims as already touched so changes to overall don't overwrite them.
  const touchedRef = useRef<Set<DimensionKey>>(
    new Set(isEdit ? DIMENSIONS.map((d) => d.key) : []),
  );

  const [title, setTitle] = useState<string>(initial?.title ?? '');
  const [body, setBody] = useState<string>(initial?.body ?? '');
  const [photos, setPhotos] = useState<ReviewAttachmentUploadResponse[]>([]);
  const [uploadError, setUploadError] = useState<string | null>(null);
  const [isUploading, setIsUploading] = useState(false);
  const [submitError, setSubmitError] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  useEscapeToClose(true, onClose);

  const createMutation = useCreateReview(orderItem.id);
  const updateMutation = useUpdateReview();

  // Lock background scroll while open.
  useEffect(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prev;
    };
  }, []);

  const handleOverallChange = (n: number) => {
    setOverall(n);
    setDims((prev) => {
      const next = { ...prev };
      for (const d of DIMENSIONS) {
        if (!touchedRef.current.has(d.key)) {
          next[d.key] = n;
        }
      }
      return next;
    });
  };

  const handleDimChange = (key: DimensionKey, n: number) => {
    touchedRef.current.add(key);
    setDims((prev) => ({ ...prev, [key]: n }));
  };

  const handleFiles = async (fileList: FileList | null) => {
    if (!fileList) return;
    const files = Array.from(fileList);
    if (photos.length + files.length > PHOTO_MAX) {
      setUploadError(`Limit ${PHOTO_MAX} photos per review.`);
      return;
    }
    setUploadError(null);
    setIsUploading(true);
    const uploaded: ReviewAttachmentUploadResponse[] = [];
    for (const file of files) {
      try {
        const resp = await api.reviews.uploadAttachment(orderItem.id, file);
        uploaded.push(resp);
      } catch {
        setUploadError('Upload failed. Try a smaller image (≤ 5 MB) in JPEG, PNG, or HEIC.');
      }
    }
    setPhotos((prev) => [...prev, ...uploaded]);
    setIsUploading(false);
    if (fileInputRef.current) fileInputRef.current.value = '';
  };

  const removePhoto = (id: string) => {
    setPhotos((prev) => prev.filter((p) => p.id !== id));
  };

  const bodyTrimmedLen = body.trim().length;
  const bodyOk = bodyTrimmedLen >= BODY_MIN && bodyTrimmedLen <= BODY_MAX;
  const overallOk = overall !== null && overall >= 1 && overall <= 5;
  const isPending = createMutation.isPending || updateMutation.isPending;
  const canSubmit = overallOk && bodyOk && !isPending && !isUploading;

  const handleSubmit = async () => {
    if (!overall) return;
    setSubmitError(null);
    try {
      if (isEdit && initial) {
        const input: EditReviewInput = {
          rating: overall,
          rating_item_as_described: dims.rating_item_as_described ?? overall,
          rating_shipping_speed: dims.rating_shipping_speed ?? overall,
          rating_communication: dims.rating_communication ?? overall,
          rating_packaging: dims.rating_packaging ?? overall,
          title: title.trim() ? title.trim() : undefined,
          body: body.trim(),
          attachment_ids: photos.length > 0 ? photos.map((p) => p.id) : undefined,
        };
        const resp = await updateMutation.mutateAsync({
          reviewId: initial.id,
          input,
        });
        onSubmitted?.(resp.data);
      } else {
        const input: NewReviewInput = {
          rating: overall,
          rating_item_as_described: dims.rating_item_as_described ?? overall,
          rating_shipping_speed: dims.rating_shipping_speed ?? overall,
          rating_communication: dims.rating_communication ?? overall,
          rating_packaging: dims.rating_packaging ?? overall,
          title: title.trim() ? title.trim() : undefined,
          body: body.trim(),
          attachment_ids: photos.length > 0 ? photos.map((p) => p.id) : undefined,
        };
        const resp = await createMutation.mutateAsync(input);
        onSubmitted?.(resp.data);
      }
      onClose();
    } catch (e) {
      setSubmitError((e as Error).message ?? 'Failed to submit review.');
    }
  };

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label={isEdit ? 'Edit your review' : 'Leave a review'}
      className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-slate-900/50 p-4"
    >
      <div className="my-8 w-full max-w-lg rounded-lg bg-white p-6 shadow-xl">
        <div className="flex items-start justify-between">
          <h2 className="text-lg font-semibold text-slate-900">
            {isEdit
              ? `Edit your review of ${storeName}`
              : `How was your experience with ${storeName}?`}
          </h2>
          <button
            type="button"
            onClick={onClose}
            className="text-slate-400 hover:text-slate-600"
          >
            <span aria-hidden>×</span>
            <span className="sr-only">Close</span>
          </button>
        </div>

        <div className="mt-2 text-sm text-slate-500">{orderItem.title_snapshot}</div>

        <div className="mt-4 space-y-5">
          <div>
            <label className="block text-sm font-medium text-slate-700">
              Overall rating
            </label>
            <div className="mt-1">
              <StarPicker
                size="lg"
                value={overall}
                onChange={handleOverallChange}
                disabled={isPending}
              />
            </div>
          </div>

          <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
            {DIMENSIONS.map((d) => (
              <div key={d.key} className="flex items-center justify-between gap-3">
                <label className="text-sm text-slate-700">{d.label}</label>
                <StarPicker
                  size="sm"
                  value={dims[d.key]}
                  onChange={(n) => handleDimChange(d.key, n)}
                  disabled={isPending}
                />
              </div>
            ))}
          </div>

          <div>
            <label htmlFor="review_title" className="block text-sm font-medium text-slate-700">
              Title <span className="text-slate-400">(optional)</span>
            </label>
            <input
              id="review_title"
              type="text"
              maxLength={TITLE_MAX}
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              className="mt-1 block w-full rounded-md border border-slate-300 px-2 py-1.5 text-sm"
              disabled={isPending}
            />
          </div>

          <div>
            <label htmlFor="review_body" className="block text-sm font-medium text-slate-700">
              Review <span className="text-red-500">*</span>
            </label>
            <textarea
              id="review_body"
              rows={5}
              maxLength={BODY_MAX}
              value={body}
              onChange={(e) => setBody(e.target.value)}
              className="mt-1 block w-full rounded-md border border-slate-300 px-2 py-1.5 text-sm"
              disabled={isPending}
              required
            />
            <div className="mt-1 flex justify-between text-xs text-slate-500">
              <span
                className={
                  bodyTrimmedLen < BODY_MIN ? 'text-amber-700' : 'text-forest-700'
                }
                data-testid="review-body-min-indicator"
              >
                {bodyTrimmedLen < BODY_MIN
                  ? `${BODY_MIN - bodyTrimmedLen} more character${
                      BODY_MIN - bodyTrimmedLen === 1 ? '' : 's'
                    } needed`
                  : 'Minimum length met'}
              </span>
              <span>{body.length}/{BODY_MAX}</span>
            </div>
          </div>

          <div>
            <span className="block text-sm font-medium text-slate-700">
              Photos <span className="text-slate-400">(optional, up to {PHOTO_MAX})</span>
            </span>
            {isEdit && initial && initial.photos.length > 0 && (
              <div
                className="mt-2 flex flex-wrap items-center gap-2"
                data-testid="review-existing-photos"
              >
                {initial.photos.map((p, i) => (
                  <div
                    key={`existing-${i}`}
                    className="h-16 w-16 overflow-hidden rounded border border-slate-200"
                  >
                    <img
                      src={p.thumb_url ?? p.url}
                      alt=""
                      className="h-full w-full object-cover"
                    />
                  </div>
                ))}
              </div>
            )}
            <div className="mt-2 flex flex-wrap items-center gap-2">
              {photos.map((p) => (
                <div
                  key={p.id}
                  className="relative h-16 w-16 overflow-hidden rounded border border-slate-200"
                  data-testid={`review-photo-${p.id}`}
                >
                  {p.thumb_url || p.url ? (
                    <img
                      src={p.thumb_url ?? p.url ?? ''}
                      alt=""
                      className="h-full w-full object-cover"
                    />
                  ) : (
                    <div className="h-full w-full bg-slate-100" />
                  )}
                  <button
                    type="button"
                    onClick={() => removePhoto(p.id)}
                    aria-label={`Remove photo ${p.id}`}
                    className="absolute right-0 top-0 m-0.5 rounded-full bg-black/60 px-1 text-xs text-white hover:bg-black/80"
                  >
                    ✕
                  </button>
                </div>
              ))}
              {photos.length < PHOTO_MAX && (
                <label className="inline-block cursor-pointer rounded border border-dashed border-slate-300 px-3 py-2 text-xs text-forest-700 hover:bg-slate-50">
                  {isUploading ? 'Uploading…' : 'Add photo'}
                  <input
                    ref={fileInputRef}
                    type="file"
                    accept="image/jpeg,image/png,image/heic"
                    multiple
                    aria-label="Add review photos"
                    className="hidden"
                    onChange={(e) => handleFiles(e.target.files)}
                    disabled={isUploading || isPending}
                  />
                </label>
              )}
            </div>
            {uploadError && (
              <p className="mt-1 text-xs text-red-600">{uploadError}</p>
            )}
          </div>

          {submitError && (
            <div className="text-sm text-red-600">{submitError}</div>
          )}
        </div>

        <div className="mt-6 flex items-center justify-end gap-2">
          <button
            type="button"
            onClick={onClose}
            className="rounded-md border border-slate-300 px-4 py-2 text-sm text-slate-700 hover:bg-slate-50"
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={handleSubmit}
            disabled={!canSubmit}
            className="rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-60"
          >
            {isPending ? 'Saving…' : isEdit ? 'Save changes' : 'Submit review'}
          </button>
        </div>
      </div>
    </div>
  );
}
