import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../core/api/api_error_mapper.dart';
import '../../../core/l10n/l10n.dart';
import '../data/admin_repository.dart';
import '../domain/admin_ai_categorize_result.dart';
import '../domain/admin_category_node.dart';
import '../domain/admin_product.dart';

enum _ProductSort {
  newest,
  oldest,
  nameAsc,
  nameDesc,
  categoryAsc,
  categoryDesc,
}

class AdminProductsPanel extends ConsumerStatefulWidget {
  final bool embedded;

  const AdminProductsPanel({super.key, this.embedded = false});

  @override
  ConsumerState<AdminProductsPanel> createState() =>
      _AdminProductsPanelState();
}

class _AdminProductsPanelState extends ConsumerState<AdminProductsPanel> {
  bool _isLoading = true;
  bool _isApplying = false;
  bool _isAiRunning = false;
  String? _error;
  String _search = '';
  _ProductSort _sort = _ProductSort.newest;
  bool _showDeletedOnly = false;
  bool _showUncategorizedOnly = false;
  String? _bulkCategoryValue;
  List<AdminProduct> _products = [];
  List<AdminCategoryNode> _categories = [];
  final Set<int> _selectedIds = <int>{};
  final Map<int, String> _rowCategoryValue = <int, String>{};
  final Set<int> _rowCategorySaving = <int>{};

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

  Future<void> _load() async {
    setState(() {
      _isLoading = true;
      _error = null;
    });
    try {
      final results = await Future.wait<dynamic>([
        _showDeletedOnly
            ? ref.read(adminRepositoryProvider).listDeletedProducts()
            : ref.read(adminRepositoryProvider).listProducts(),
        ref.read(adminRepositoryProvider).listCategoryTree(),
      ]);
      if (!mounted) return;
      setState(() {
        _products = results[0] as List<AdminProduct>;
        _categories = results[1] as List<AdminCategoryNode>;
        _rowCategoryValue.clear();
        _rowCategorySaving.clear();
      });
    } catch (e) {
      if (!mounted) return;
      setState(() => _error = mapErrorToUserMessage(e, context));
    } finally {
      if (mounted) setState(() => _isLoading = false);
    }
  }

  String _sortLabel(_ProductSort sort) {
    switch (sort) {
      case _ProductSort.newest:
        return context.l10n.adminSortNewest;
      case _ProductSort.oldest:
        return context.l10n.adminSortOldest;
      case _ProductSort.nameAsc:
        return context.l10n.adminSortNameAsc;
      case _ProductSort.nameDesc:
        return context.l10n.adminSortNameDesc;
      case _ProductSort.categoryAsc:
        return context.l10n.adminSortCategoryAsc;
      case _ProductSort.categoryDesc:
        return context.l10n.adminSortCategoryDesc;
    }
  }

  List<({String value, String label})> _flattenCategories(
    List<AdminCategoryNode> nodes, [
    int depth = 0,
  ]) {
    final result = <({String value, String label})>[];
    final sorted = [...nodes]
      ..sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
    for (final node in sorted) {
      final prefix = depth == 0 ? '' : '${'  ' * depth}↳ ';
      result.add((value: node.id.toString(), label: '$prefix${node.name}'));
      result.addAll(_flattenCategories(node.children, depth + 1));
    }
    return result;
  }

  Future<void> _applyBulkCategory() async {
    if (_selectedIds.isEmpty || _isApplying) return;
    setState(() => _isApplying = true);
    try {
      await ref.read(adminRepositoryProvider).bulkSetCategory(
            _selectedIds.toList(),
            categoryId: _bulkCategoryValue == null ||
                    _bulkCategoryValue == '__remove__'
                ? null
                : int.parse(_bulkCategoryValue!),
          );
      if (!mounted) return;
      setState(() {
        _selectedIds.clear();
        _bulkCategoryValue = null;
      });
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(context.l10n.adminProductsUpdated)),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    } finally {
      if (mounted) setState(() => _isApplying = false);
    }
  }

  Future<void> _runAiCategorize() async {
    if (_isAiRunning || _showDeletedOnly) return;
    setState(() => _isAiRunning = true);
    try {
      final suggestions = await ref.read(adminRepositoryProvider).aiCategorizeBulk(
            productIds: _selectedIds.isEmpty ? null : _selectedIds.toList(),
          );
      if (!mounted) return;
      if (suggestions.isEmpty) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(context.l10n.adminNoAiSuggestions)),
        );
        return;
      }

      final selectedProductIds = await showDialog<Set<int>>(
        context: context,
        builder: (_) => _AiApplyDialog(suggestions: suggestions),
      );
      if (selectedProductIds == null || selectedProductIds.isEmpty) return;

      final grouped = <int, List<int>>{};
      for (final row in suggestions) {
        if (!selectedProductIds.contains(row.productId)) continue;
        grouped.putIfAbsent(row.categoryId, () => <int>[]).add(row.productId);
      }
      for (final entry in grouped.entries) {
        await ref.read(adminRepositoryProvider).bulkSetCategory(
              entry.value,
              categoryId: entry.key,
            );
      }
      if (!mounted) return;
      setState(() => _selectedIds.clear());
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(context.l10n.adminAiApplied(selectedProductIds.length))),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    } finally {
      if (mounted) setState(() => _isAiRunning = false);
    }
  }

  Future<void> _mergeSelected() async {
    if (_selectedIds.length != 2 || _showDeletedOnly) return;
    final ids = _selectedIds.toList();
    final first = ids[0];
    final second = ids[1];
    var sourceId = first;
    final optionToTarget = <int, int>{
      first: second,
      second: first,
    };

    final confirmed = await showDialog<bool>(
      context: context,
      builder: (dialogContext) {
        return StatefulBuilder(
          builder: (dialogContext, setDialogState) => AlertDialog(
            title: Text(context.l10n.adminMergeProductsTitle),
            content: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(context.l10n.adminMergeProductsHint),
                const SizedBox(height: 12),
                SegmentedButton<int>(
                  segments: [
                    ButtonSegment<int>(
                      value: first,
                      label: Text(_nameForId(first)),
                    ),
                    ButtonSegment<int>(
                      value: second,
                      label: Text(_nameForId(second)),
                    ),
                  ],
                  selected: <int>{sourceId},
                  onSelectionChanged: (selection) {
                    if (selection.isEmpty) return;
                    setDialogState(() => sourceId = selection.first);
                  },
                ),
                const SizedBox(height: 12),
                Text(
                  'Källa: ${_nameForId(sourceId)}\nMål: ${_nameForId(optionToTarget[sourceId]!)}',
                ),
              ],
            ),
            actions: [
              TextButton(
                onPressed: () => Navigator.pop(dialogContext, false),
                child: Text(context.l10n.cancelAction),
              ),
              FilledButton(
                onPressed: () => Navigator.pop(dialogContext, true),
                child: Text(context.l10n.adminMergeAction),
              ),
            ],
          ),
        );
      },
    );

    if (confirmed != true || !mounted) return;
    final targetId = sourceId == first ? second : first;
    try {
      await ref.read(adminRepositoryProvider).mergeProducts(
            sourceProductId: sourceId,
            targetProductId: targetId,
          );
      if (!mounted) return;
      setState(() => _selectedIds.clear());
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(context.l10n.adminProductsMerged)),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    }
  }

  Future<void> _removeProduct(AdminProduct product) async {
    if (_showDeletedOnly) return;
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        title: Text(context.l10n.adminDeleteProductTitle),
        content: Text(context.l10n.adminDeleteProductConfirm(product.displayName)),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(dialogContext, false),
            child: Text(context.l10n.cancelAction),
          ),
          FilledButton(
            onPressed: () => Navigator.pop(dialogContext, true),
            child: Text(context.l10n.deleteAction),
          ),
        ],
      ),
    );
    if (confirmed != true || !mounted) return;

    try {
      await ref.read(adminRepositoryProvider).removeProduct(product.id);
      if (!mounted) return;
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(context.l10n.adminProductDeleted)),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    }
  }

  Future<void> _restoreSelected() async {
    if (_selectedIds.isEmpty || !_showDeletedOnly || _isApplying) return;
    setState(() => _isApplying = true);
    try {
      for (final id in _selectedIds.toList()) {
        await ref.read(adminRepositoryProvider).restoreProduct(id);
      }
      if (!mounted) return;
      setState(() => _selectedIds.clear());
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Valda produkter återställda.')),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    } finally {
      if (mounted) setState(() => _isApplying = false);
    }
  }

  Future<void> _restoreProduct(AdminProduct product) async {
    try {
      await ref.read(adminRepositoryProvider).restoreProduct(product.id);
      if (!mounted) return;
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Produkt återställd.')),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
    }
  }

  String _rowCategoryFor(AdminProduct product) {
    return _rowCategoryValue[product.id] ?? (product.categoryId?.toString() ?? '__remove__');
  }

  bool _rowCategoryChanged(AdminProduct product) {
    final selected = _rowCategoryFor(product);
    final categoryId = selected == '__remove__' ? null : int.tryParse(selected);
    return categoryId != product.categoryId;
  }

  Future<void> _saveRowCategory(AdminProduct product) async {
    if (_showDeletedOnly || _rowCategorySaving.contains(product.id)) return;
    final selected = _rowCategoryFor(product);
    final categoryId = selected == '__remove__' ? null : int.tryParse(selected);
    if (categoryId == product.categoryId) return;

    setState(() => _rowCategorySaving.add(product.id));
    try {
      await ref.read(adminRepositoryProvider).setProductCategory(
            product.id,
            categoryId: categoryId,
          );
      if (!mounted) return;
      await _load();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Kategori uppdaterad för ${product.displayName}.')),
      );
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(mapErrorToUserMessage(e, context))),
      );
      setState(() => _rowCategorySaving.remove(product.id));
    }
  }

  String _nameForId(int id) {
    final match = _products.where((p) => p.id == id).toList();
    if (match.isEmpty) return '#$id';
    return match.first.displayName;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final categoryOptions = _flattenCategories(_categories);
    final filtered = _products.where((product) {
      if (!_showDeletedOnly && _showUncategorizedOnly && product.categoryId != null) {
        return false;
      }
      final query = _search.trim().toLowerCase();
      if (query.isEmpty) return true;
      return product.id.toString().contains(query) ||
          product.name.toLowerCase().contains(query) ||
          (product.canonicalName ?? '').toLowerCase().contains(query) ||
          (product.normalizedName ?? '').toLowerCase().contains(query) ||
          (product.categoryPath ?? '').toLowerCase().contains(query);
    }).toList()
      ..sort((a, b) {
        switch (_sort) {
          case _ProductSort.newest:
            return b.id.compareTo(a.id);
          case _ProductSort.oldest:
            return a.id.compareTo(b.id);
          case _ProductSort.nameAsc:
            return a.displayName.toLowerCase().compareTo(b.displayName.toLowerCase());
          case _ProductSort.nameDesc:
            return b.displayName.toLowerCase().compareTo(a.displayName.toLowerCase());
          case _ProductSort.categoryAsc:
            return (a.categoryPath ?? '').toLowerCase().compareTo((b.categoryPath ?? '').toLowerCase());
          case _ProductSort.categoryDesc:
            return (b.categoryPath ?? '').toLowerCase().compareTo((a.categoryPath ?? '').toLowerCase());
        }
      });

    if (_isLoading) return const Center(child: CircularProgressIndicator());
    if (_error != null) {
      return Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(_error!, style: TextStyle(color: theme.colorScheme.error)),
            const SizedBox(height: 16),
            FilledButton(onPressed: _load, child: Text(context.l10n.retryAction)),
          ],
        ),
      );
    }

    final content = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        TextField(
          decoration: InputDecoration(
            labelText: context.l10n.adminSearchProduct,
            prefixIcon: const Icon(Icons.search),
            border: const OutlineInputBorder(),
          ),
          onChanged: (value) => setState(() => _search = value),
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            DropdownButton<_ProductSort>(
              value: _sort,
              items: _ProductSort.values
                  .map(
                    (value) => DropdownMenuItem<_ProductSort>(
                      value: value,
                      child: Text(_sortLabel(value)),
                    ),
                  )
                  .toList(),
              onChanged: (value) {
                if (value == null) return;
                setState(() => _sort = value);
              },
            ),
            FilterChip(
              label: Text(context.l10n.adminShowDeleted),
              selected: _showDeletedOnly,
              onSelected: (value) {
                setState(() {
                  _showDeletedOnly = value;
                  _showUncategorizedOnly = false;
                  _selectedIds.clear();
                  _bulkCategoryValue = null;
                });
                _load();
              },
            ),
            FilterChip(
              label: Text(context.l10n.adminShowUncategorized),
              selected: _showUncategorizedOnly,
              onSelected: _showDeletedOnly
                  ? null
                  : (value) => setState(() => _showUncategorizedOnly = value),
            ),
            if (!_showDeletedOnly)
              SizedBox(
                width: 260,
                child: DropdownButtonFormField<String>(
                  initialValue: _bulkCategoryValue,
                  decoration: InputDecoration(
                    labelText: context.l10n.adminBulkSetCategory,
                    border: const OutlineInputBorder(),
                  ),
                  items: [
                    DropdownMenuItem<String>(
                      value: '__remove__',
                      child: Text(context.l10n.adminRemoveCategory),
                    ),
                    ...categoryOptions.map(
                      (option) => DropdownMenuItem<String>(
                        value: option.value,
                        child: Text(option.label),
                      ),
                    ),
                  ],
                  onChanged: (value) => setState(() => _bulkCategoryValue = value),
                ),
              ),
            if (!_showDeletedOnly)
              FilledButton(
                onPressed: _selectedIds.isEmpty || _isApplying
                    ? null
                    : _applyBulkCategory,
                child: _isApplying
                    ? const SizedBox(
                        height: 18,
                        width: 18,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : Text('Uppdatera valda (${_selectedIds.length})'),
              ),
            if (!_showDeletedOnly)
              FilledButton.tonal(
                onPressed: _isAiRunning ? null : _runAiCategorize,
                child: _isAiRunning
                    ? const SizedBox(
                        height: 18,
                        width: 18,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : Text(
                        _selectedIds.isEmpty
                            ? 'AI-kategorisera okategoriserade'
                            : 'AI-kategorisera valda (${_selectedIds.length})',
                      ),
              ),
            if (!_showDeletedOnly)
              FilledButton.tonal(
                onPressed: _selectedIds.length == 2 ? _mergeSelected : null,
                child: const Text('Slå ihop 2 valda'),
              ),
            if (_showDeletedOnly)
              FilledButton.tonal(
                onPressed: _selectedIds.isEmpty || _isApplying ? null : _restoreSelected,
                child: _isApplying
                    ? const SizedBox(
                        height: 18,
                        width: 18,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                  : Text(context.l10n.adminRestoreSelected(_selectedIds.length))
              ),
          ],
        ),
        const SizedBox(height: 16),
        if (filtered.isEmpty)
          Text(context.l10n.adminNoProductsFound)
        else
          ...filtered.map(
            (product) => Card(
              child: Column(
                children: [
                  CheckboxListTile(
                    value: _selectedIds.contains(product.id),
                    onChanged: (checked) {
                      setState(() {
                        if (checked == true) {
                          _selectedIds.add(product.id);
                        } else {
                          _selectedIds.remove(product.id);
                        }
                      });
                    },
                    title: Text(product.displayName),
                    subtitle: Text(
                      [
                        if (product.displayName != product.name)
                          'Original: ${product.name}',
                        'Kategori: ${product.categoryPath ?? 'Saknas'}',
                        if (product.status != null) 'Status: ${product.status}',
                        if (_showDeletedOnly && product.deletedAt != null)
                          'Raderad: ${product.deletedAt}',
                        'ID: ${product.id}',
                      ].join('\n'),
                    ),
                    isThreeLine: true,
                    controlAffinity: ListTileControlAffinity.leading,
                  ),
                  Padding(
                    padding: const EdgeInsets.only(right: 12, bottom: 10),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        if (!_showDeletedOnly)
                          Expanded(
                            child: Row(
                              children: [
                                SizedBox(
                                  width: 280,
                                  child: DropdownButtonFormField<String>(
                                    key: ValueKey(
                                      'row-category-${product.id}-${_rowCategoryFor(product)}',
                                    ),
                                    initialValue: _rowCategoryFor(product),
                                    decoration: InputDecoration(
                                      labelText: context.l10n.adminInlineCategory,
                                      border: const OutlineInputBorder(),
                                      isDense: true,
                                    ),
                                    items: [
                                      DropdownMenuItem<String>(
                                        value: '__remove__',
                                        child: Text(context.l10n.adminNoCategory),
                                      ),
                                      ...categoryOptions.map(
                                        (option) => DropdownMenuItem<String>(
                                          value: option.value,
                                          child: Text(option.label),
                                        ),
                                      ),
                                    ],
                                    onChanged: (value) {
                                      if (value == null) return;
                                      setState(() => _rowCategoryValue[product.id] = value);
                                    },
                                  ),
                                ),
                                const SizedBox(width: 8),
                                FilledButton.tonalIcon(
                                  onPressed: _rowCategoryChanged(product) &&
                                          !_rowCategorySaving.contains(product.id)
                                      ? () => _saveRowCategory(product)
                                      : null,
                                  icon: _rowCategorySaving.contains(product.id)
                                      ? const SizedBox(
                                          height: 16,
                                          width: 16,
                                          child: CircularProgressIndicator(strokeWidth: 2),
                                        )
                                      : const Icon(Icons.save_outlined),
                                  label: Text(context.l10n.saveAction),
                                ),
                              ],
                            ),
                          )
                        else
                          const SizedBox.shrink(),
                        const SizedBox(width: 8),
                        if (_showDeletedOnly)
                          TextButton.icon(
                            onPressed: () => _restoreProduct(product),
                            icon: const Icon(Icons.restore),
                            label: Text(context.l10n.adminRestoreAction),
                          )
                        else
                          TextButton.icon(
                            onPressed: () => _removeProduct(product),
                            icon: const Icon(Icons.delete_outline),
                            label: Text(context.l10n.deleteAction),
                          ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
      ],
    );

    if (!widget.embedded) {
      return ListView(
        padding: const EdgeInsets.all(16),
        children: [content],
      );
    }

    return content;
  }
}

class _AiApplyDialog extends StatefulWidget {
  final List<AdminAiCategorizeResult> suggestions;

  const _AiApplyDialog({required this.suggestions});

  @override
  State<_AiApplyDialog> createState() => _AiApplyDialogState();
}

class _AiApplyDialogState extends State<_AiApplyDialog> {
  late final Set<int> _selected;

  @override
  void initState() {
    super.initState();
    _selected = widget.suggestions.map((e) => e.productId).toSet();
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(context.l10n.adminAiSuggestionsTitle),
      content: SizedBox(
        width: 700,
        child: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ...widget.suggestions.map(
                (row) => CheckboxListTile(
                  value: _selected.contains(row.productId),
                  onChanged: (checked) {
                    setState(() {
                      if (checked == true) {
                        _selected.add(row.productId);
                      } else {
                        _selected.remove(row.productId);
                      }
                    });
                  },
                  title: Text(row.productName),
                  subtitle: Text(
                    [
                      'Kategori: ${row.categoryPath}',
                      'Confidence: ${row.confidence}',
                      if (row.usedFallback) 'Fallback använd',
                      'Produkt-ID: ${row.productId}',
                    ].join('\n'),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text(context.l10n.cancelAction),
        ),
        FilledButton(
          onPressed: _selected.isEmpty
              ? null
              : () => Navigator.pop(context, _selected),
          child: Text(context.l10n.adminApplySelected(_selected.length)),
        ),
      ],
    );
  }
}