import 'package:file_picker/file_picker.dart';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import '../../../core/api/api_paths.dart';
import '../../../core/api/api_providers.dart';
import '../../../core/ui/category_then_product_picker.dart';
import '../../admin/domain/admin_category_node.dart';
import '../../../core/utils/pdf_opener.dart';
import '../../auth/data/auth_providers.dart';
import '../../shopping_list/data/shopping_list_providers.dart';
import '../data/flyer_import_session.dart';
import '../data/import_providers.dart';
import '../domain/flyer_import_item.dart';
import '../domain/flyer_import_result.dart';
import 'error_dialog.dart';

class FlyerImportTab extends ConsumerStatefulWidget {
  const FlyerImportTab({super.key});

  @override
  ConsumerState<FlyerImportTab> createState() => _FlyerImportTabState();
}

class _FlyerImportTabState extends ConsumerState<FlyerImportTab> {
  bool _isLoading = false;
  bool _isSaving = false;
  PlatformFile? _pickedFile;
  Uint8List? _restoredSourceBytes;
  List<AdminCategoryNode> _categoryTree = const [];
  FlyerImportResult? _result;
  final Map<int, bool> _selected = {};

  @override
  void initState() {
    super.initState();
    _loadCategoryTree();
    _restoreSession();
  }

  Future<void> _loadCategoryTree() async {
    try {
      final token = await ref.read(authStateProvider.future);
      final api = ref.read(apiClientProvider);
      final categoryData =
          await api.getJson(CategoryApiPaths.tree, token: token);
      final categoryList = categoryData is List<dynamic>
          ? categoryData
          : (categoryData is Map<String, dynamic> &&
                  categoryData['items'] is List<dynamic>)
              ? categoryData['items'] as List<dynamic>
              : const <dynamic>[];
      final tree = categoryList
          .map((e) =>
              AdminCategoryNode.fromJson(Map<String, dynamic>.from(e as Map)))
          .toList();
      if (!mounted) return;
      setState(() => _categoryTree = tree);
    } catch (_) {
      // Kategoriträdet är valfritt för att visa listan.
    }
  }

  Future<void> _restoreSession() async {
    final notifier = ref.read(flyerImportSessionProvider.notifier);
    await notifier.restore();
    var session = ref.read(flyerImportSessionProvider);

    final token = await ref.read(authStateProvider.future);
    final repo = ref.read(importRepositoryProvider);

    if (session?.sessionId != null) {
      try {
        final serverResult = await repo.getFlyerImportSession(
          sessionId: session!.sessionId!,
          token: token,
        );
        if (!mounted) return;
        final selected = session.selected.isEmpty
            ? {
                for (var i = 0; i < serverResult.items.length; i++)
                  i: serverResult.items[i].matchedProductId != null,
              }
            : session.selected;
        setState(() {
          _result = serverResult;
          _restoredSourceBytes = null;
          _selected
            ..clear()
            ..addAll(selected);
        });
        await _loadRestoredSourceIfNeeded(serverResult, token);
        notifier.setImportedResult(
          result: serverResult,
          selected: selected,
          fileName: session.fileName,
        );
        return;
      } catch (_) {
        // fallback nedan
      }
    }

    try {
      final latest = await repo.getLatestFlyerImportSession(token: token);
      if (!mounted) return;
      if (latest.sessionId != null && latest.items.isNotEmpty) {
        final selected = {
          for (var i = 0; i < latest.items.length; i++)
            i: latest.items[i].matchedProductId != null,
        };
        setState(() {
          _result = latest;
          _restoredSourceBytes = null;
          _selected
            ..clear()
            ..addAll(selected);
        });
        await _loadRestoredSourceIfNeeded(latest, token);
        notifier.setImportedResult(
          result: latest,
          selected: selected,
          fileName: session?.fileName,
        );
      } else if (session?.result != null) {
        setState(() {
          _result = session!.result;
          _selected
            ..clear()
            ..addAll(session.selected);
        });
      }
    } catch (_) {
      session = ref.read(flyerImportSessionProvider);
      if (!mounted || session?.result == null) return;
      setState(() {
        _result = session!.result;
        _restoredSourceBytes = null;
        _selected
          ..clear()
          ..addAll(session.selected);
      });
    }
  }

  Future<void> _loadRestoredSourceIfNeeded(
      FlyerImportResult result, String? token) async {
    if (result.sessionId == null || result.sourceAvailable != true) return;
    if (_pickedFile?.bytes != null) return;
    try {
      final repo = ref.read(importRepositoryProvider);
      final bytes = await repo.getFlyerSourceBytes(
          sessionId: result.sessionId!, token: token);
      if (!mounted) return;
      setState(() => _restoredSourceBytes = bytes);
    } catch (_) {
      if (!mounted) return;
      setState(() => _restoredSourceBytes = null);
    }
  }

  Future<void> _pickFile() async {
    final result = await FilePicker.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['pdf', 'txt', 'png', 'jpg', 'jpeg', 'webp'],
      withData: true,
    );
    if (result == null || result.files.isEmpty) return;
    setState(() => _pickedFile = result.files.first);
  }

  Future<void> _parseFlyer() async {
    final file = _pickedFile;
    if (file?.bytes == null) return;

    setState(() => _isLoading = true);
    try {
      final token = await ref.read(authStateProvider.future);
      final repo = ref.read(importRepositoryProvider);
      final parsed = await repo.importFlyerFile(
        bytes: file!.bytes!,
        filename: file.name,
        token: token,
      );

      if (!mounted) return;
      final selected = <int, bool>{};
      for (var i = 0; i < parsed.items.length; i++) {
        selected[i] = parsed.items[i].matchedProductId != null;
      }
      setState(() {
        _result = parsed;
        _restoredSourceBytes = null;
        _selected
          ..clear()
          ..addAll(selected);
      });
      ref.read(flyerImportSessionProvider.notifier).setImportedResult(
            result: parsed,
            selected: selected,
            fileName: file.name,
          );
    } catch (e) {
      if (mounted) showErrorDialog(context, 'Flyerimport misslyckades: $e');
    } finally {
      if (mounted) setState(() => _isLoading = false);
    }
  }

  Future<void> _planSelected() async {
    final result = _result;
    if (result?.sessionId == null) return;

    final itemsToSave = <Map<String, dynamic>>[];
    final selectedItemIds = <int>[];
    for (var i = 0; i < result!.items.length; i++) {
      final item = result.items[i];
      final isSelected = _selected[i] == true;
      if (!isSelected || item.flyerItemId == null) continue;
      selectedItemIds.add(item.flyerItemId!);
      itemsToSave.add({
        'itemId': item.flyerItemId,
        'plannedQuantity': 1,
        'plannedUnit': item.priceUnit,
        'priority': 'normal',
      });
    }

    if (itemsToSave.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Markera minst en rad att planera.')),
      );
      return;
    }

    setState(() => _isSaving = true);
    try {
      final token = await ref.read(authStateProvider.future);
      final repo = ref.read(importRepositoryProvider);
      final saved = await repo.createFlyerSelectionsBulk(
        sessionId: result.sessionId!,
        items: itemsToSave,
        token: token,
      );
      final shopping = await repo.planFlyerSelectionsToShoppingList(
        sessionId: result.sessionId!,
        itemIds: selectedItemIds,
        token: token,
      );
      if (!mounted) return;
      final created = (shopping['created'] as num?)?.toInt() ?? 0;
      final updated = (shopping['updated'] as num?)?.toInt() ?? 0;
      ref.invalidate(shoppingListItemsProvider);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(
              '${saved.length} planerade. Inköpslista: $created tillagda, $updated uppdaterade.'),
          action: SnackBarAction(
            label: 'Öppna',
            onPressed: () => context.go('/inkopslista'),
          ),
        ),
      );
    } catch (e) {
      if (mounted) showErrorDialog(context, 'Kunde inte planera varor: $e');
    } finally {
      if (mounted) setState(() => _isSaving = false);
    }
  }

  Future<void> _editItem(int index, FlyerImportItem item) async {
    final sessionId = _result?.sessionId;
    final itemId = item.flyerItemId;
    if (sessionId == null || itemId == null) return;

    final nameController = TextEditingController(text: item.rawName);
    int? selectedCategoryId = item.categoryId;
    String? selectedCategoryPath = item.category;

    final payload = await showDialog<
        ({String name, int? categoryId, String? categoryPath})>(
      context: context,
      builder: (context) {
        return StatefulBuilder(
          builder: (context, setLocalState) {
            return AlertDialog(
              title: const Text('Redigera rad'),
              content: SizedBox(
                width: 420,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    TextField(
                      controller: nameController,
                      decoration: const InputDecoration(
                        labelText: 'Namn',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 12),
                    Text(
                      selectedCategoryPath == null ||
                              selectedCategoryPath!.isEmpty
                          ? 'Ingen kategori vald'
                          : selectedCategoryPath!,
                    ),
                    const SizedBox(height: 8),
                    Wrap(
                      spacing: 8,
                      children: [
                        OutlinedButton.icon(
                          onPressed: _categoryTree.isEmpty
                              ? null
                              : () async {
                                  final selected =
                                      await CategoryThenProductPicker
                                          .showCategorySheet(
                                    context,
                                    categoryTree: _categoryTree,
                                    preselectedCategoryId: selectedCategoryId,
                                  );
                                  if (selected == null) return;
                                  setLocalState(() {
                                    selectedCategoryId = selected.id;
                                    selectedCategoryPath = selected.path;
                                  });
                                },
                          icon: const Icon(Icons.category_outlined),
                          label: const Text('Välj kategori'),
                        ),
                        TextButton(
                          onPressed: () {
                            setLocalState(() {
                              selectedCategoryId = null;
                              selectedCategoryPath = null;
                            });
                          },
                          child: const Text('Rensa'),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
              actions: [
                TextButton(
                  onPressed: () => Navigator.of(context).pop(),
                  child: const Text('Avbryt'),
                ),
                FilledButton(
                  onPressed: () {
                    Navigator.of(context).pop((
                      name: nameController.text.trim(),
                      categoryId: selectedCategoryId,
                      categoryPath: selectedCategoryPath,
                    ));
                  },
                  child: const Text('Spara'),
                ),
              ],
            );
          },
        );
      },
    );

    nameController.dispose();
    if (payload == null || payload.name.isEmpty) return;

    try {
      final token = await ref.read(authStateProvider.future);
      final repo = ref.read(importRepositoryProvider);
      final updated = await repo.updateFlyerSessionItem(
        sessionId: sessionId,
        itemId: itemId,
        rawName: payload.name,
        categoryId: payload.categoryId,
        token: token,
      );

      if (!mounted) return;
      final result = _result;
      if (result == null) return;
      final nextItems = [...result.items];
      nextItems[index] = updated;
      final nextResult = FlyerImportResult(
        sessionId: result.sessionId,
        items: nextItems,
        warnings: result.warnings,
        sourceAvailable: result.sourceAvailable,
        sourceFileName: result.sourceFileName,
        sourceMimeType: result.sourceMimeType,
        sourceFileSize: result.sourceFileSize,
      );
      setState(() {
        _result = nextResult;
      });
      ref.read(flyerImportSessionProvider.notifier).setImportedResult(
            result: nextResult,
            selected: Map<int, bool>.from(_selected),
            fileName: _pickedFile?.name ?? result.sourceFileName,
          );
    } catch (e) {
      if (!mounted) return;
      showErrorDialog(context, 'Kunde inte uppdatera rad: $e');
    }
  }

  String _formatPrice(double? price, String? unit) {
    if (price == null) return '';
    final raw = price.toStringAsFixed(2).replaceAll('.', ',');
    final unitPart =
        (unit != null && unit.trim().isNotEmpty) ? '/${unit.trim()}' : '';
    return '$raw kr$unitPart';
  }

  String _removeLimitTextFromOfferText(String offerText, String? limitText) {
    final trimmedOffer = offerText.trim();
    final trimmedLimit = limitText?.trim() ?? '';
    if (trimmedOffer.isEmpty || trimmedLimit.isEmpty) return trimmedOffer;

    final escaped = RegExp.escape(trimmedLimit);
    final withoutLimit = trimmedOffer
        .replaceAll(RegExp(escaped, caseSensitive: false), '')
        .trim();
    final withoutLeadingPunctuation =
        withoutLimit.replaceAll(RegExp(r'^[,.;:\s-]+'), '').trim();
    return withoutLeadingPunctuation
        .replaceAll(RegExp(r'[,.;:\s-]+$'), '')
        .trim();
  }

  Widget _buildOfferBadge(FlyerImportItem item, ThemeData theme) {
    final hasOffer = item.isOffer ||
        (item.offerText?.trim().isNotEmpty ?? false) ||
        item.price != null;
    if (!hasOffer) return const SizedBox.shrink();

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
      decoration: BoxDecoration(
        color: Colors.red.shade50,
        borderRadius: BorderRadius.circular(999),
        border: Border.all(color: Colors.red.shade200),
      ),
      child: Text(
        'ERBJUDANDE',
        style: theme.textTheme.labelSmall?.copyWith(
          color: Colors.red.shade900,
          fontWeight: FontWeight.w700,
        ),
      ),
    );
  }

  String _getQualityLevel(FlyerImportItem item) {
    final parseConf = item.parseConfidence ?? 0;
    final matchConf = item.matchConfidence ?? 0;
    final avgConf = (parseConf + matchConf) / 2;

    if (avgConf >= 0.80) return 'Hög';
    if (avgConf >= 0.60) return 'Medel';
    return 'Låg';
  }

  Color _getQualityColor(FlyerImportItem item) {
    final level = _getQualityLevel(item);
    if (level == 'Hög') return Colors.green.shade700;
    if (level == 'Medel') return Colors.orange.shade700;
    return Colors.red.shade700;
  }

  Widget _buildQualityBadge(FlyerImportItem item, ThemeData theme) {
    final level = _getQualityLevel(item);
    final color = _getQualityColor(item);

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.15),
        borderRadius: BorderRadius.circular(4),
        border: Border.all(color: color.withValues(alpha: 0.4)),
      ),
      child: Text(
        level,
        style: theme.textTheme.labelSmall?.copyWith(
          color: color,
          fontWeight: FontWeight.w600,
        ),
      ),
    );
  }

  Widget _buildWarningsPanel(ThemeData theme) {
    final warnings = _result?.warnings ?? const <String>[];
    if (warnings.isEmpty) return const SizedBox.shrink();

    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.amber.shade50,
        border: Border.all(color: Colors.amber.shade300),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Icon(Icons.warning_amber_rounded,
                  color: Colors.amber.shade800, size: 18),
              const SizedBox(width: 8),
              Text(
                'Varningar (${warnings.length})',
                style: theme.textTheme.labelMedium?.copyWith(
                  color: Colors.amber.shade900,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ],
          ),
          const SizedBox(height: 8),
          ...warnings.map((warning) => Padding(
                padding: const EdgeInsets.only(bottom: 4),
                child: Text(
                  '• $warning',
                  style: theme.textTheme.bodySmall?.copyWith(
                    color: Colors.amber.shade900,
                  ),
                ),
              )),
        ],
      ),
    );
  }

  Widget _buildFlyerPreview(ThemeData theme) {
    final file = _pickedFile;
    final bytes = file?.bytes ?? _restoredSourceBytes;
    if (bytes == null) return const SizedBox.shrink();

    final filename = file?.name ?? _result?.sourceFileName ?? '';
    final fallbackExt = filename.contains('.') ? filename.split('.').last : '';
    final ext = (file?.extension ?? fallbackExt).toLowerCase();
    final isImage = ['png', 'jpg', 'jpeg', 'webp', 'bmp'].contains(ext);

    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          ListTile(
            dense: true,
            leading: Icon(
              isImage ? Icons.image_outlined : Icons.picture_as_pdf_outlined,
              color: theme.colorScheme.primary,
            ),
            title: const Text('Flyerförhandsvisning'),
            subtitle: Text(filename),
            trailing: isImage
                ? null
                : OutlinedButton.icon(
                    icon: const Icon(Icons.open_in_new, size: 16),
                    label: const Text('Visa flyer'),
                    style: OutlinedButton.styleFrom(
                        visualDensity: VisualDensity.compact),
                    onPressed: () async {
                      final messenger = ScaffoldMessenger.of(context);
                      final opened = await openPdfBytes(bytes);
                      if (!context.mounted || opened) return;
                      messenger.showSnackBar(
                        const SnackBar(
                          content: Text(
                              'PDF kan bara öppnas direkt i webbversionen just nu.'),
                        ),
                      );
                    },
                  ),
          ),
          if (isImage)
            Padding(
              padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(8),
                child: ConstrainedBox(
                  constraints: const BoxConstraints(maxHeight: 420),
                  child: Image.memory(bytes, fit: BoxFit.contain),
                ),
              ),
            )
          else
            const SizedBox(height: 8),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final items = _result?.items ?? const <FlyerImportItem>[];
    final selectedCount = _selected.values.where((value) => value).length;

    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Ladda upp flyer, granska erbjudanden och planera inköp med ett klick.',
            style: theme.textTheme.bodyMedium,
          ),
          const SizedBox(height: 16),
          OutlinedButton.icon(
            onPressed: _isLoading ? null : _pickFile,
            icon: const Icon(Icons.attach_file),
            label: Text(_pickedFile?.name ?? 'Välj flyerfil'),
          ),
          const SizedBox(height: 12),
          FilledButton.icon(
            onPressed: (!_isLoading && _pickedFile?.bytes != null)
                ? _parseFlyer
                : null,
            icon: const Icon(Icons.auto_awesome),
            label: const Text('Importera flyer'),
          ),
          const SizedBox(height: 12),
          _buildFlyerPreview(theme),
          if (_isLoading) ...[
            const SizedBox(height: 12),
            const LinearProgressIndicator(),
          ],
          if (items.isNotEmpty) ...[
            const SizedBox(height: 20),
            _buildWarningsPanel(theme),
            if ((_result?.warnings ?? const []).isNotEmpty)
              const SizedBox(height: 12),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text('${items.length} rader hittades',
                    style: theme.textTheme.titleSmall),
                TextButton(
                  onPressed: () {
                    final target = selectedCount < items.length;
                    setState(() {
                      for (var i = 0; i < items.length; i++) {
                        _selected[i] = target;
                      }
                    });
                    ref
                        .read(flyerImportSessionProvider.notifier)
                        .setSelectedForAll(items.length, target);
                  },
                  child: Text(selectedCount < items.length
                      ? 'Välj alla'
                      : 'Avmarkera alla'),
                ),
              ],
            ),
            const SizedBox(height: 8),
            ...items.asMap().entries.map((entry) {
              final index = entry.key;
              final item = entry.value;
              final priceText = _formatPrice(item.price, item.priceUnit);
              final comparisonText =
                  _formatPrice(item.comparisonPrice, item.comparisonUnit);
              final limitText = item.offerLimitText?.trim();
              final sanitizedOfferText = item.offerText == null
                  ? ''
                  : _removeLimitTextFromOfferText(item.offerText!, limitText);

              return CheckboxListTile(
                value: _selected[index] ?? false,
                onChanged: (value) {
                  final checked = value ?? false;
                  setState(() => _selected[index] = checked);
                  ref
                      .read(flyerImportSessionProvider.notifier)
                      .setSelected(index, checked);
                },
                title: Row(
                  children: [
                    Expanded(child: Text(item.rawName)),
                    IconButton(
                      tooltip: 'Redigera',
                      visualDensity: VisualDensity.compact,
                      icon: const Icon(
                        Icons.edit_outlined,
                        size: 18,
                        semanticLabel: 'Redigera rad',
                      ),
                      onPressed: () => _editItem(index, item),
                    ),
                    const SizedBox(width: 8),
                    _buildQualityBadge(item, theme),
                    const SizedBox(width: 8),
                    _buildOfferBadge(item, theme),
                  ],
                ),
                subtitle: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    if (priceText.isNotEmpty) Text('Pris: $priceText'),
                    if ((item.category ?? '').trim().isNotEmpty)
                      Text('Kategori: ${item.category}'),
                    if (comparisonText.isNotEmpty)
                      Text('Jämförpris: $comparisonText'),
                    if (limitText != null && limitText.isNotEmpty)
                      Text(
                        'Begränsning: $limitText',
                        style: theme.textTheme.bodyMedium?.copyWith(
                          color: Colors.orange.shade900,
                          fontWeight: FontWeight.w600,
                        ),
                      ),
                    if (sanitizedOfferText.isNotEmpty) Text(sanitizedOfferText),
                    if (item.matchedProductName != null)
                      Text('Match: ${item.matchedProductName}'),
                  ],
                ),
                controlAffinity: ListTileControlAffinity.leading,
              );
            }),
            const SizedBox(height: 8),
            SizedBox(
              width: double.infinity,
              child: FilledButton.icon(
                onPressed:
                    (_isSaving || selectedCount == 0) ? null : _planSelected,
                icon: _isSaving
                    ? const SizedBox(
                        width: 16,
                        height: 16,
                        child: CircularProgressIndicator(
                            strokeWidth: 2, color: Colors.white),
                      )
                    : const Icon(Icons.playlist_add_check),
                label: Text('Planera $selectedCount markerade'),
              ),
            ),
          ],
        ],
      ),
    );
  }
}
