ui/settings-components.jsx

/**
 * React/Preact components for WikiShield settings interface
 */

import { h, Component } from 'preact';

/**
 * Toggle Switch Component
 */
export class Toggle extends Component {
	render() {
		const { value, onChange, label, description, id } = this.props;

		let currentValue = value;
		return (
			<div
				id={id || ''}
				class={`settings-toggle ${value ? 'active' : ''}`}
				onClick={e => {
					currentValue = !currentValue;
					e.target.closest('.settings-toggle').classList.toggle('active', currentValue);
					onChange(currentValue);
				}}
			>
				<div class="toggle-switch">
					<div class="toggle-slider"></div>
				</div>
			</div>
		);
	}
}

export class Radio extends Component {
	render() {
		const { value, options = [], onChange, name, id } = this.props;

		return (
			<div
				id={id || ''}
				class="settings-radio-group"
			>
				{options.map((option) => (
					<div
						data-key={option.value}
						class={`settings-radio-option ${value === option.value ? 'selected' : ''}`}
						onClick={e => {
							e.target.closest('.settings-radio-group').querySelectorAll('.settings-radio-option.selected').forEach(el => el.classList.remove('selected'));
							e.target.classList.add('selected');
							onChange(option.value);
						}}
					>
						{option.label}
					</div>
				))}
			</div>
		);
	}
}

/**
 * Numeric Input Component
 */
export class NumericInput extends Component {
	constructor(props) {
		super(props);
		this.state = {
			inputValue: props.value
		};
	}

	componentDidUpdate(prevProps) {
		if (prevProps.value !== this.props.value) {
			this.setState({ inputValue: this.props.value });
		}
	}

	handleMinus = () => {
		const { min, step, onChange } = this.props;
		const currentValue = Number(this.state.inputValue);
		const newValue = Math.round(Math.max(currentValue - step, min) * 100) / 100;
		this.setState({ inputValue: newValue });
		onChange(newValue);
	}

	handlePlus = () => {
		const { max, step, onChange } = this.props;
		const currentValue = Number(this.state.inputValue);
		const newValue = Math.round(Math.min(currentValue + step, max) * 100) / 100;
		this.setState({ inputValue: newValue });
		onChange(newValue);
	}

	handleInputChange = () => {
		const { min, max, step, onChange } = this.props;
		const { inputValue } = this.state;

		if (isNaN(Number(inputValue))) {
			this.setState({ inputValue: this.props.value });
			return;
		}

		let newValue = Math.round(Math.min(Math.max(Number(inputValue), min), max) * 100) / 100;
		newValue = step >= 1 ? Math.round(newValue) : newValue;

		this.setState({ inputValue: newValue });
		onChange(newValue);
	}

	handleKeyUp = (e) => {
		if (e.key.toLowerCase() === "enter") {
			this.handleInputChange();
			e.target.blur();
		}
	}

	render() {
		const { inputValue } = this.state;

		return (
			<div class="numeric-input-container">
				<span
					class="fa fa-minus numeric-input-button"
					onClick={this.handleMinus}
				></span>
				<input
					type="name"
					class="numeric-input"
					value={inputValue}
					onInput={(e) => this.setState({ inputValue: e.target.value })}
					onBlur={this.handleInputChange}
					onKeyUp={this.handleKeyUp}
					autoComplete="off"
				/>
				<span
					class="fa fa-plus numeric-input-button"
					onClick={this.handlePlus}
				></span>
			</div>
		);
	}
}

/**
 * Volume Control Component
 */
export class VolumeControl extends Component {
	constructor(props) {
		super(props);
		this.state = {
			currentSound: props.currentSound || props.triggerKey
		};
	}

	playSound = () => {
		const { playFunction } = this.props;
		if (playFunction) playFunction();
	}

	render() {
		const { title, description, value, soundOptions, onVolumeChange, onSoundChange, sounds } = this.props;
		const { currentSound } = this.state;

		return (
			<div class="audio-volume-control">
				<div class="volume-control-header">
					<div class="volume-control-info">
						<div class="volume-control-title">{title}</div>
						<div class="volume-control-desc">{description}</div>
					</div>
					<button
						class="volume-control-preview"
						onClick={this.playSound}
						title="Preview sound"
					>
						<span class="fa fa-play"></span>
					</button>
				</div>

				<div class="volume-control-main">
					<select
						class="volume-control-sound-select"
						value={currentSound}
						onChange={(e) => {
							this.setState({ currentSound: e.target.value });
							onSoundChange(e.target.value);
						}}
					>
						{soundOptions}
					</select>

					<div class="volume-control-slider-container">
						<span class="fa fa-volume-down"></span>
						<input
							type="range"
							class="volume-control-slider"
							min="0"
							max="1"
							step="0.01"
							value={value}
							onInput={(e) => onVolumeChange(parseFloat(e.target.value))}
							autoComplete="off"
						/>
						<span class="fa fa-volume-up"></span>
						<span class="volume-control-value">{Math.round(value * 100)}%</span>
					</div>
				</div>
			</div>
		);
	}
}

/**
 * Settings Section Component
 */
export class SettingsSection extends Component {
	render() {
		const { title, description, compact, inline, children, id } = this.props;

		return (
			<div class={`settings-section ${compact ? 'compact' : ''} ${inline ? 'inline' : ''}`} id={id || ''}>
				{title && <div class="settings-section-title">{title}</div>}
				{description && <div class="settings-section-desc">{description}</div>}
				{children}
			</div>
		);
	}
}

export class SettingsSectionContent extends Component {
	render() {
		const { title, description, children, id } = this.props;

		return (
			<div class={`settings-section-content`} id={id || ''}>
				{title && <div class="settings-section-title">{title}</div>}
				{description && <div class="settings-section-desc">{description}</div>}
				{children}
			</div>
		);
	}
}

export class SettingsTogglesSection extends Component {
	render() {
		const { title, description, children } = this.props;
		return (
			<div class="settings-toggles-section">
				{title && <div class="settings-section-title">{title}</div>}
				{description && <div class="settings-section-desc">{description}</div>}
				{children}
			</div>
		);
	}
}

/**
 * Settings Compact Grid Component
 */
export class SettingsCompactGrid extends Component {
	render() {
		return (
			<div class="settings-compact-grid">
				{this.props.children}
			</div>
		);
	}
}

export class DraggableOrderList extends Component {
	constructor(props) {
		super(props);
		this.state = {
			items: [],
			draggedIndex: null,
			placeholderIndex: null
		};
		this.listRef = null;
	}

	componentDidMount() {
		this.syncItemsFromChildren();
	}

	componentDidUpdate(prevProps) {
		if (prevProps.children !== this.props.children && this.state.draggedIndex === null) {
			this.syncItemsFromChildren();
		}
	}

	syncItemsFromChildren = () => {
		const { children } = this.props;
		if (Array.isArray(children)) {
			this.setState({ items: children.map((child, i) => ({ child, key: child.key || i })) });
		}
	}

	handleDragStart = (index, e) => {
		this.setState({ draggedIndex: index, placeholderIndex: index });
		e.dataTransfer.effectAllowed = 'move';
		e.dataTransfer.setData('text/plain', index);

		// Add a slight delay to allow the drag image to be captured
		requestAnimationFrame(() => {
			this.setState(state => ({ ...state }));
		});
	}

	handleDragOver = (index, e) => {
		e.preventDefault();
		const { draggedIndex, placeholderIndex } = this.state;

		if (draggedIndex === null || index === placeholderIndex) return;

		// Reorder items in real-time
		this.setState(state => {
			const newItems = [...state.items];
			const draggedItem = newItems[state.draggedIndex];

			// Remove from old position
			newItems.splice(state.draggedIndex, 1);
			// Insert at new position
			newItems.splice(index, 0, draggedItem);

			return {
				items: newItems,
				draggedIndex: index,
				placeholderIndex: index
			};
		});
	}

	handleDragEnd = () => {
		const { onReorder } = this.props;
		const { items } = this.state;

		// Notify parent of the final order
		if (onReorder) {
			onReorder(items.map(item => item.key));
		}

		this.setState({ draggedIndex: null, placeholderIndex: null });
	}

	render() {
		const { items, draggedIndex } = this.state;
		const isDragging = draggedIndex !== null;

		return (
			<div class={`draggable-order-list ${isDragging ? 'is-dragging' : ''}`} ref={el => this.listRef = el}>
				{items.map((item, index) => {
					const isThisDragging = draggedIndex === index;

					return (
						<div
							key={item.key}
							class={`draggable-order-item-wrapper ${isThisDragging ? 'dragging' : ''}`}
							draggable
							onDragStart={(e) => this.handleDragStart(index, e)}
							onDragOver={(e) => this.handleDragOver(index, e)}
							onDragEnd={this.handleDragEnd}
						>
							{item.child}
						</div>
					);
				})}
			</div>
		);
	}
}

export class DraggableOrderItem extends Component {
	handleToggle = (e) => {
		e.stopPropagation();
		const { onToggle, enabled } = this.props;
		if (onToggle) {
			onToggle(!enabled);
		}
	}

	render() {
		const { name, enabled = true } = this.props;

		return (
			<div class={`draggable-order-item ${enabled ? '' : 'disabled'}`}>
				<span class="draggable-order-item-name">{name}</span>
				<div
					class="draggable-order-item-toggle"
					onClick={this.handleToggle}
					title={enabled ? 'Click to disable' : 'Click to enable'}
				/>
			</div>
		);
	}
}

/**
 * General Settings Panel Component
 */
export class GeneralSettings extends Component {
	render() {
		const {
			openExternally,
			maxEditCount,
			maxQueueSize,
			minOresScore,
			watchlistExpiry,
			namespaces,
			selectedNamespaces,
			onMaxEditCountChange,
			onMaxQueueSizeChange,
			onMinOresScoreChange,
			onNamespaceToggle,
		} = this.props;

		return (
			<div>
				<SettingsCompactGrid>
					<SettingsSection
						compact
						id="open-externally"
						title="Open Wikishield in new tab"
						description="When enabled, WikiShield will open in a new browser tab instead of the current one."
					>
						<Toggle
							value={openExternally}
							onChange={(value) => {
								localStorage.setItem("WikiShield:OpenExternally", value);
								this.props.onOpenExternallyChange(value);
							}}
						/>
					</SettingsSection>

					<SettingsSection
						compact
						id="maximum-edit-count"
						title="Maximum edit count"
						description="Edits from users with more than this edit count will not be shown"
					>
						<NumericInput
							value={maxEditCount}
							min={0}
							max={1000000}
							step={50}
							onChange={onMaxEditCountChange}
						/>
					</SettingsSection>

					<SettingsSection
						compact
						id="maximum-queue-size"
						title="Maximum queue size"
						description="The queue will not load additional edits after reaching this size"
					>
						<NumericInput
							value={maxQueueSize}
							min={1}
							max={500}
							step={1}
							onChange={onMaxQueueSizeChange}
						/>
					</SettingsSection>

					<SettingsSection
						compact
						id="minimum-ores-score"
						title="Minimum ORES score"
						description="The minimum ORES score required to show an edit in the recent changes queue"
					>
						<NumericInput
							value={minOresScore}
							min={0}
							max={1}
							step={0.05}
							onChange={onMinOresScoreChange}
						/>
					</SettingsSection>

					<SettingsSection
							compact
							id="watchlist-expiry"
							title="Watchlist expiry for warned users"
							description="How long to watch user talk pages after issuing warnings"
						>
							<select
								value={watchlistExpiry}
								onChange={(e) => onWatchlistExpiryChange(e.target.value)}
							>
								<option value="none">None</option>
								<option value="1 hour">1 hour</option>
								<option value="1 day">1 day</option>
								<option value="1 week">1 week</option>
								<option value="1 month">1 month</option>
								<option value="3 months">3 months</option>
								<option value="6 months">6 months</option>
								<option value="indefinite">Indefinite</option>
							</select>
					</SettingsSection>
				</SettingsCompactGrid>

				<SettingsSection
					title="Namespaces to show"
					description="Only edits from the selected namespaces will be shown in your queue."
				>
					<div class="checkbox-container">
						{Object.entries(namespaces).map(([key, namespace]) => (
							<div class="namespace-item" key={key}>
								<label class="checkbox-box">
									<input
										type="checkbox"
										checked={selectedNamespaces.includes(namespace.id)}
										onChange={(e) => {
											onNamespaceToggle(namespace.id, e.target.checked);
										}}
										autoComplete="off"
									/>
									<div class="checkmark"></div>
								</label>
								<span>{namespace.name}</span>
							</div>
						))}
					</div>
				</SettingsSection>
			</div>
		);
	}
}

export class PerformanceSettings extends Component {
	render() {
		return (
			<div>
				<SettingsSection
					title="Startup Animation"
					description="Enable or disable the startup animation when launching WikiShield"
				>
					<Radio
						id="performance-mode"
						name="performance-mode"
						value={this.props.startup}
						options={[
							{ value: 'always_off', label: 'Always Off' },
							{ value: 'adaptive', label: 'Adaptive' },
							{ value: 'always_on', label: 'Always On' }
						]}
						onChange={this.props.onStartupChange}
					/>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Audio Settings Panel Component
 */
export class AudioSettings extends Component {
	render() {
		const {
			volumes,
			soundMappings,
			sounds,
			onVolumeChange,
			onSoundChange,
			playSound
		} = this.props;

		// Build sound selector options grouped by category
		const soundsByCategory = {};
		Object.entries(sounds).forEach(([key, sound]) => {
			const category = sound.category || 'other';
			if (!soundsByCategory[category]) soundsByCategory[category] = [];
			soundsByCategory[category].push({ key, sound });
		});

		const categoryOrder = ['ui', 'alert', 'warning', 'action', 'alert', 'positive', 'negative', 'other'];
		const categoryNames = {
			ui: 'UI Sounds',
			alert: 'Alerts',
			warning: 'Warnings',
			action: 'Actions',
			alert: 'Alerts',
			positive: 'Positive',
			negative: 'Negative',
			other: 'Other'
		};

		const buildSoundOptions = () => {
			const options = [];
			options.push(<option value="none">🔇 Disabled</option>);

			categoryOrder.forEach(category => {
				if (soundsByCategory[category]) {
					const categoryItems = soundsByCategory[category].map(({ key, sound }) => (
						<option value={key} key={key}>
							{sound.icon || '🔊'} {sound.name || key}
						</option>
					));
					options.push(
						<optgroup label={categoryNames[category] || category} key={category}>
							{categoryItems}
						</optgroup>
					);
				}
			});

			return options;
		};

		const soundOptions = buildSoundOptions();

		const audioControls = [
			{ key: 'click', title: 'Button Click Sound', description: 'Played when clicking buttons' },
			{ key: 'alert', title: 'Alert Sound', description: 'Played for important alerts' },
			{ key: 'sparkle', title: 'Sparkle Sound', description: 'Played for positive actions' },
			{ key: 'error', title: 'Error Sound', description: 'Played when an error occurs' },
			{ key: 'alert', title: 'Alert Sound', description: 'Played for new alerts' },
			{ key: 'warning', title: 'Warning Sound', description: 'Played when issuing warnings' },
			{ key: 'rollback', title: 'Rollback Sound', description: 'Played when rolling back edits' },
			{ key: 'queue', title: 'Queue Update Sound', description: 'Played when the queue updates' }
		];

		return (
			<div>
				<SettingsSection
					title="Audio Controls"
					description="Configure volume and sounds for different actions"
				>
					{audioControls.map(({ key, title, description }) => (
						<VolumeControl
							key={key}
							triggerKey={key}
							title={title}
							description={description}
							value={volumes[key] ?? 0.5}
							currentSound={soundMappings[key] || key}
							soundOptions={soundOptions}
							sounds={sounds}
							onVolumeChange={(value) => onVolumeChange(key, value)}
							onSoundChange={(soundKey) => onSoundChange(key, soundKey)}
							playFunction={() => playSound(key)}
						/>
					))}
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Appearance Settings Panel Component
 */
export class QueueSettings extends Component {
	constructor(props) {
		super(props);
		this.state = {
			selectedPalette: props.selectedPalette,
			queues: [...props.queues]
		};
	}

	handlePaletteChange = (index) => {
		this.setState({ selectedPalette: index });
		this.props.onPaletteChange(index);
	}

	handleReorder = (orderedKeys) => {
		const { queues } = this.state;
		const { onQueueReorder } = this.props;

		const queueMap = new Map(queues);
		const newQueues = orderedKeys.map(key => [ key, queueMap.get(key) ]);

		this.setState({ queues: newQueues });
		if (onQueueReorder) {
			onQueueReorder(newQueues);
		}
	}

	handleToggle = (id, enabled) => {
		const { queues } = this.state;
		const { onQueueToggle } = this.props;

		const newQueues = queues.map(([ queueId, data ]) =>
			queueId === id ? [ queueId, { ...data, enabled } ] : [ queueId, data ]
		);

		this.setState({ queues: newQueues });
		if (onQueueToggle) {
			onQueueToggle(id, enabled);
		}
	}

	render() {
		const { colorPalettes } = this.props;
		const { selectedPalette, queues } = this.state;

		return (
			<div>
				<SettingsSection
					title="Queues"
					description="Enable or disable different edit queues, and customize their order"
				>
					<DraggableOrderList onReorder={this.handleReorder}>
						{queues.map(([ id, queue ]) => (
							<DraggableOrderItem
								key={queue.key}
								name={queue.name}
								enabled={queue.enabled}
								onToggle={(enabled) => this.handleToggle(queue.key, enabled)}
							/>
						))}
					</DraggableOrderList>
				</SettingsSection>
				<SettingsSection
					title="Color Palette"
					description="Choose how ORES scores are displayed visually"
				>
					<div class="palette-selector">
						{colorPalettes.map((colors, index) => (
							<div
								key={index}
								class={`palette-option ${selectedPalette === index ? 'selected' : ''}`}
								onClick={() => this.handlePaletteChange(index)}
							>
								<div class="palette-name">Palette {index + 1}</div>
								<div class="palette-preview">
									{colors.map((color, i) => (
										<div
											key={i}
											class="palette-color"
											style={{ backgroundColor: color }}
										/>
									))}
								</div>
							</div>
						))}
					</div>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Zen Settings Panel Component
 */
export class ZenSettings extends Component {
	render() {
		const {
			enabled,

			sound,
			music,

			notices,
			alerts,

			badges,
			toasts,
		} = this.props;

		return (
			<div>
				<SettingsTogglesSection
					title="Zen Mode"
					description="Customize your distraction-free reviewing experience"
				>
					<SettingsSection compact inline>
						<SettingsSectionContent
							title="Enable Zen Mode"
							description="Reduce on-screen distractions while reviewing edits"
						/>
						<Toggle
							id="zen-mode-enable"
							value={enabled}
							onChange={this.props.onEnableChange}
						/>
					</SettingsSection>
				</SettingsTogglesSection>
				<SettingsCompactGrid>
					<SettingsSection
							compact
							title="Enable Sounds"
							description="Play sounds in Zen mode"
					>
						<Toggle
							value={sound.enabled}
							onChange={this.props.onSoundChange}
						/>
					</SettingsSection>
					<SettingsSection
							compact
							title="Enable Music"
							description="Play background music in Zen mode"
					>
						<Toggle
							value={music.enabled}
							onChange={this.props.onMusicChange}
						/>
					</SettingsSection>

					<SettingsSection
						compact
						title="Alerts"
						description="Show alerts in Zen mode"
					>
						<Toggle
							value={alerts.enabled}
							onChange={this.props.onAlertsChange}
						/>
					</SettingsSection>
					<SettingsSection
						compact
						title="Notices"
						description="Show notices in Zen mode"
					>
						<Toggle
							value={notices.enabled}
							onChange={this.props.onNoticesChange}
						/>
					</SettingsSection>
					<SettingsSection
						compact
						title="Toasts"
						description="Show toast messages in Zen mode"
					>
						<Toggle
							value={toasts.enabled}
							onChange={this.props.onToastsChange}
						/>
					</SettingsSection>

					<SettingsSection
						compact
						title="Notification Badges"
						description="Show all notification badges in Zen mode."
					>
						<Toggle
							value={badges.enabled}
							onChange={this.props.onBadgesChange}
						/>
					</SettingsSection>
				</SettingsCompactGrid>
			</div>
		);
	}
}

/**
 * User List Component (for whitelist/highlight users)
 */
export class UserList extends Component {
	render() {
		const { users, onRemove, showDates } = this.props;

		if (users.length === 0) {
			return <div class="user-list-empty">No users in this list</div>;
		}

		return (
			<div class="user-list">
				{users.map((user) => (
					<div class="user-list-item" key={user.name}>
						<span class="user-list-name">{user.name}</span>
						{showDates && user.date && (
							<span class="user-list-date">{new Date(user.date).toLocaleDateString()}</span>
						)}
						<button
							class="user-list-remove"
							onClick={() => onRemove(user.name)}
							title="Remove user"
						>
							<span class="fa fa-times"></span>
						</button>
					</div>
				))}
			</div>
		);
	}
}

/**
 * Whitelist Panel Component
 */
export class WhitelistSettings extends Component {
	constructor(props) {
		super(props);
		this.state = {
			newUsername: ''
		};
	}

	handleAdd = () => {
		const { newUsername } = this.state;
		if (newUsername.trim()) {
			this.props.onAdd(newUsername.trim());
			this.setState({ newUsername: '' });
		}
	}

	render() {
		const { users, onRemove } = this.props;
		const { newUsername } = this.state;

		return (
			<div>
				<SettingsSection
					title="Whitelist"
					description="Users on the whitelist will not appear in your queue"
				>
					<div class="user-list-controls">
						<input
							type="text"
							class="user-list-input"
							placeholder="Enter username..."
							value={newUsername}
							onInput={(e) => this.setState({ newUsername: e.target.value })}
							onKeyDown={(e) => {
								if (e.key === 'Enter') this.handleAdd();
							}}
							autoComplete="off"
						/>
						<button
							class="user-list-add-button"
							onClick={this.handleAdd}
						>
							<span class="fa fa-plus"></span> Add User
						</button>
					</div>
					<UserList
						users={users}
						onRemove={onRemove}
						showDates={true}
					/>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Highlight Users Panel Component
 */
export class HighlightSettings extends Component {
	constructor(props) {
		super(props);
		this.state = {
			newUsername: ''
		};
	}

	handleAdd = () => {
		const { newUsername } = this.state;
		if (newUsername.trim()) {
			this.props.onAdd(newUsername.trim());
			this.setState({ newUsername: '' });
		}
	}

	render() {
		const { users, onRemove } = this.props;
		const { newUsername } = this.state;

		return (
			<div>
				<SettingsSection
					title="highlight Users"
					description="Edits from highlight users will be shown with a yellow indicator"
				>
					<div class="user-list-controls">
						<input
							type="text"
							class="user-list-input"
							placeholder="Enter username..."
							value={newUsername}
							onInput={(e) => this.setState({ newUsername: e.target.value })}
							onKeyDown={(e) => {
								if (e.key === 'Enter') this.handleAdd();
							}}
							autoComplete="off"
						/>
						<button
							class="user-list-add-button"
							onClick={this.handleAdd}
						>
							<span class="fa fa-plus"></span> Add User
						</button>
					</div>
					<UserList
						users={users}
						onRemove={onRemove}
						showDates={true}
					/>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Statistics Panel Component
 */
export class StatisticsSettings extends Component {
	render() {
		const { stats } = this.props;

		return (
			<div>
				<SettingsSection
					title="Your Statistics"
					description="Your WikiShield usage statistics"
				>
					<div class="stats-grid">
						<div class="stat-item">
							<div class="stat-value">{stats.editsReviewed || 0}</div>
							<div class="stat-label">Edits Reviewed</div>
						</div>
						<div class="stat-item">
							<div class="stat-value">{stats.editsReverted || 0}</div>
							<div class="stat-label">Edits Reverted</div>
						</div>
						<div class="stat-item">
							<div class="stat-value">{stats.warningsIssued || 0}</div>
							<div class="stat-label">Warnings Issued</div>
						</div>
						<div class="stat-item">
							<div class="stat-value">{stats.usersReported || 0}</div>
							<div class="stat-label">Users Reported</div>
						</div>
						<div class="stat-item">
							<div class="stat-value">{stats.pagesProtected || 0}</div>
							<div class="stat-label">Pages Protected</div>
						</div>
						<div class="stat-item">
							<div class="stat-value">{stats.thanksGiven || 0}</div>
							<div class="stat-label">Thanks Given</div>
						</div>
					</div>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * AI Settings Panel Component
 */
export class AISettings extends Component {
	constructor(props) {
		super(props);
		this.state = {
			connectionStatus: '',
			modelsStatus: '',
			availableModels: [],
			testingConnection: false,
			loadingModels: false
		};
	}

	testConnection = async () => {
		const { ollamaServerUrl, onTestConnection } = this.props;
		this.setState({ testingConnection: true, connectionStatus: 'Testing...' });

		const result = await onTestConnection();

		this.setState({
			testingConnection: false,
			connectionStatus: result ? 'Connected!' : 'Failed to connect'
		});
	}

	refreshModels = async () => {
		const { onRefreshModels } = this.props;
		this.setState({ loadingModels: true, modelsStatus: 'Loading...' });

		const models = await onRefreshModels();

		this.setState({
			loadingModels: false,
			availableModels: models || [],
			modelsStatus: models ? `Found ${models.length} models` : 'Failed to load models'
		});
	}

	render() {
		const {
			enableOllamaAI,
			ollamaServerUrl,
			selectedModel,
			onEnableChange,
			onServerUrlChange,
			onModelSelect
		} = this.props;

		const { connectionStatus, modelsStatus, availableModels, testingConnection, loadingModels } = this.state;

		return (
			<div>
				<SettingsSection
					id="enable-ollama-ai"
					title="Enable Ollama AI Analysis"
					description="Use local AI models with complete privacy. Free & fast."
				>
					<Toggle
						value={enableOllamaAI}
						onChange={onEnableChange}
					/>
				</SettingsSection>

				<SettingsSection
					id="ollama-server-url"
					title="Server URL"
				>
					<div class="ollama-url-controls">
						<input
							type="text"
							id="ollama-url-input"
							value={ollamaServerUrl}
							onInput={(e) => onServerUrlChange(e.target.value)}
							placeholder="http://localhost:11434"
							style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; margin-bottom: 8px;"
							autoComplete="off"
						/>
						<button
							id="test-connection-btn"
							onClick={this.testConnection}
							disabled={testingConnection}
							style="padding: 6px 12px; background: #667eea; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 0.9em;"
						>
							Test Connection
						</button>
						<span id="connection-status" style="margin-left: 8px; font-size: 0.9em;">
							{connectionStatus}
						</span>
					</div>
				</SettingsSection>

				<SettingsSection
					id="ollama-model-select"
					title="Model Selection"
				>
					<div class="ollama-model-controls">
						<button
							id="refresh-models-btn"
							onClick={this.refreshModels}
							disabled={loadingModels}
							style="padding: 6px 12px; background: #667eea; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 0.9em;"
						>
							<span class="fa fa-sync"></span> Refresh Models
						</button>
						<span id="models-status" style="margin-left: 8px; font-size: 0.9em;">
							{modelsStatus}
						</span>
						<div style="margin-top: 12px;" id="models-container">
							{availableModels.length === 0 ? (
								<div style="color: #666; font-style: italic; font-size: 0.9em;">
									Click "Refresh Models" to load available models
								</div>
							) : (
								<div class="models-list">
									{availableModels.map((model) => (
										<div
											key={model.name}
											class={`model-item ${selectedModel === model.name ? 'selected' : ''}`}
											onClick={() => onModelSelect(model.name)}
										>
											<div class="model-name">{model.name}</div>
											<div class="model-size">{model.size}</div>
										</div>
									))}
								</div>
							)}
						</div>
					</div>
				</SettingsSection>

				<SettingsSection>
					<div class="settings-section-title" style="color: #dc3545;">CORS Setup Required</div>
					<div class="settings-section-desc" style="background: rgba(255, 243, 205, 0.2); padding: 10px; border-radius: 6px; border-left: 4px solid #ffc107; font-size: 0.9em;">
						<strong>Set environment variable:</strong> <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; color: #333;">OLLAMA_ORIGINS</code>
						<br /><strong>Value:</strong> <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; color: #333;">https://en.wikipedia.org,https://*.wikipedia.org</code>
						<br /><br />
						<details style="cursor: pointer;">
							<summary style="font-weight: 600; margin-bottom: 6px;">Windows (Permanent)</summary>
							<ol style="margin: 6px 0; padding-left: 20px; font-size: 0.85em;">
								<li>System Properties → Environment Variables</li>
								<li>New Variable: <code style="color: #333;">OLLAMA_ORIGINS</code></li>
								<li>Value: <code style="color: #333;">https://en.wikipedia.org,https://*.wikipedia.org</code></li>
								<li>Restart Ollama</li>
							</ol>
						</details>
						<details style="cursor: pointer;">
							<summary style="font-weight: 600; margin-bottom: 6px;">Windows (Temporary)</summary>
							<pre style="background: #2d2d2d; color: #f8f8f2; padding: 8px; border-radius: 4px; font-size: 0.8em; margin: 6px 0;">{`$env:OLLAMA_ORIGINS="https://en.wikipedia.org,https://*.wikipedia.org"
ollama serve`}</pre>
						</details>
						<details style="cursor: pointer;">
							<summary style="font-weight: 600; margin-bottom: 6px;">macOS/Linux</summary>
							Add to <code>~/.bashrc</code> or <code>~/.zshrc</code>:
							<pre style="background: #2d2d2d; color: #f8f8f2; padding: 8px; border-radius: 4px; font-size: 0.8em; margin: 6px 0;">{`export OLLAMA_ORIGINS="https://en.wikipedia.org,https://*.wikipedia.org"`}</pre>
							Then: <code>source ~/.bashrc && ollama serve</code>
						</details>
					</div>
				</SettingsSection>

				<SettingsSection title="Quick Info">
					<div class="settings-section-desc" style="font-size: 0.9em;">
						<strong>Get Ollama:</strong> <a href="https://ollama.com" target="_blank" style="color: #667eea; font-weight: bold;">ollama.com</a>
						<br /><strong>Popular models:</strong> llama3.2, mistral, gemma2, qwen2.5
						<br /><strong>Detects:</strong> Vandalism, spam, POV, attacks, copyright issues, policy violations
					</div>
				</SettingsSection>
			</div>
		);
	}
}

export class AutoReportingSettings extends Component {
	render() {
		const {
			enableAutoReporting,
			autoReportReasons,
			selectedAutoReportReasons,
		} = this.props;

		return (
			<div>
				<SettingsSection
					id="enable-auto-reporting"
					title="Enable Auto-Reporting"
					description="Automatically report edits that receive certain warnings"
				>
					<Toggle
						value={enableAutoReporting}
						onChange={this.props.onEnableChange}
					/>
				</SettingsSection>
				<SettingsSection
					title="Auto-Reportable Warnings"
					description="Select which warnings should trigger automatic reporting"
				>
					<div class="checkbox-container">
						{autoReportReasons.map((warning) => (
							<div class="auto-reportable-warning-item" key={warning}>
								<label class="checkbox-box">
									<input
										type="checkbox"
										checked={selectedAutoReportReasons.has(warning)}
										onChange={(e) => {
											this.props.onWarningToggle(warning, e.target.checked);
										}}
										autoComplete="off"
									/>
									<div class="checkmark"></div>
								</label>
								<span>{warning}</span>
							</div>
						))}
					</div>
				</SettingsSection>
			</div>
		);
	}
}

/**
 * Import/Export Settings Panel Component
 */
export class SaveSettings extends Component {
	constructor(props) {
		super(props);
		this.state = {
			showImportInput: false,
			importValue: '',
			statusMessage: null
		};
	}

	handleExport = () => {
		try {
			const result = this.props.onExport();
			this.setState({
				statusMessage: {
					type: 'success',
					title: 'Settings exported successfully!',
					message: 'The base64 string has been copied to your clipboard.'
				}
			});
			setTimeout(() => this.setState({ statusMessage: null }), 5000);
		} catch (error) {
			this.setState({
				statusMessage: {
					type: 'error',
					title: 'Export failed!',
					message: error.message
				}
			});
		}
	}

	handleImportToggle = () => {
		if (!this.state.showImportInput) {
			this.setState({ showImportInput: true, statusMessage: null });
		} else {
			this.handleImportApply();
		}
	}

	handleImportApply = () => {
		const { importValue } = this.state;

		if (!importValue.trim()) {
			this.setState({
				statusMessage: {
					type: 'error',
					title: 'No input!',
					message: 'Please paste a base64 settings string.'
				}
			});
			return;
		}

		try {
			const result = this.props.onImport(importValue);

			let warningsHtml = '';
			if (result.warnings && result.warnings.length > 0) {
				warningsHtml = '<br/><br/><strong>Warnings:</strong><ul>';
				result.warnings.forEach(warning => {
					warningsHtml += `<li>${warning}</li>`;
				});
				warningsHtml += '</ul>';
			}

			this.setState({
				statusMessage: {
					type: 'success',
					title: 'Settings imported successfully!',
					message: 'Please reload the page for all changes to take effect.',
					extra: warningsHtml
				},
				showImportInput: false,
				importValue: ''
			});
		} catch (error) {
			this.setState({
				statusMessage: {
					type: 'error',
					title: 'Import failed!',
					message: error.message
				}
			});
		}
	}

	handleReset = async () => {
		if (await this.props.onReset()) {
			this.setState({
				statusMessage: {
					type: 'success',
					title: 'Settings reset successfully!',
					message: 'All settings have been restored to default values.'
				}
			});
		}
	}

	render() {
		const {
			showImportInput, importValue, statusMessage,
			enableCloudStorage,
			onCloudStorageToggle
		} = this.state;

		return (
			<div>
				<SettingsSection
					id="enable-cloud-storage"
					title="Enable Cloud Storage"
					description="Store your settings in the cloud for access across multiple browsers and devices"
				>
					<Toggle
						value={enableCloudStorage}
						onChange={onCloudStorageToggle}
					/>
				</SettingsSection>
				<SettingsSection
					title="Import / Export / Reset Settings"
					description="Import, export, or reset your WikiShield settings. Settings are encoded as a base64 string for easy sharing."
				>
					<div class="buttons">
						<button
							id="export-settings-btn"
							onClick={this.handleExport}
						>
							<span class="fa fa-download"></span> Export Settings
						</button>
						<button
							id="import-settings-btn"
							onClick={this.handleImportToggle}
							style={`${showImportInput ? '--background: 40, 167, 69, 1;' : ''}`}
						>
							<span class={`fa ${showImportInput ? 'fa-check' : 'fa-upload'}`}></span>
							{showImportInput ? ' Apply Import' : ' Import Settings'}
						</button>
						<button
							id="reset-settings-btn"
							onClick={this.handleReset}
							style="--background: 211, 51, 51;"
						>
							<span class="fa fa-undo"></span> Reset to Default
						</button>
					</div>

					{statusMessage && (
						<div
							id="import-export-status"
							class={statusMessage.type === 'success' ? 'status-success' : 'status-error'}
						>
							<div>
								<span class={`fa ${statusMessage.type === 'success' ? 'fa-check-circle' : 'fa-times-circle'}`}></span>
								<div>
									<strong>{statusMessage.title}</strong>
									<div>{statusMessage.message}</div>
									{statusMessage.extra && (
										<div dangerouslySetInnerHTML={{ __html: statusMessage.extra }} />
									)}
								</div>
							</div>
						</div>
					)}

					{showImportInput && (
						<textarea
							id="import-settings-input"
							placeholder="Paste base64 settings string here..."
							value={importValue}
							onInput={(e) => this.setState({ importValue: e.target.value })}
							resize="vertical"
						/>
					)}
				</SettingsSection>
			</div>
		);
	}
}

/**
 * About Panel Component
 */
export class AboutSettings extends Component {
	render() {
		const { version, changelog } = this.props;

		return (
			<div>
				<SettingsSection title="About WikiShield">
					<div class="about-content">
						<div class="about-version">
							<span class="fa fa-shield-alt"></span>
							<span>WikiShield v{version}</span>
						</div>
						<div class="about-description">
							<p>WikiShield is a powerful tool for patrolling Wikipedia edits in real-time.</p>
							<p>Developed with ❤️ for the Wikipedia community.</p>
						</div>
						<div class="about-links">
							<a href="https://en.wikipedia.org/wiki/Wikipedia:WikiShield" target="_blank">
								<span class="fa fa-book"></span> Documentation
							</a>
							<a href="https://github.com/LuniZunie/WikiShield" target="_blank">
								<span class="fa fa-code-branch"></span> Source Code
							</a>
							<a href="https://en.wikipedia.org/wiki/Wikipedia_talk:WikiShield" target="_blank">
								<span class="fa fa-comments"></span> Feedback
							</a>
						</div>
					</div>
				</SettingsSection>

				{changelog && (
					<SettingsSection>
						<div class="changelog-content" dangerouslySetInnerHTML={{ __html: changelog }} />
					</SettingsSection>
				)}
			</div>
		);
	}
}