import React, { useCallback, useState } from 'react';

import {
  MEDIA_MAX_REGEN_PER_SUGGESTION,
  RESOURCE_TYPE_ARTICLE_SUGGESTION,
} from '../../service/media/media.interface';
import type {
  MediaGenerateRequest,
  MediaResponse,
} from '../../service/media/media.interface';
import {
  deleteMedia,
  downloadMedia,
  generateMedia,
  regenerateMedia,
} from '../../service/media/media.service';

const EXTRA_PROMPT_MAX: number = 500;

interface ArticleImageSuggestion {
  placement?: string;
  caption?: string;
  alt?: string;
}

interface ImageSuggestionCardProps {
  articleId: string;
  suggestionIndex: number;
  suggestion: ArticleImageSuggestion;
  media: MediaResponse | null;
  clientId: string;
  token: string;
  onMediaChanged: (media: MediaResponse) => void;
  onMediaDeleted: (suggestionId: string) => void;
  onError?: (message: string) => void;
}

/**
 * @param {ImageSuggestionCardProps} props
 * @return {JSX.Element}
 */
function ImageSuggestionCard({
  articleId,
  suggestionIndex,
  suggestion,
  media,
  clientId,
  token,
  onMediaChanged,
  onMediaDeleted,
  onError,
}: ImageSuggestionCardProps): JSX.Element {
  const [busy, setBusy] = useState<boolean>(false);
  const [downloading, setDownloading] = useState<boolean>(false);
  const [deleting, setDeleting] = useState<boolean>(false);
  const [extraPrompt, setExtraPrompt] = useState<string>('');
  const [includeText, setIncludeText] = useState<boolean>(false);

  const hasImage: boolean = !!media?.image_url;
  const regenCount: number = media?.regen_count ?? 0;
  const regenExhausted: boolean = regenCount >= MEDIA_MAX_REGEN_PER_SUGGESTION;
  const buttonDisabled: boolean = busy || (hasImage && regenExhausted);

  const submit = useCallback(async (): Promise<void> => {
    setBusy(true);
    try {
      const payload: MediaGenerateRequest = {
        resource_type: RESOURCE_TYPE_ARTICLE_SUGGESTION,
        resource_id: articleId,
        suggestion_id: String(suggestionIndex),
        extra_prompt: extraPrompt.trim() || null,
        include_text: includeText,
      };
      const fresh: MediaResponse = hasImage
        ? await regenerateMedia(clientId, token, payload)
        : await generateMedia(clientId, token, payload);
      onMediaChanged(fresh);
      setExtraPrompt('');
    } catch (generationError) {
      onError?.(
        generationError instanceof Error
          ? generationError.message
          : 'Failed to generate image.'
      );
    } finally {
      setBusy(false);
    }
  }, [
    articleId,
    suggestionIndex,
    extraPrompt,
    includeText,
    hasImage,
    clientId,
    token,
    onMediaChanged,
    onError,
  ]);

  const download = useCallback(async (): Promise<void> => {
    if (!media?.image_url) return;
    setDownloading(true);
    try {
      const blob: Blob = await downloadMedia(
        clientId,
        token,
        RESOURCE_TYPE_ARTICLE_SUGGESTION,
        articleId,
        String(suggestionIndex)
      );
      const objectUrl: string = URL.createObjectURL(blob);
      const anchor: HTMLAnchorElement = document.createElement('a');
      anchor.href = objectUrl;
      anchor.download = `${articleId}-${suggestionIndex}.png`;
      document.body.appendChild(anchor);
      anchor.click();
      anchor.remove();
      URL.revokeObjectURL(objectUrl);
    } catch (downloadError) {
      onError?.(
        downloadError instanceof Error
          ? downloadError.message
          : "Couldn't download the image."
      );
    } finally {
      setDownloading(false);
    }
  }, [media?.image_url, articleId, suggestionIndex, clientId, token, onError]);

  const remove = useCallback(async (): Promise<void> => {
    if (!media) return;
    setDeleting(true);
    try {
      const suggestionId: string = String(suggestionIndex);
      await deleteMedia(
        clientId,
        token,
        RESOURCE_TYPE_ARTICLE_SUGGESTION,
        articleId,
        suggestionId
      );
      onMediaDeleted(suggestionId);
    } catch (deleteError) {
      onError?.(
        deleteError instanceof Error
          ? deleteError.message
          : "Couldn't delete the image."
      );
    } finally {
      setDeleting(false);
    }
  }, [media, clientId, token, articleId, suggestionIndex, onMediaDeleted]);

  const buttonLabel: string = hasImage
    ? regenExhausted
      ? 'No regenerations left'
      : `Regenerate (${MEDIA_MAX_REGEN_PER_SUGGESTION - regenCount} left)`
    : 'Generate image';

  return (
    <div className="flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-3">
      <div className="flex flex-col gap-3 md:flex-row">
        <div className="md:w-48 md:flex-none">
          {hasImage ? (
            <img
              src={media!.image_url}
              alt={suggestion.alt || 'Generated illustration'}
              className="aspect-video w-full rounded-md border border-gray-200 object-cover"
            />
          ) : (
            <div className="flex aspect-video w-full items-center justify-center rounded-md border border-dashed border-gray-200 bg-gray-50 text-gray-400">
              ✦
            </div>
          )}
        </div>
        <div className="flex-1 space-y-1">
          {suggestion.placement && (
            <span className="inline-flex w-fit items-center rounded-md bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700">
              {suggestion.placement}
            </span>
          )}
          {suggestion.caption && (
            <p className="text-sm text-gray-900">{suggestion.caption}</p>
          )}
          {suggestion.alt && (
            <p className="text-xs text-gray-500">alt: {suggestion.alt}</p>
          )}
        </div>
      </div>

      <textarea
        value={extraPrompt}
        onChange={event =>
          setExtraPrompt(event.target.value.slice(0, EXTRA_PROMPT_MAX))
        }
        placeholder="Optional - extra direction for the image (style, palette, framing)."
        rows={2}
        disabled={busy || (hasImage && regenExhausted)}
        className="w-full rounded-md border border-gray-200 px-3 py-2 text-sm text-gray-900 focus:border-[#B7007C] focus:outline-none focus:ring-1 focus:ring-[#B7007C]/30 disabled:bg-gray-50"
      />

      <label className="flex items-center gap-2 text-xs text-gray-500">
        <input
          type="checkbox"
          checked={includeText}
          onChange={event => setIncludeText(event.target.checked)}
          disabled={busy || (hasImage && regenExhausted)}
          className="h-3.5 w-3.5 accent-[#B7007C]"
        />
        Include text in image (forced to the article&apos;s language)
      </label>

      <div className="flex flex-wrap items-center justify-between gap-2">
        <span className="text-xs text-gray-500">
          {hasImage
            ? `Regenerations used: ${regenCount}/${MEDIA_MAX_REGEN_PER_SUGGESTION}`
            : 'No image yet.'}
        </span>
        <div className="flex items-center gap-2">
          {hasImage && (
            <button
              type="button"
              onClick={download}
              disabled={downloading}
              className="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-md border border-gray-200 bg-white px-3 text-xs font-medium text-gray-700 hover:border-[#B7007C]/30 hover:text-[#B7007C] disabled:opacity-60"
            >
              Download
            </button>
          )}
          {hasImage && (
            <button
              type="button"
              onClick={remove}
              disabled={deleting}
              className="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-md border border-red-200 bg-white px-3 text-xs font-medium text-red-600 hover:bg-red-50 disabled:opacity-60"
            >
              Delete
            </button>
          )}
          <button
            type="button"
            onClick={submit}
            disabled={buttonDisabled}
            className="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-md bg-[#B7007C] px-3 text-xs font-semibold text-white hover:bg-[#900063] disabled:opacity-60"
          >
            {buttonLabel}
          </button>
        </div>
      </div>
    </div>
  );
}

export default ImageSuggestionCard;
