import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../widgets/dialog_action_button.dart';
import '../i18n/strings.g.dart';
import '../media/media_item.dart';
import '../media/media_kind.dart';
import '../services/plex_client.dart';
import '../utils/app_logger.dart';
import '../utils/dialogs.dart';
import '../utils/formatters.dart';
import '../utils/language_codes.dart';
import '../utils/provider_extensions.dart';
import '../utils/snackbar_helper.dart';
import '../widgets/app_icon.dart';
import '../widgets/artwork_picker_dialog.dart';
import '../widgets/focusable_list_tile.dart';
import '../widgets/focused_scroll_scaffold.dart';
import '../widgets/optimized_media_image.dart';
import '../widgets/tag_edit_dialog.dart';
import '../widgets/loading_indicator_box.dart';

/// Plex `type` number used by `/library/sections/{id}/all` PUT — required by
/// [PlexClient.updateMetadata]. Mirrors the legacy `PlexMediaType.typeNumber`
/// helper so the migrated [PlexMetadataEditScreen] (which now operates on
/// [MediaItem]) can still talk to the Plex update endpoint.
int _plexTypeNumberForKind(MediaKind kind) => switch (kind) {
  MediaKind.movie => 1,
  MediaKind.show => 2,
  MediaKind.season => 3,
  MediaKind.episode => 4,
  MediaKind.artist => 8,
  MediaKind.album => 9,
  MediaKind.track => 10,
  _ => 0,
};

/// Plex-only metadata editor. Calls Plex-specific PUT endpoints; the Jellyfin
/// backend has no analogous surface yet.
class PlexMetadataEditScreen extends StatefulWidget {
  final MediaItem metadata;

  const PlexMetadataEditScreen({super.key, required this.metadata});

  @override
  State<PlexMetadataEditScreen> createState() => _PlexMetadataEditScreenState();
}

class _PlexMetadataEditScreenState extends State<PlexMetadataEditScreen> {
  late PlexClient _client;

  /// Full neutral metadata reloaded after save / artwork picker. Metadata
  /// editing uses Plex-only update endpoints (Jellyfin has no equivalent in
  /// the current scope), so the in-memory model is [MediaItem] but the
  /// boundary call to [PlexClient.updateMetadata] is Plex-only.
  MediaItem? _fullMetadata;
  bool _isLoading = true;
  bool _isSaving = false;

  // Text field values
  String? _title;
  String? _titleSort;
  String? _originalTitle;
  String? _originallyAvailableAt;
  String? _contentRating;
  String? _studio;
  String? _tagline;
  String? _summary;

  // Original values for change detection
  String? _origTitle;
  String? _origTitleSort;
  String? _origOriginalTitle;
  String? _origOriginallyAvailableAt;
  String? _origContentRating;
  String? _origStudio;
  String? _origTagline;
  String? _origSummary;

  // Tag field values
  final Map<String, List<String>> _tags = {};
  final Map<String, List<String>> _origTags = {};

  // Advanced prefs (loaded from metadata JSON)
  final Map<String, String> _currentPrefs = {};

  static bool _tagsEqual(List<String> a, List<String> b) => a.length == b.length && a.every((e) => b.contains(e));

  bool get _hasTagChanges => _tags.keys.any((k) => !_tagsEqual(_tags[k] ?? [], _origTags[k] ?? []));

  bool get _hasChanges =>
      _title != _origTitle ||
      _titleSort != _origTitleSort ||
      _originalTitle != _origOriginalTitle ||
      _originallyAvailableAt != _origOriginallyAvailableAt ||
      _contentRating != _origContentRating ||
      _studio != _origStudio ||
      _tagline != _origTagline ||
      _summary != _origSummary ||
      _hasTagChanges;

  MediaKind get _mediaType => widget.metadata.kind;

  /// Library section id required by the Plex update endpoint. Plex stores it
  /// as an int; [MediaItem.libraryId] preserves it as a string.
  int? get _librarySectionId => int.tryParse(_fullMetadata?.libraryId ?? widget.metadata.libraryId ?? '');

  @override
  void initState() {
    super.initState();
    _client = context.getPlexClientWithFallback(widget.metadata.serverId);
    _loadMetadata();
  }

  Future<void> _loadMetadata() async {
    try {
      // If the passed metadata already has full fields (e.g., from detail screen),
      // use it directly instead of re-fetching. We check both summary and
      // libraryId since the edit screen needs both for display and save.
      if (widget.metadata.summary != null && widget.metadata.libraryId != null) {
        _fullMetadata = widget.metadata;
        _initFieldsFromMetadata(widget.metadata);
        setState(() => _isLoading = false);
        return;
      }

      final meta = await _client.fetchItem(widget.metadata.id);
      if (!mounted) return;
      if (meta != null) {
        _fullMetadata = meta;
        _initFieldsFromMetadata(meta);
      } else {
        _initFieldsFromMetadata(widget.metadata);
      }
      setState(() => _isLoading = false);
    } catch (e) {
      if (!mounted) return;
      _initFieldsFromMetadata(widget.metadata);
      setState(() => _isLoading = false);
    }
  }

  void _initFieldsFromMetadata(MediaItem meta) {
    _title = meta.title;
    _titleSort = meta.titleSort ?? '';
    _originalTitle = meta.originalTitle ?? '';
    _originallyAvailableAt = meta.originallyAvailableAt ?? '';
    _contentRating = meta.contentRating ?? '';
    _studio = meta.studio ?? '';
    _tagline = meta.tagline ?? '';
    _summary = meta.summary ?? '';

    _origTitle = _title;
    _origTitleSort = _titleSort;
    _origOriginalTitle = _originalTitle;
    _origOriginallyAvailableAt = _originallyAvailableAt;
    _origContentRating = _contentRating;
    _origStudio = _studio;
    _origTagline = _tagline;
    _origSummary = _summary;

    void initTag(String key, List<String>? values) {
      _tags[key] = List.of(values ?? []);
      _origTags[key] = List.of(values ?? []);
    }

    initTag('genre', meta.genres);
    initTag('director', meta.directors);
    initTag('writer', meta.writers);
    initTag('producer', meta.producers);
    initTag('country', meta.countries);
    initTag('collection', meta.collections);
    initTag('label', meta.labels);
    initTag('style', meta.styles);
    initTag('mood', meta.moods);
  }

  Future<void> _save() async {
    if (!_hasChanges || _isSaving) return;

    final sectionId = _librarySectionId;
    if (sectionId == null) {
      if (mounted) showErrorSnackBar(context, t.metadataEdit.metadataUpdateFailed);
      return;
    }

    setState(() => _isSaving = true);

    Map<String, ({List<String> current, List<String> original})>? tagChanges;
    for (final key in _tags.keys) {
      final current = _tags[key] ?? [];
      final original = _origTags[key] ?? [];
      if (!_tagsEqual(current, original)) {
        tagChanges ??= {};
        tagChanges[key] = (current: current, original: original);
      }
    }

    bool success = false;
    try {
      success = await _client.updateMetadata(
        sectionId: sectionId,
        ratingKey: widget.metadata.id,
        typeNumber: _plexTypeNumberForKind(_mediaType),
        title: _title != _origTitle ? _title : null,
        titleSort: _titleSort != _origTitleSort ? _titleSort : null,
        originalTitle: _originalTitle != _origOriginalTitle ? _originalTitle : null,
        originallyAvailableAt: _originallyAvailableAt != _origOriginallyAvailableAt ? _originallyAvailableAt : null,
        contentRating: _contentRating != _origContentRating ? _contentRating : null,
        studio: _studio != _origStudio ? _studio : null,
        tagline: _tagline != _origTagline ? _tagline : null,
        summary: _summary != _origSummary ? _summary : null,
        tagChanges: tagChanges,
      );
    } catch (e, st) {
      // [PlexClient._wrapBoolApiCall] rethrows on HTTP/network errors —
      // catch here so `_isSaving` doesn't get stuck `true`.
      appLogger.e('Failed to update metadata', error: e, stackTrace: st);
    }

    if (!mounted) return;
    setState(() => _isSaving = false);

    if (success) {
      showSuccessSnackBar(context, t.metadataEdit.metadataUpdated);
      Navigator.pop(context, true);
    } else {
      showErrorSnackBar(context, t.metadataEdit.metadataUpdateFailed);
    }
  }

  Future<void> _editTextField({
    required String title,
    required String label,
    required String? currentValue,
    required ValueChanged<String> onChanged,
    bool multiline = false,
  }) async {
    final String? result;
    if (multiline) {
      result = await showMultilineTextInputDialog(context, title: title, labelText: label, initialValue: currentValue);
    } else {
      result = await showTextInputDialog(
        context,
        title: title,
        labelText: label,
        hintText: '',
        initialValue: currentValue,
      );
    }

    if (result != null && mounted) {
      final value = result;
      setState(() => onChanged(value));
    }
  }

  Future<void> _editDate() async {
    DateTime initial = DateTime.now();
    if (_originallyAvailableAt != null && _originallyAvailableAt!.isNotEmpty) {
      final parsed = DateTime.tryParse(_originallyAvailableAt!);
      if (parsed != null) initial = parsed;
    }

    final picked = await showDatePicker(
      context: context,
      initialDate: initial,
      firstDate: DateTime(1800),
      lastDate: DateTime(2200),
    );

    if (picked != null && mounted) {
      setState(() {
        _originallyAvailableAt = '${picked.year}-${padNumber(picked.month, 2)}-${padNumber(picked.day, 2)}';
      });
    }
  }

  Future<void> _openArtworkPicker(String element) async {
    final result = await showDialog<bool>(
      context: context,
      builder: (context) => ArtworkPickerDialog(client: _client, ratingKey: widget.metadata.id, element: element),
    );

    if (result == true && mounted) {
      // Re-fetch metadata to get updated artwork paths without resetting
      // any text field edits the user may have made.
      await _reloadArtwork();
    }
  }

  Future<void> _reloadArtwork() async {
    try {
      final meta = await _client.fetchItem(widget.metadata.id);
      if (!mounted) return;
      if (meta != null) {
        setState(() => _fullMetadata = meta);
      }
    } catch (_) {
      // Artwork was already saved by the picker; display will refresh next
      // time the editor is opened.
    }
  }

  Future<void> _showAdvancedSettingDialog({
    required String title,
    required String prefKey,
    required List<({String value, String label})> options,
  }) async {
    // Determine current value from metadata or default
    final currentValue = _currentPrefs[prefKey] ?? _getMetadataPrefValue(prefKey);

    final result = await showDialog<String>(
      context: context,
      builder: (dialogContext) {
        String? selected = currentValue;
        return StatefulBuilder(
          builder: (context, setDialogState) {
            return AlertDialog(
              title: Text(title),
              content: SizedBox(
                width: double.maxFinite,
                child: RadioGroup<String>(
                  groupValue: selected,
                  onChanged: (val) {
                    setDialogState(() => selected = val);
                    Navigator.pop(dialogContext, val);
                  },
                  child: ListView(
                    shrinkWrap: true,
                    children: options.map((option) {
                      return FocusableRadioListTile<String>(
                        key: ValueKey(option.value),
                        title: Text(option.label),
                        value: option.value,
                      );
                    }).toList(),
                  ),
                ),
              ),
              actions: [DialogActionButton(onPressed: () => Navigator.pop(dialogContext), label: t.common.cancel)],
            );
          },
        );
      },
    );

    if (result != null && mounted) {
      final previous = _currentPrefs[prefKey];
      setState(() => _currentPrefs[prefKey] = result);
      try {
        await _client.updateMetadataPrefs(widget.metadata.id, {prefKey: result});
      } catch (e, st) {
        // [PlexClient._wrapBoolApiCall] rethrows — revert the optimistic
        // UI change and surface a snackbar so the radio doesn't lie.
        appLogger.e('Failed to update metadata prefs', error: e, stackTrace: st);
        if (!mounted) return;
        setState(() {
          if (previous == null) {
            _currentPrefs.remove(prefKey);
          } else {
            _currentPrefs[prefKey] = previous;
          }
        });
        showErrorSnackBar(context, t.metadataEdit.metadataUpdateFailed);
      }
    }
  }

  String _getMetadataPrefValue(String key) {
    // These prefs appear as keys on the raw metadata JSON when non-default.
    // Since we use typed models, we check known fields. Falls back to the
    // public [MediaItem] when the Plex-typed cache hasn't loaded yet.
    //
    // [subtitleLanguage] / [subtitleMode] live on [PlexMediaItem] (Plex-only
    // — Jellyfin has no per-item subtitle preference). This screen is
    // documented as Plex-only at the class level, so the cast is safe; on
    // the off chance a Jellyfin item slips through, fall back to the
    // unset/default string.
    final fullMeta = _fullMetadata;
    final fullPlex = fullMeta is PlexMediaItem ? fullMeta : null;
    final widgetMeta = widget.metadata;
    final widgetPlex = widgetMeta is PlexMediaItem ? widgetMeta : null;
    switch (key) {
      case 'audioLanguage':
        return fullMeta?.audioLanguage ?? widgetMeta.audioLanguage ?? '';
      case 'subtitleLanguage':
        return fullPlex?.subtitleLanguage ?? widgetPlex?.subtitleLanguage ?? '';
      case 'subtitleMode':
        return (fullPlex?.subtitleMode ?? widgetPlex?.subtitleMode)?.toString() ?? '-1';
      default:
        return '';
    }
  }

  String _getDisplayValueForPref(String prefKey, List<({String value, String label})> options) {
    final val = _currentPrefs[prefKey] ?? _getMetadataPrefValue(prefKey);
    for (final option in options) {
      if (option.value == val) return option.label;
    }
    return options.first.label;
  }

  bool get _showSortTitle => _mediaType != MediaKind.season;
  bool get _showOriginalTitle => _mediaType == MediaKind.movie || _mediaType == MediaKind.show;
  bool get _showReleaseDate => _mediaType != MediaKind.season;
  bool get _showContentRating => _mediaType != MediaKind.season;
  bool get _showStudio => _mediaType == MediaKind.movie || _mediaType == MediaKind.show;
  bool get _showTagline => _mediaType == MediaKind.movie || _mediaType == MediaKind.show;
  bool get _showBackground =>
      _mediaType == MediaKind.movie || _mediaType == MediaKind.show || _mediaType == MediaKind.episode;
  bool get _showExtendedArtwork =>
      _mediaType == MediaKind.movie || _mediaType == MediaKind.show || _mediaType == MediaKind.collection;
  bool get _showAdvanced => _mediaType != MediaKind.episode;

  List<({String key, String label})> get _tagFields {
    switch (_mediaType) {
      case MediaKind.movie:
      case MediaKind.show:
        return [
          (key: 'genre', label: t.metadataEdit.genre),
          (key: 'director', label: t.metadataEdit.director),
          (key: 'writer', label: t.metadataEdit.writer),
          (key: 'producer', label: t.metadataEdit.producer),
          (key: 'country', label: t.metadataEdit.country),
          (key: 'collection', label: t.metadataEdit.collection),
          (key: 'label', label: t.metadataEdit.label),
        ];
      case MediaKind.episode:
        return [(key: 'director', label: t.metadataEdit.director), (key: 'writer', label: t.metadataEdit.writer)];
      case MediaKind.artist:
        return [
          (key: 'genre', label: t.metadataEdit.genre),
          (key: 'style', label: t.metadataEdit.style),
          (key: 'mood', label: t.metadataEdit.mood),
          (key: 'country', label: t.metadataEdit.country),
          (key: 'collection', label: t.metadataEdit.collection),
        ];
      case MediaKind.album:
        return [
          (key: 'genre', label: t.metadataEdit.genre),
          (key: 'style', label: t.metadataEdit.style),
          (key: 'mood', label: t.metadataEdit.mood),
          (key: 'collection', label: t.metadataEdit.collection),
        ];
      default:
        return [];
    }
  }

  Future<void> _editTag(String key, String label) async {
    final result = await showDialog<List<String>>(
      context: context,
      builder: (context) => TagEditDialog(title: label, initialTags: _tags[key] ?? []),
    );
    if (result != null && mounted) {
      setState(() => _tags[key] = result);
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return FocusedScrollScaffold(title: Text(t.metadataEdit.screenTitle), slivers: [LoadingIndicatorBox.sliver]);
    }

    return FocusedScrollScaffold(
      title: Text(t.metadataEdit.screenTitle),
      actions: [
        if (_isSaving)
          const Padding(padding: EdgeInsets.all(12), child: LoadingIndicatorBox(size: 24))
        else
          IconButton(onPressed: _hasChanges ? _save : null, icon: const AppIcon(Symbols.check_rounded, fill: 1)),
      ],
      slivers: [
        SliverPadding(
          padding: const EdgeInsets.all(16),
          sliver: SliverList(
            delegate: SliverChildListDelegate([
              _buildBasicInfoCard(),
              if (_tagFields.isNotEmpty) ...[const SizedBox(height: 16), _buildTagsCard()],
              const SizedBox(height: 16),
              _buildArtworkCard(),
              if (_showAdvanced) ...[const SizedBox(height: 16), _buildAdvancedSettingsCard()],
            ]),
          ),
        ),
      ],
    );
  }

  Widget _buildBasicInfoCard() {
    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              t.metadataEdit.basicInfo,
              style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
            ),
          ),
          _buildFieldTile(
            label: t.metadataEdit.title,
            value: _title,
            onTap: () => _editTextField(
              title: t.metadataEdit.title,
              label: t.metadataEdit.title,
              currentValue: _title,
              onChanged: (v) => _title = v,
            ),
          ),
          if (_showSortTitle)
            _buildFieldTile(
              label: t.metadataEdit.sortTitle,
              value: _titleSort,
              onTap: () => _editTextField(
                title: t.metadataEdit.sortTitle,
                label: t.metadataEdit.sortTitle,
                currentValue: _titleSort,
                onChanged: (v) => _titleSort = v,
              ),
            ),
          if (_showOriginalTitle)
            _buildFieldTile(
              label: t.metadataEdit.originalTitle,
              value: _originalTitle,
              onTap: () => _editTextField(
                title: t.metadataEdit.originalTitle,
                label: t.metadataEdit.originalTitle,
                currentValue: _originalTitle,
                onChanged: (v) => _originalTitle = v,
              ),
            ),
          if (_showReleaseDate)
            _buildFieldTile(label: t.metadataEdit.releaseDate, value: _originallyAvailableAt, onTap: _editDate),
          if (_showContentRating)
            _buildFieldTile(
              label: t.metadataEdit.contentRating,
              value: _contentRating,
              onTap: () => _editTextField(
                title: t.metadataEdit.contentRating,
                label: t.metadataEdit.contentRating,
                currentValue: _contentRating,
                onChanged: (v) => _contentRating = v,
              ),
            ),
          if (_showStudio)
            _buildFieldTile(
              label: t.metadataEdit.studio,
              value: _studio,
              onTap: () => _editTextField(
                title: t.metadataEdit.studio,
                label: t.metadataEdit.studio,
                currentValue: _studio,
                onChanged: (v) => _studio = v,
              ),
            ),
          if (_showTagline)
            _buildFieldTile(
              label: t.metadataEdit.tagline,
              value: _tagline,
              onTap: () => _editTextField(
                title: t.metadataEdit.tagline,
                label: t.metadataEdit.tagline,
                currentValue: _tagline,
                onChanged: (v) => _tagline = v,
              ),
            ),
          _buildFieldTile(
            label: t.metadataEdit.summary,
            value: _summary,
            onTap: () => _editTextField(
              title: t.metadataEdit.summary,
              label: t.metadataEdit.summary,
              currentValue: _summary,
              onChanged: (v) => _summary = v,
              multiline: true,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildFieldTile({required String label, String? value, required VoidCallback onTap}) {
    final displayValue = (value == null || value.isEmpty) ? t.metadataEdit.notSet : value;
    final isNotSet = value == null || value.isEmpty;

    return ListTile(
      title: Text(label),
      subtitle: Text(
        displayValue,
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
        style: isNotSet
            ? TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant.withValues(alpha: 0.5))
            : null,
      ),
      trailing: const AppIcon(Symbols.chevron_right_rounded),
      onTap: onTap,
    );
  }

  Widget _buildTagsCard() {
    final fields = _tagFields;
    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              t.metadataEdit.tags,
              style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
            ),
          ),
          for (final field in fields)
            _buildFieldTile(
              label: field.label,
              value: (_tags[field.key] ?? []).isEmpty ? null : (_tags[field.key]!).join(', '),
              onTap: () => _editTag(field.key, field.label),
            ),
        ],
      ),
    );
  }

  Widget _buildArtworkTile({
    required double width,
    required double height,
    required String? imagePath,
    required String label,
    required String element,
    BoxFit fit = BoxFit.cover,
  }) {
    return ListTile(
      leading: SizedBox(
        width: width,
        height: height,
        child: ClipRRect(
          borderRadius: const BorderRadius.all(Radius.circular(4)),
          child: OptimizedMediaImage(client: _client, imagePath: imagePath, width: width, height: height, fit: fit),
        ),
      ),
      title: Text(label),
      trailing: const AppIcon(Symbols.chevron_right_rounded),
      onTap: () => _openArtworkPicker(element),
    );
  }

  Widget _buildArtworkCard() {
    // Prefer the freshly fetched metadata for image paths, falling back to
    // the public [MediaItem] before the fetch resolves.
    final fullMeta = _fullMetadata;
    final thumb = fullMeta?.thumbPath ?? widget.metadata.thumbPath;
    final art = fullMeta?.artPath ?? widget.metadata.artPath;
    final clearLogo = fullMeta?.clearLogoPath ?? widget.metadata.clearLogoPath;
    final backgroundSquare = fullMeta?.backgroundSquarePath ?? widget.metadata.backgroundSquarePath;

    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              t.metadataEdit.artwork,
              style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
            ),
          ),
          _buildArtworkTile(width: 40, height: 60, imagePath: thumb, label: t.metadataEdit.poster, element: 'posters'),
          if (_showBackground)
            _buildArtworkTile(width: 80, height: 45, imagePath: art, label: t.metadataEdit.background, element: 'arts'),
          if (_showExtendedArtwork)
            _buildArtworkTile(
              width: 80,
              height: 32,
              imagePath: clearLogo,
              label: t.metadataEdit.logo,
              element: 'clearLogos',
              fit: BoxFit.contain,
            ),
          if (_showExtendedArtwork)
            _buildArtworkTile(
              width: 50,
              height: 50,
              imagePath: backgroundSquare,
              label: t.metadataEdit.squareArt,
              element: 'squareArts',
            ),
        ],
      ),
    );
  }

  Widget _buildAdvancedSettingsCard() {
    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              t.metadataEdit.advancedSettings,
              style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
            ),
          ),
          if (_mediaType == MediaKind.show) ..._buildShowAdvancedSettings(),
          if (_mediaType == MediaKind.movie) ..._buildMovieAdvancedSettings(),
          if (_mediaType == MediaKind.season) ..._buildSeasonAdvancedSettings(),
        ],
      ),
    );
  }

  List<Widget> _buildShowAdvancedSettings() {
    return [
      _buildAdvancedTile(
        title: t.metadataEdit.episodeSorting,
        prefKey: 'episodeSort',
        options: [
          (value: '-1', label: t.metadataEdit.libraryDefault),
          (value: '0', label: t.metadataEdit.oldestFirst),
          (value: '1', label: t.metadataEdit.newestFirst),
        ],
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.keep,
        prefKey: 'autoDeletionItemPolicyUnwatchedLibrary',
        options: [
          (value: '0', label: t.metadataEdit.allEpisodes),
          (value: '5', label: t.metadataEdit.latestEpisodes(count: '5')),
          (value: '3', label: t.metadataEdit.latestEpisodes(count: '3')),
          (value: '1', label: t.metadataEdit.latestEpisode),
          (value: '-3', label: t.metadataEdit.episodesAddedPastDays(count: '3')),
          (value: '-7', label: t.metadataEdit.episodesAddedPastDays(count: '7')),
          (value: '-30', label: t.metadataEdit.episodesAddedPastDays(count: '30')),
        ],
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.deleteAfterPlaying,
        prefKey: 'autoDeletionItemPolicyWatchedLibrary',
        options: [
          (value: '0', label: t.metadataEdit.never),
          (value: '1', label: t.metadataEdit.afterADay),
          (value: '7', label: t.metadataEdit.afterAWeek),
          (value: '30', label: t.metadataEdit.afterAMonth),
          (value: '100', label: t.metadataEdit.onNextRefresh),
        ],
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.seasons,
        prefKey: 'flattenSeasons',
        options: [
          (value: '-1', label: t.metadataEdit.libraryDefault),
          (value: '0', label: t.metadataEdit.show),
          (value: '1', label: t.metadataEdit.hide),
        ],
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.episodeOrdering,
        prefKey: 'showOrdering',
        options: [
          (value: '', label: t.metadataEdit.libraryDefault),
          (value: 'tmdbAiring', label: t.metadataEdit.tmdbAiring),
          (value: 'tvdbAiring', label: t.metadataEdit.tvdbAiring),
          (value: 'tvdbAbsolute', label: t.metadataEdit.tvdbAbsolute),
        ],
      ),
      ..._buildMetadataLanguageTiles(),
      ..._buildAudioSubtitleTiles(t.metadataEdit.accountDefault),
    ];
  }

  List<Widget> _buildMovieAdvancedSettings() {
    return _buildMetadataLanguageTiles();
  }

  List<Widget> _buildSeasonAdvancedSettings() {
    return _buildAudioSubtitleTiles(t.metadataEdit.seriesDefault);
  }

  List<Widget> _buildMetadataLanguageTiles() {
    return [
      _buildAdvancedTile(
        title: t.metadataEdit.metadataLanguage,
        prefKey: 'languageOverride',
        options: _metadataLanguageOptions(t.metadataEdit.libraryDefault),
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.useOriginalTitle,
        prefKey: 'useOriginalTitle',
        options: [
          (value: '-1', label: t.metadataEdit.libraryDefault),
          (value: '0', label: t.common.no),
          (value: '1', label: t.common.yes),
        ],
      ),
    ];
  }

  List<Widget> _buildAudioSubtitleTiles(String defaultLabel) {
    return [
      _buildAdvancedTile(
        title: t.metadataEdit.preferredAudioLanguage,
        prefKey: 'audioLanguage',
        options: _audioSubtitleLanguageOptions(defaultLabel),
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.preferredSubtitleLanguage,
        prefKey: 'subtitleLanguage',
        options: _audioSubtitleLanguageOptions(defaultLabel),
      ),
      _buildAdvancedTile(
        title: t.metadataEdit.subtitleMode,
        prefKey: 'subtitleMode',
        options: [
          (value: '-1', label: defaultLabel),
          (value: '0', label: t.metadataEdit.manuallySelected),
          (value: '1', label: t.metadataEdit.shownWithForeignAudio),
          (value: '2', label: t.metadataEdit.alwaysEnabled),
        ],
      ),
    ];
  }

  Widget _buildAdvancedTile({
    required String title,
    required String prefKey,
    required List<({String value, String label})> options,
  }) {
    return ListTile(
      title: Text(title),
      subtitle: Text(_getDisplayValueForPref(prefKey, options)),
      trailing: const AppIcon(Symbols.chevron_right_rounded),
      onTap: () => _showAdvancedSettingDialog(title: title, prefKey: prefKey, options: options),
    );
  }
}

// Plex locale codes for metadata agent language.
const _plexLocaleCodes = [
  'ar-SA',
  'bg-BG',
  'ca-ES',
  'zh-CN',
  'zh-HK',
  'zh-TW',
  'hr-HR',
  'cs-CZ',
  'da-DK',
  'nl-NL',
  'en-US',
  'en-AU',
  'en-CA',
  'en-GB',
  'et-EE',
  'fi-FI',
  'fr-FR',
  'fr-CA',
  'de-DE',
  'el-GR',
  'he-IL',
  'hi-IN',
  'hu-HU',
  'is-IS',
  'id-ID',
  'it-IT',
  'ja-JP',
  'ko-KR',
  'lv-LV',
  'lt-LT',
  'nb-NO',
  'fa-IR',
  'pl-PL',
  'pt-BR',
  'pt-PT',
  'ro-RO',
  'ru-RU',
  'sk-SK',
  'es-ES',
  'es-MX',
  'sv-SE',
  'th-TH',
  'tr-TR',
  'uk-UA',
  'vi-VN',
];

// Common 2-letter codes shown at the top of audio/subtitle pickers.
const _commonAudioSubtitleCodes = ['en', 'ja', 'fr', 'de', 'it', 'es', 'pt', 'ru', 'ar'];

List<({String value, String label})> _buildLanguageOptions(String defaultLabel, List<String> codes) {
  return [(value: '', label: defaultLabel), ...codes.map((c) => (value: c, label: LanguageCodes.getDisplayName(c)))];
}

List<({String value, String label})> _metadataLanguageOptions(String defaultLabel) =>
    _buildLanguageOptions(defaultLabel, _plexLocaleCodes);

List<({String value, String label})> _audioSubtitleLanguageOptions(String defaultLabel) =>
    _buildLanguageOptions(defaultLabel, [..._commonAudioSubtitleCodes, ..._plexLocaleCodes]);
