import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../focus/focusable_button.dart';
import '../focus/focusable_wrapper.dart';
import '../i18n/strings.g.dart';
import '../services/file_picker_service.dart';
import '../services/plex_client.dart';
import '../utils/app_logger.dart';
import '../utils/dialogs.dart';
import '../utils/snackbar_helper.dart';
import '../widgets/app_icon.dart';
import '../widgets/optimized_media_image.dart';
import 'loading_indicator_box.dart';

class ArtworkPickerDialog extends StatefulWidget {
  final PlexClient client;
  final String ratingKey;
  final String element; // "posters" or "arts"

  const ArtworkPickerDialog({super.key, required this.client, required this.ratingKey, required this.element});

  @override
  State<ArtworkPickerDialog> createState() => _ArtworkPickerDialogState();
}

class _ArtworkPickerDialogState extends State<ArtworkPickerDialog> {
  List<Map<String, dynamic>>? _artworkList;
  bool _isLoading = true;
  bool _isApplying = false;

  ({int crossAxisCount, double aspectRatio, String title}) get _elementConfig {
    return switch (widget.element) {
      'posters' => (crossAxisCount: 3, aspectRatio: 2.0 / 3.0, title: t.metadataEdit.selectPoster),
      'arts' => (crossAxisCount: 2, aspectRatio: 16.0 / 9.0, title: t.metadataEdit.selectBackground),
      'clearLogos' => (crossAxisCount: 2, aspectRatio: 2.5 / 1.0, title: t.metadataEdit.selectLogo),
      'squareArts' => (crossAxisCount: 3, aspectRatio: 1.0, title: t.metadataEdit.selectSquareArt),
      _ => (crossAxisCount: 3, aspectRatio: 2.0 / 3.0, title: t.metadataEdit.selectPoster),
    };
  }

  @override
  void initState() {
    super.initState();
    _loadArtwork();
  }

  Future<void> _loadArtwork() async {
    final artwork = await widget.client.getAvailableArtwork(widget.ratingKey, widget.element);
    if (!mounted) return;
    setState(() {
      _artworkList = artwork;
      _isLoading = false;
    });
  }

  Future<void> _selectArtwork(Map<String, dynamic> artwork) async {
    // Use ratingKey (the artwork provider identifier) rather than key (a
    // file-serving path that is already percent-encoded).  Passing key through
    // Dio's query-parameter encoding double-encodes it, causing Plex to
    // silently ignore the selection despite returning 200.
    final url = artwork['ratingKey'] as String? ?? artwork['key'] as String?;
    if (url == null || _isApplying) return;
    await _runArtworkUpdate(() => widget.client.setArtworkFromUrl(widget.ratingKey, widget.element, url));
  }

  Future<void> _addFromUrl() async {
    final url = await showTextInputDialog(
      context,
      title: t.metadataEdit.fromUrl,
      labelText: t.metadataEdit.imageUrl,
      hintText: t.metadataEdit.enterImageUrl,
    );

    if (url == null || url.isEmpty || !mounted) return;
    await _runArtworkUpdate(() => widget.client.setArtworkFromUrl(widget.ratingKey, widget.element, url));
  }

  Future<void> _uploadFile() async {
    final result = await FilePickerService.instance.pickFiles(type: FileType.image, withData: true);

    if (result == null || result.files.isEmpty || !mounted) return;

    final bytes = result.files.first.bytes;
    if (bytes == null) return;
    await _runArtworkUpdate(() => widget.client.uploadArtwork(widget.ratingKey, widget.element, bytes));
  }

  /// Runs an artwork update API call with shared loading-state and
  /// error-handling. The underlying client throws on HTTP errors (see
  /// [PlexClient] `_wrapBoolApiCall`), so we must catch here or `_isApplying`
  /// gets stuck `true` and the user sees an infinite spinner.
  Future<void> _runArtworkUpdate(Future<bool> Function() action) async {
    if (_isApplying) return;
    setState(() => _isApplying = true);
    bool success = false;
    try {
      success = await action();
    } catch (e, st) {
      appLogger.e('Artwork update failed', error: e, stackTrace: st);
    }
    if (!mounted) return;
    setState(() => _isApplying = false);
    if (success) {
      showSuccessSnackBar(context, t.metadataEdit.artworkUpdated);
      Navigator.pop(context, true);
    } else {
      showErrorSnackBar(context, t.metadataEdit.artworkUpdateFailed);
    }
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(_elementConfig.title),
      content: SizedBox(
        width: 500,
        height: 400,
        child: _isLoading ? const Center(child: CircularProgressIndicator()) : _buildArtworkContent(),
      ),
      actions: [
        if (_isApplying) const Padding(padding: EdgeInsets.all(8), child: LoadingIndicatorBox(size: 24)),
        FocusableButton(
          onPressed: _addFromUrl,
          child: TextButton.icon(
            onPressed: _addFromUrl,
            icon: const AppIcon(Symbols.link_rounded, size: 18),
            label: Text(t.metadataEdit.fromUrl),
          ),
        ),
        FocusableButton(
          onPressed: _uploadFile,
          child: TextButton.icon(
            onPressed: _uploadFile,
            icon: const AppIcon(Symbols.upload_rounded, size: 18),
            label: Text(t.metadataEdit.uploadFile),
          ),
        ),
        FocusableButton(
          autofocus: true,
          onPressed: () => Navigator.pop(context),
          child: TextButton(onPressed: () => Navigator.pop(context), child: Text(t.common.cancel)),
        ),
      ],
    );
  }

  Widget _buildArtworkContent() {
    if (_artworkList == null || _artworkList!.isEmpty) {
      return Center(child: Text(t.metadataEdit.noArtworkAvailable));
    }
    return _buildGrid();
  }

  Widget _buildGrid() {
    final config = _elementConfig;

    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: config.crossAxisCount,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
        childAspectRatio: config.aspectRatio,
      ),
      itemCount: _artworkList!.length,
      itemBuilder: (context, index) {
        final artwork = _artworkList![index];
        final thumbUrl = artwork['thumb'] as String?;
        final isSelected = artwork['selected'] == true;

        return FocusableWrapper(
          borderRadius: 8,
          onSelect: () => _selectArtwork(artwork),
          child: GestureDetector(
            onTap: () => _selectArtwork(artwork),
            child: Stack(
              fit: StackFit.expand,
              children: [
                Container(
                  decoration: BoxDecoration(
                    color: Theme.of(context).colorScheme.surfaceContainerHighest,
                    borderRadius: const BorderRadius.all(Radius.circular(8)),
                  ),
                  child: ClipRRect(
                    borderRadius: const BorderRadius.all(Radius.circular(8)),
                    child: OptimizedMediaImage(client: widget.client, imagePath: thumbUrl, fit: BoxFit.contain),
                  ),
                ),
                if (isSelected)
                  Positioned(
                    right: 6,
                    bottom: 6,
                    child: Container(
                      padding: const EdgeInsets.all(4),
                      decoration: BoxDecoration(color: Theme.of(context).colorScheme.primary, shape: BoxShape.circle),
                      child: Icon(Symbols.check_rounded, size: 16, color: Theme.of(context).colorScheme.onPrimary),
                    ),
                  ),
              ],
            ),
          ),
        );
      },
    );
  }
}
