// Rich theme picker — grid of swatched theme tiles, plus a
// custom-seed-color tile that opens a small color picker
// dialog. Replaces the dropdown that used to live inside the
// Settings dialog's Theme section.
//
// The picker writes through StudioApp.setThemePlugin. Built-in
// = null. Installed plugin = its capability identifier
// (e.g. studio.theme.solarized). Custom = `custom:#RRGGBB`.
// See main.dart's _pluginThemes for the parsing side.

import 'package:flutter/material.dart';

import '../data/theme_plugin.dart';
import '../l10n/app_localizations.dart';
import '../main.dart';
import '../theme/tokens.dart';

class ThemePickerGrid extends StatefulWidget {
  const ThemePickerGrid({super.key});

  @override
  State<ThemePickerGrid> createState() => _ThemePickerGridState();
}

class _ThemePickerGridState extends State<ThemePickerGrid> {
  late Future<List<String>> _capsFuture;
  // Per-capability cached schemes for the tile previews. We
  // load them lazily once the list arrives so the grid
  // doesn't block while every plugin's theme_for() round-
  // trips. A null entry means "loaded, no scheme" — the
  // tile falls back to a generic palette.
  final Map<String, ThemePluginSchemes?> _previews = {};

  @override
  void initState() {
    super.initState();
    _capsFuture = listThemePluginCapabilities();
  }

  Future<void> _ensurePreview(String cap) async {
    if (_previews.containsKey(cap)) return;
    _previews[cap] = null;
    final schemes = await loadThemePluginSchemes(cap);
    if (!mounted) return;
    setState(() => _previews[cap] = schemes);
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final l = AppLocalizations.of(context)!;
    final app = StudioApp.of(context);
    return FutureBuilder<List<String>>(
      future: _capsFuture,
      builder: (context, snap) {
        if (snap.connectionState != ConnectionState.done) {
          return const Padding(
            padding: EdgeInsets.symmetric(vertical: ChainSpace.sm),
            child: SizedBox(
              width: 14,
              height: 14,
              child: CircularProgressIndicator(strokeWidth: 2),
            ),
          );
        }
        final caps = snap.data ?? const <String>[];
        // Trigger preview loads for any plugin we haven't
        // fetched yet — best-effort, fire-and-forget.
        for (final cap in caps) {
          if (!_previews.containsKey(cap)) {
            // ignore: discarded_futures
            _ensurePreview(cap);
          }
        }
        final active = app?.themePluginNotifier.value;
        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                _builtInTile(theme, l, active),
                for (final cap in caps) _pluginTile(theme, cap, active),
                _customTile(theme, l, active),
              ],
            ),
            const SizedBox(height: ChainSpace.sm),
            Text(
              l.themePluginHint,
              style: theme.textTheme.bodySmall?.copyWith(
                color: theme.colorScheme.onSurfaceVariant,
              ),
            ),
          ],
        );
      },
    );
  }

  Widget _builtInTile(ThemeData theme, AppLocalizations l, String? active) {
    return _Tile(
      label: l.themePluginNone,
      swatches: [
        theme.colorScheme.primary,
        theme.colorScheme.secondary,
        theme.colorScheme.tertiary,
      ],
      selected: active == null,
      onTap: () {
        StudioApp.of(context)?.setThemePlugin(null);
        _confirmApplied(l, l.themePluginNone);
        setState(() {});
      },
    );
  }

  /// Brief snackbar acknowledging the theme switch. The picker
  /// applies themes instantly (no Save), so the operator needs
  /// a visible cue that the click was registered — without it,
  /// a slow theme load looks like "the click did nothing".
  void _confirmApplied(AppLocalizations l, String displayName) {
    final messenger = ScaffoldMessenger.maybeOf(context);
    if (messenger == null) return;
    messenger.removeCurrentSnackBar();
    messenger.showSnackBar(
      SnackBar(
        content: Text(l.themeApplied(displayName)),
        duration: const Duration(seconds: 2),
        behavior: SnackBarBehavior.floating,
      ),
    );
  }

  Widget _pluginTile(ThemeData theme, String cap, String? active) {
    // Pretty name: studio.theme.sunflower → "Sunflower".
    final display = _displayName(cap);
    final preview = _previews[cap];
    // Swatches come from the plugin's matching-brightness
    // scheme so the tile literally previews what selecting
    // it does. Fall back to the active theme's accents
    // while the plugin's preview hasn't loaded yet.
    final isDark = theme.brightness == Brightness.dark;
    final scheme = isDark ? preview?.dark : preview?.light;
    final swatches = scheme != null
        ? <Color>[scheme.primary, scheme.secondary, scheme.tertiary]
        : <Color>[
            theme.colorScheme.primary,
            theme.colorScheme.secondary,
            theme.colorScheme.tertiary,
          ];
    return _Tile(
      label: display,
      swatches: swatches,
      selected: active == cap,
      loading: preview == null,
      onTap: () {
        StudioApp.of(context)?.setThemePlugin(cap);
        _confirmApplied(AppLocalizations.of(context)!, display);
        setState(() {});
      },
    );
  }

  Widget _customTile(ThemeData theme, AppLocalizations l, String? active) {
    final isCustom = active != null && active.startsWith('custom:');
    final seedHex = isCustom ? active.substring('custom:'.length) : null;
    final seed = seedHex != null
        ? _parseHexColor(seedHex)
        : theme.colorScheme.primary;
    // Material 3 derives a full ColorScheme from one seed —
    // primary, secondary, tertiary, surface, outline, on-*.
    // The custom tile previews that full palette so it reads
    // visually as a complete theme, NOT a single colour.
    final scheme = ColorScheme.fromSeed(
      seedColor: seed ?? theme.colorScheme.primary,
      brightness: theme.brightness,
    );
    return _Tile(
      label: l.themePluginCustom,
      swatches: [scheme.primary, scheme.secondary, scheme.tertiary],
      selected: isCustom,
      // Slight visual cue that this tile is a different
      // *source* of theme (seed-derived) compared to the
      // plugin tiles (hand-curated palette). Same widget, the
      // dashed border separates the concept.
      editable: true,
      onTap: () async {
        final picked = await _showColorPickerDialog(
          context,
          initial: seed ?? theme.colorScheme.primary,
        );
        if (picked == null || !mounted) return;
        final hex = picked.toARGB32().toRadixString(16).padLeft(8, '0');
        // Strip alpha component — we only persist the RGB.
        final rgb = hex.substring(2).toUpperCase();
        StudioApp.of(context)?.setThemePlugin('custom:#$rgb');
        _confirmApplied(l, '#$rgb');
        setState(() {});
      },
    );
  }

  String _displayName(String cap) {
    // studio.theme.glass-apple → "Glass Apple"
    final last = cap.split('.').last;
    return last
        .split(RegExp(r'[-_]'))
        .map((w) => w.isEmpty ? w : '${w[0].toUpperCase()}${w.substring(1)}')
        .join(' ');
  }
}

Color? _parseHexColor(String s) {
  final cleaned = s.replaceAll('#', '');
  if (cleaned.length != 6) return null;
  final raw = int.tryParse(cleaned, radix: 16);
  if (raw == null) return null;
  return Color(0xFF000000 | raw);
}

class _Tile extends StatelessWidget {
  final String label;
  final List<Color> swatches;
  final bool selected;
  final bool loading;

  /// Adds a small tune icon at the top-right, telegraphing
  /// "this tile opens an editor instead of applying
  /// immediately". Used for the Custom tile so the operator
  /// knows clicking it will open the colour-picker dialog.
  final bool editable;
  final VoidCallback onTap;
  const _Tile({
    required this.label,
    required this.swatches,
    required this.selected,
    required this.onTap,
    this.loading = false,
    this.editable = false,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return SizedBox(
      width: 132,
      height: 76,
      child: Material(
        color: theme.colorScheme.surfaceContainerHigh,
        borderRadius: BorderRadius.circular(ChainRadius.sm),
        child: InkWell(
          onTap: onTap,
          borderRadius: BorderRadius.circular(ChainRadius.sm),
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(ChainRadius.sm),
              border: Border.all(
                color: selected
                    ? theme.colorScheme.primary
                    : theme.colorScheme.outlineVariant,
                width: selected ? 2 : 1,
              ),
            ),
            padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  children: [
                    for (final c in swatches) ...[
                      Container(
                        width: 16,
                        height: 16,
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          color: c,
                          border: Border.all(
                            color: theme.colorScheme.outline.withValues(
                              alpha: 0.4,
                            ),
                            width: 0.5,
                          ),
                        ),
                      ),
                      const SizedBox(width: 4),
                    ],
                    if (loading)
                      const SizedBox(
                        width: 10,
                        height: 10,
                        child: CircularProgressIndicator(strokeWidth: 1.5),
                      ),
                  ],
                ),
                Row(
                  children: [
                    Expanded(
                      child: Text(
                        label,
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                        style: theme.textTheme.bodySmall?.copyWith(
                          fontWeight: selected
                              ? FontWeight.w600
                              : FontWeight.w500,
                          color: theme.colorScheme.onSurface,
                        ),
                      ),
                    ),
                    if (editable && !selected)
                      Icon(
                        Icons.tune,
                        size: 14,
                        color: theme.colorScheme.onSurfaceVariant,
                      ),
                    if (selected)
                      Icon(
                        Icons.check_circle,
                        size: 14,
                        color: theme.colorScheme.primary,
                      ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Future<Color?> _showColorPickerDialog(
  BuildContext context, {
  required Color initial,
}) {
  return showDialog<Color>(
    context: context,
    builder: (ctx) => _ColorPickerDialog(initial: initial),
  );
}

class _ColorPickerDialog extends StatefulWidget {
  final Color initial;
  const _ColorPickerDialog({required this.initial});
  @override
  State<_ColorPickerDialog> createState() => _ColorPickerDialogState();
}

class _ColorPickerDialogState extends State<_ColorPickerDialog> {
  late Color _color = widget.initial;
  late final TextEditingController _hex = TextEditingController(
    text: _hexOf(widget.initial),
  );

  /// Curated 12-colour palette — primary Material 3 hues at
  /// the 500 step, picked so each is visually distinct + each
  /// yields a usable ColorScheme.fromSeed. The operator can
  /// override via the hex field below.
  static const List<int> _presets = [
    0xFF1976D2, // Blue
    0xFF00ACC1, // Cyan
    0xFF43A047, // Green
    0xFFFFB300, // Amber
    0xFFF4511E, // Deep Orange
    0xFFE53935, // Red
    0xFFD81B60, // Pink
    0xFF8E24AA, // Purple
    0xFF5E35B1, // Deep Purple
    0xFF3949AB, // Indigo
    0xFF00897B, // Teal
    0xFF6D4C41, // Brown
  ];

  String _hexOf(Color c) =>
      '#${c.toARGB32().toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}';

  void _pickPreset(int argb) {
    setState(() {
      _color = Color(argb);
      _hex.text = _hexOf(_color);
    });
  }

  void _commitHex(String input) {
    final cleaned = input.replaceAll('#', '').trim();
    if (cleaned.length != 6) return;
    final raw = int.tryParse(cleaned, radix: 16);
    if (raw == null) return;
    setState(() => _color = Color(0xFF000000 | raw));
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final l = AppLocalizations.of(context)!;
    final preview = ColorScheme.fromSeed(
      seedColor: _color,
      brightness: theme.brightness,
    );
    return AlertDialog(
      title: Text(l.themePluginCustomDialogTitle),
      content: SizedBox(
        width: 360,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                for (final argb in _presets)
                  GestureDetector(
                    onTap: () => _pickPreset(argb),
                    child: Container(
                      width: 28,
                      height: 28,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Color(argb),
                        border: Border.all(
                          color: _color.toARGB32() == argb
                              ? theme.colorScheme.primary
                              : theme.colorScheme.outlineVariant,
                          width: _color.toARGB32() == argb ? 2.5 : 1,
                        ),
                      ),
                    ),
                  ),
              ],
            ),
            const SizedBox(height: ChainSpace.md),
            TextField(
              controller: _hex,
              decoration: InputDecoration(
                labelText: l.themePluginCustomHexLabel,
                hintText: '#1976D2',
                isDense: true,
                border: const OutlineInputBorder(),
              ),
              onChanged: _commitHex,
              onSubmitted: _commitHex,
            ),
            const SizedBox(height: ChainSpace.md),
            Text(l.themePluginCustomPreview, style: theme.textTheme.labelSmall),
            const SizedBox(height: 6),
            Row(
              children: [
                _swatch(preview.primary, 'P'),
                _swatch(preview.secondary, 'S'),
                _swatch(preview.tertiary, 'T'),
                _swatch(preview.surface, '∎'),
                _swatch(preview.error, '!'),
              ],
            ),
          ],
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text(l.themePluginCustomCancel),
        ),
        FilledButton(
          onPressed: () => Navigator.pop(context, _color),
          child: Text(l.themePluginCustomApply),
        ),
      ],
    );
  }

  Widget _swatch(Color c, String letter) {
    final theme = Theme.of(context);
    return Padding(
      padding: const EdgeInsets.only(right: 6),
      child: Container(
        width: 28,
        height: 28,
        alignment: Alignment.center,
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          color: c,
          border: Border.all(
            color: theme.colorScheme.outlineVariant,
            width: 0.5,
          ),
        ),
        child: Text(
          letter,
          style: TextStyle(
            color: ThemeData.estimateBrightnessForColor(c) == Brightness.dark
                ? Colors.white
                : Colors.black,
            fontSize: 10,
            fontWeight: FontWeight.w700,
            fontFamily: 'monospace',
          ),
        ),
      ),
    );
  }
}
