/**
 * SaveMediaModal — "Add to Saved Media" dialog opened from the star
 * button in ImageLightbox. Collects a user-editable name, alt text,
 * and up to 10 tags, then writes the item through SavedMediaManager
 * into `io.brycord.saved_media` account data.
 *
 * Name / alt text / tag limits match Fluxer's equivalent modal. On
 * successful save we also push the fresh snapshot into SavedMediaStore
 * optimistically so the EmojiPicker Media tab and the ImageLightbox
 * star both reflect the new state instantly — the accountData
 * listener in MatrixActions will fire on the next sync tick as an
 * idempotent no-op.
 *
 * Save failures (homeserver rejection, network blip, account-data
 * size limit) surface inline under the body via the `.error` row
 * and keep the modal open so the user can retry.
 */
import { useEffect, useState } from 'react';
import { Modal } from '@brycord/ui';
import { X } from '@phosphor-icons/react';
import {
	SavedMediaManager,
	classifyMediaKind,
} from '@brycord/matrix-client';
import type { SavedMediaItem } from '@brycord/matrix-client';
import SavedMediaStore from '@app/stores/SavedMediaStore';
import styles from './SaveMediaModal.module.css';

interface SaveMediaModalProps {
	isOpen: boolean;
	onClose: () => void;
	/** Raw mxc:// URL of the asset being saved. Dedupe key. */
	mxcUrl: string;
	/** Filename the media was originally uploaded with. Used as the
	 *  default value of the Name field and preserved on the item. */
	originalFilename: string;
	/** MIME type captured from the Attachment — drives `kind`. */
	contentType?: string;
	width?: number;
	height?: number;
	/** Fired after a successful save so callers can flip their
	 *  local "is saved" state (e.g. the ImageLightbox star). */
	onSaved?: (item: SavedMediaItem) => void;
}

const NAME_MAX = 120;
const ALT_MAX = 320;
const TAGS_MAX = 10;
const TAG_MAX_LENGTH = 32;

export function SaveMediaModal({
	isOpen,
	onClose,
	mxcUrl,
	originalFilename,
	contentType,
	width,
	height,
	onSaved,
}: SaveMediaModalProps) {
	const [name, setName] = useState(originalFilename);
	const [altText, setAltText] = useState('');
	const [tags, setTags] = useState<string[]>([]);
	const [tagDraft, setTagDraft] = useState('');
	const [error, setError] = useState<string | null>(null);
	const [saving, setSaving] = useState(false);

	// Reset every time the modal opens on a new asset. Without this
	// a stale tag draft from a previous save would bleed into the
	// next favorite action.
	useEffect(() => {
		if (!isOpen) return;
		const existing = SavedMediaStore.getByMxc(mxcUrl);
		setName(existing?.name ?? originalFilename);
		setAltText(existing?.altText ?? '');
		setTags(existing?.tags ?? []);
		setTagDraft('');
		setError(null);
		setSaving(false);
	}, [isOpen, mxcUrl, originalFilename]);

	const canSave = name.trim().length > 0 && !saving;

	const addTag = () => {
		const raw = tagDraft.trim();
		if (!raw) return;
		if (tags.length >= TAGS_MAX) return;
		const tag = raw.slice(0, TAG_MAX_LENGTH);
		// Drop duplicates (case-insensitive) so re-entering the
		// same tag doesn't create visual dupes.
		if (tags.some((t) => t.toLowerCase() === tag.toLowerCase())) {
			setTagDraft('');
			return;
		}
		setTags([...tags, tag]);
		setTagDraft('');
	};

	const removeTag = (tag: string) => {
		setTags(tags.filter((t) => t !== tag));
	};

	const handleTagKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
		if (e.key === 'Enter') {
			e.preventDefault();
			addTag();
		}
	};

	const handleSave = async () => {
		if (!canSave) return;
		setSaving(true);
		setError(null);
		try {
			const item = await SavedMediaManager.getInstance().saveItem({
				mxcUrl,
				kind: classifyMediaKind(contentType),
				name: name.trim().slice(0, NAME_MAX),
				altText: altText.trim() ? altText.trim().slice(0, ALT_MAX) : undefined,
				tags,
				originalFilename,
				contentType,
				width,
				height,
			});
			// Optimistic store push — the accountData listener will
			// also fire on the next sync tick with the same content,
			// which is an idempotent no-op.
			SavedMediaStore.handleDataLoaded(
				SavedMediaManager.getInstance().getData(),
			);
			onSaved?.(item);
			onClose();
		} catch (err: any) {
			setError(err?.message || 'Failed to save media.');
			setSaving(false);
		}
	};

	return (
		<Modal.Root isOpen={isOpen} onClose={onClose} size="small" zIndex={17000}>
			<Modal.Header title="Add to Saved Media" onClose={onClose} />
			<Modal.Content>
				<div className={styles.body}>
					<div className={styles.field}>
						<label className={styles.label} htmlFor="save-media-name">
							Name
						</label>
						<input
							id="save-media-name"
							type="text"
							className={styles.input}
							value={name}
							onChange={(e) => setName(e.target.value.slice(0, NAME_MAX))}
							maxLength={NAME_MAX}
							autoFocus
						/>
					</div>

					<div className={styles.field}>
						<label className={styles.label} htmlFor="save-media-alt">
							Alt Text
						</label>
						<textarea
							id="save-media-alt"
							className={styles.textarea}
							placeholder="Describe the media"
							value={altText}
							onChange={(e) => setAltText(e.target.value.slice(0, ALT_MAX))}
							maxLength={ALT_MAX}
							rows={3}
						/>
					</div>

					<div className={styles.field}>
						<label className={styles.label} htmlFor="save-media-tag">
							Tags ({tags.length}/{TAGS_MAX})
						</label>
						{tags.length > 0 && (
							<div className={styles.tagChips}>
								{tags.map((tag) => (
									<span key={tag} className={styles.tagChip}>
										<span className={styles.tagChipText}>{tag}</span>
										<button
											type="button"
											className={styles.tagChipRemove}
											onClick={() => removeTag(tag)}
											aria-label={`Remove tag ${tag}`}
										>
											<X size={12} weight="bold" />
										</button>
									</span>
								))}
							</div>
						)}
						<div className={styles.tagRow}>
							<input
								id="save-media-tag"
								type="text"
								className={styles.input}
								placeholder="Add a tag"
								value={tagDraft}
								onChange={(e) => setTagDraft(e.target.value.slice(0, TAG_MAX_LENGTH))}
								onKeyDown={handleTagKeyDown}
								maxLength={TAG_MAX_LENGTH}
								disabled={tags.length >= TAGS_MAX}
							/>
							<button
								type="button"
								className={styles.addButton}
								onClick={addTag}
								disabled={
									tagDraft.trim().length === 0 || tags.length >= TAGS_MAX
								}
							>
								Add
							</button>
						</div>
					</div>

					{error && <div className={styles.error}>{error}</div>}

					<div className={styles.actions}>
						<button
							type="button"
							className={styles.secondaryButton}
							onClick={onClose}
							disabled={saving}
						>
							Cancel
						</button>
						<button
							type="button"
							className={styles.primaryButton}
							onClick={handleSave}
							disabled={!canSave}
						>
							{saving ? 'Saving…' : 'Save'}
						</button>
					</div>
				</div>
			</Modal.Content>
		</Modal.Root>
	);
}
