import ChannelStore from '@app/stores/ChannelStore';
import ServerStore from '@app/stores/ServerStore';
import { MatrixClientManager, SpaceManager } from '@brycord/matrix-client';
import { PencilSimple, Trash, UploadSimple } from '@phosphor-icons/react';
/**
 * OverviewTab — Server Settings → Overview. Lets an admin rename
 * the space and change its avatar (icon). Both writes go through
 * `SpaceManager`, which sends the canonical Matrix `m.room.name`
 * and `m.room.avatar` state events on the space room — so the
 * change is interoperable with Element / Cinny / any other Matrix
 * client viewing the same space.
 *
 * Renders the same form on desktop and mobile; the `mobile` prop
 * just swaps the chrome to the rounded-card style the rest of the
 * mobile settings tabs use. Logic is identical across both.
 *
 * Permission gate uses `SpaceManager.canManageSpaceProfile`, which
 * checks the local user's power level against the room's
 * `state_default` (and any per-event overrides) for both
 * `m.room.name` and `m.room.avatar`.
 */
import { observer } from 'mobx-react-lite';
import { useEffect, useMemo, useRef, useState } from 'react';
import styles from './OverviewTab.module.css';

const AFK_TIMEOUT_OPTIONS: { label: string; value: number }[] = [
	{ label: '1 minute', value: 60 * 1000 },
	{ label: '5 minutes', value: 5 * 60 * 1000 },
	{ label: '15 minutes', value: 15 * 60 * 1000 },
	{ label: '30 minutes', value: 30 * 60 * 1000 },
	{ label: '1 hour', value: 60 * 60 * 1000 },
];

interface OverviewTabProps {
	serverId: string;
	mobile?: boolean;
}

function getInitials(name: string): string {
	return name
		.split(/\s+/)
		.map((w) => w[0])
		.join('')
		.slice(0, 2)
		.toUpperCase();
}

export const OverviewTab = observer(function OverviewTab({ serverId, mobile = false }: OverviewTabProps) {
	const server = ServerStore.getServer(serverId);

	const canManage = useMemo(() => {
		try {
			return SpaceManager.getInstance().canManageSpaceProfile(serverId);
		} catch {
			return false;
		}
	}, [serverId]);

	// Form state — seeded from the live server snapshot. We track a
	// "pending" file separately from the committed avatar so the user
	// can preview their pick before tapping Save.
	const [name, setName] = useState(server?.name ?? '');
	const [pendingIconFile, setPendingIconFile] = useState<File | null>(null);
	const [pendingPreviewUrl, setPendingPreviewUrl] = useState<string | null>(null);
	// `removeIcon` is a flag set when the user clicks "Remove Icon" so
	// we can send an empty `m.room.avatar` content on save without
	// also wiping a freshly-picked file.
	const [removeIcon, setRemoveIcon] = useState(false);
	// AFK settings. `null` means "no AFK channel configured".
	// `initialAfk*` tracks the committed values so we can diff on save.
	const [afkChannelId, setAfkChannelId] = useState<string | null>(null);
	const [afkTimeoutMs, setAfkTimeoutMs] = useState<number>(5 * 60 * 1000);
	const [initialAfkChannelId, setInitialAfkChannelId] = useState<string | null>(null);
	const [initialAfkTimeoutMs, setInitialAfkTimeoutMs] = useState<number>(5 * 60 * 1000);
	const [saving, setSaving] = useState(false);
	const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
	const fileInputRef = useRef<HTMLInputElement>(null);

	// Reset state whenever the server name in the store changes
	// underneath us (e.g. another client renamed it).
	useEffect(() => {
		setName(server?.name ?? '');
	}, [server?.name]);

	// Load the currently-configured AFK settings when the server
	// switches. Reads synchronously from the space's in-memory
	// state event — no network round-trip.
	useEffect(() => {
		const current = SpaceManager.getInstance().getAfkSettings(serverId);
		const channelId = current?.channelId ?? null;
		const timeoutMs = current?.timeoutMs ?? 5 * 60 * 1000;
		setAfkChannelId(channelId);
		setAfkTimeoutMs(timeoutMs);
		setInitialAfkChannelId(channelId);
		setInitialAfkTimeoutMs(timeoutMs);
	}, [serverId]);

	// Cleanup the temporary preview URL when the component unmounts
	// or the user picks a different file.
	useEffect(() => {
		return () => {
			if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl);
		};
	}, [pendingPreviewUrl]);

	if (!server) {
		return (
			<div className={styles.gate}>
				<h2 className={styles.gateTitle}>Server not found</h2>
				<p className={styles.gateBody}>This server is no longer available.</p>
			</div>
		);
	}

	if (!canManage) {
		return (
			<div className={styles.gate}>
				<h2 className={styles.gateTitle}>Overview</h2>
				<p className={styles.gateBody}>
					You need permission to manage this server. Ask an admin to grant you a role with a higher power level.
				</p>
			</div>
		);
	}

	const trimmedName = name.trim();
	const nameDirty = trimmedName !== (server.name ?? '');
	const iconDirty = pendingIconFile !== null || removeIcon;
	const afkDirty = afkChannelId !== initialAfkChannelId || afkTimeoutMs !== initialAfkTimeoutMs;
	const isDirty = nameDirty || iconDirty || afkDirty;
	const nameValid = trimmedName.length > 0 && trimmedName.length <= 100;
	const canSave = isDirty && nameValid && !saving;

	// List of voice channels in this server for the AFK dropdown.
	// Filtered live from ChannelStore so newly-created voice channels
	// appear without needing a remount.
	const voiceChannels = ChannelStore.getVoiceChannels(serverId);

	const handlePickFile = () => {
		fileInputRef.current?.click();
	};

	const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
		const file = e.target.files?.[0];
		if (fileInputRef.current) fileInputRef.current.value = '';
		if (!file) return;
		setStatus(null);

		if (!file.type.startsWith('image/')) {
			setStatus({
				type: 'error',
				message: 'Server icon must be an image file.',
			});
			return;
		}
		if (file.size > 10 * 1024 * 1024) {
			setStatus({
				type: 'error',
				message: 'Server icon must be smaller than 10 MB.',
			});
			return;
		}

		if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl);
		setPendingIconFile(file);
		setPendingPreviewUrl(URL.createObjectURL(file));
		// Picking a new file overrides any pending "remove" flag.
		setRemoveIcon(false);
	};

	const handleRemoveIcon = () => {
		if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl);
		setPendingIconFile(null);
		setPendingPreviewUrl(null);
		setRemoveIcon(true);
		setStatus(null);
	};

	const handleSave = async () => {
		if (!canSave) return;
		setSaving(true);
		setStatus(null);
		try {
			const sm = SpaceManager.getInstance();
			// Avatar mutation first so the optimistic store update at
			// the end has both fields in their final state.
			let newIconHttp: string | undefined = server.icon;
			if (pendingIconFile) {
				const mxc = await sm.setSpaceAvatar(serverId, pendingIconFile);
				if (mxc) {
					// Match the same dimensioned thumbnail SpaceManager
					// produces so the local server entry stays in sync
					// with what a fresh sync would compute.
					const client = MatrixClientManager.getInstance().getClient();
					newIconHttp = client.mxcUrlToHttp(mxc, 128, 128, 'crop') ?? undefined;
				}
			} else if (removeIcon) {
				await sm.setSpaceAvatar(serverId, null);
				newIconHttp = undefined;
			}

			if (nameDirty) {
				await sm.renameSpace(serverId, trimmedName);
			}

			if (afkDirty) {
				await sm.setAfkSettings(serverId, afkChannelId, afkTimeoutMs);
				setInitialAfkChannelId(afkChannelId);
				setInitialAfkTimeoutMs(afkTimeoutMs);
			}

			// Optimistic local update so the sidebar / hero refresh
			// instantly instead of waiting for the next sync tick.
			ServerStore.handleServerUpdate({
				...server,
				name: nameDirty ? trimmedName : server.name,
				icon: newIconHttp,
			});

			if (pendingPreviewUrl) URL.revokeObjectURL(pendingPreviewUrl);
			setPendingIconFile(null);
			setPendingPreviewUrl(null);
			setRemoveIcon(false);
			setStatus({ type: 'success', message: 'Saved.' });
		} catch (err: any) {
			setStatus({
				type: 'error',
				message: err?.message || 'Failed to save changes.',
			});
		} finally {
			setSaving(false);
		}
	};

	// Decide which preview src to render: pending file > current
	// server icon > nothing (initials fallback). When the user has
	// queued an icon removal, force the initials path.
	const showInitials = removeIcon || (!pendingPreviewUrl && !server.icon);
	const previewSrc = pendingPreviewUrl ?? server.icon;

	const rootClass = mobile ? `${styles.root} ${styles.mobileRoot}` : styles.root;
	const iconBlockClass = mobile ? `${styles.iconBlock} ${styles.mobileIconBlock}` : styles.iconBlock;
	const fieldLabelClass = mobile ? `${styles.fieldLabel} ${styles.mobileFieldLabel}` : styles.fieldLabel;
	const saveButtonClass = mobile ? `${styles.saveButton} ${styles.mobileSaveButton}` : styles.saveButton;
	const actionsClass = mobile ? `${styles.actions} ${styles.mobileActions}` : styles.actions;

	return (
		<div className={rootClass}>
			{!mobile && <h2 className={styles.heading}>Overview</h2>}

			<div className={iconBlockClass}>
				<div className={styles.iconPreviewWrap}>
					{showInitials ? (
						<div className={styles.iconInitials}>{getInitials(server.name)}</div>
					) : (
						<img src={previewSrc} alt={server.name} className={styles.iconPreview} draggable={false} />
					)}
					<div className={styles.iconBadge}>
						<PencilSimple size={12} weight="bold" />
					</div>
				</div>
				<div className={styles.iconActions}>
					<p className={styles.iconActionsHelp}>
						We recommend an image of at least 512×512 — JPEG, PNG, GIF, or WebP, up to 10 MB.
					</p>
					<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
						<button type="button" className={styles.iconButton} onClick={handlePickFile} disabled={saving}>
							<UploadSimple size={14} weight="bold" />
							{server.icon || pendingPreviewUrl ? 'Change Icon' : 'Upload Icon'}
						</button>
						{(server.icon || pendingPreviewUrl) && (
							<button
								type="button"
								className={`${styles.iconButton} ${styles.iconButtonDanger}`}
								onClick={handleRemoveIcon}
								disabled={saving}
							>
								<Trash size={14} weight="bold" />
								Remove
							</button>
						)}
					</div>
				</div>
			</div>
			<input
				ref={fileInputRef}
				type="file"
				accept="image/png,image/jpeg,image/gif,image/webp"
				onChange={handleFileChange}
				style={{ display: 'none' }}
			/>

			<div>
				<div className={fieldLabelClass}>Server Name</div>
				{mobile ? (
					<div className={styles.mobileNameCard}>
						<input
							type="text"
							className={styles.nameInput}
							value={name}
							onChange={(e) => setName(e.target.value)}
							maxLength={100}
							placeholder="My Server"
						/>
					</div>
				) : (
					<input
						type="text"
						className={styles.nameInput}
						value={name}
						onChange={(e) => setName(e.target.value)}
						maxLength={100}
						placeholder="My Server"
					/>
				)}
			</div>

			<div className={styles.idleSection}>
				<div className={styles.idleHeading}>Idle Settings</div>
				<p className={styles.idleDescription}>
					Move members to an AFK voice channel when they go idle. They'll be automatically muted on move.
				</p>
				<div className={styles.idleRow}>
					<div className={styles.idleField}>
						<div className={fieldLabelClass}>AFK / Idle Channel</div>
						<select
							className={styles.idleSelect}
							value={afkChannelId ?? ''}
							onChange={(e) => setAfkChannelId(e.target.value || null)}
						>
							<option value="">No AFK Channel</option>
							{voiceChannels.map((ch) => (
								<option key={ch.id} value={ch.id}>
									{ch.name}
								</option>
							))}
						</select>
					</div>
					<div className={styles.idleField}>
						<div className={fieldLabelClass}>AFK Timeout</div>
						<select
							className={styles.idleSelect}
							value={afkTimeoutMs}
							onChange={(e) => setAfkTimeoutMs(Number(e.target.value))}
						>
							{AFK_TIMEOUT_OPTIONS.map((opt) => (
								<option key={opt.value} value={opt.value}>
									{opt.label}
								</option>
							))}
						</select>
					</div>
				</div>
			</div>

			{status && (
				<div className={`${styles.status} ${status.type === 'error' ? styles.statusError : styles.statusSuccess}`}>
					{status.message}
				</div>
			)}

			<div className={actionsClass}>
				<button type="button" className={saveButtonClass} onClick={handleSave} disabled={!canSave}>
					{saving ? 'Saving…' : 'Save Changes'}
				</button>
			</div>
		</div>
	);
});
