import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import '../../modules/search/grocery_item.dart';

// ─── Models ───────────────────────────────────────────────────────────────────

class StoreProduct {
  final int productId;
  final String productName;
  final String productImage;
  final double productCost;
  final double discountValue;
  final String productUnit;
  final String unitValue;
  final String productStatus;
  final int subcategoryId;
  final String subcategoryName;

  const StoreProduct({
    required this.productId,
    required this.productName,
    required this.productImage,
    required this.productCost,
    required this.discountValue,
    required this.productUnit,
    required this.unitValue,
    required this.productStatus,
    required this.subcategoryId,
    required this.subcategoryName,
  });

  factory StoreProduct.fromJson(Map<String, dynamic> json, String subcategoryName) {
    return StoreProduct(
      productId: json['productid'] ?? 0,
      productName: json['productname'] ?? '',
      productImage: json['productimage'] ?? '',
      productCost: (json['productcost'] ?? 0).toDouble(),
      discountValue: (json['discountvalue'] ?? 0).toDouble(),
      productUnit: json['productunit'] ?? '',
      unitValue: json['unitvalue'] ?? '',
      productStatus: json['productstatus'] ?? '',
      subcategoryId: json['subcategoryid'] ?? 0,
      subcategoryName: subcategoryName,
    );
  }

  bool get isAvailable => productStatus == 'available';

  double get finalPrice {
    if (discountValue <= 0) return productCost;
    return productCost - (productCost * discountValue / 100);
  }
}

class StoreInfo {
  final int tenantId;
  final String tenantName;
  final String address;
  final String suburb;
  final String city;
  final String latitude;
  final String longitude;
  final int locationId;
  final String locationName;
  final int categoryId;
  final int ordersCount;

  const StoreInfo({
    required this.tenantId,
    required this.tenantName,
    required this.address,
    required this.suburb,
    required this.city,
    required this.latitude,
    required this.longitude,
    required this.locationId,
    required this.locationName,
    required this.categoryId,
    required this.ordersCount,
  });

  factory StoreInfo.fromJson(Map<String, dynamic> json) {
    return StoreInfo(
      tenantId: json['tenantid'] ?? 0,
      tenantName: json['tenantname'] ?? '',
      address: json['address'] ?? '',
      suburb: json['suburb'] ?? '',
      city: json['city'] ?? '',
      latitude: json['latitude'] ?? '',
      longitude: json['longitude'] ?? '',
      locationId: json['locationid'] ?? 0,
      locationName: json['locationname'] ?? '',
      categoryId: json['categoryid'] ?? 0,
      ordersCount: json['orderscount'] ?? 0,
    );
  }
}

/// A single AI item matched to one or more store products across stores.
class MatchedItem {
  final GroceryItem aiItem;

  /// storeName → list of matched products in that store
  final Map<String, List<StoreProduct>> storeMatches;

  const MatchedItem({
    required this.aiItem,
    required this.storeMatches,
  });

  bool get hasMatches => storeMatches.isNotEmpty;

  int get totalMatchCount =>
      storeMatches.values.fold(0, (sum, list) => sum + list.length);
}

// ─── Controller ───────────────────────────────────────────────────────────────

class StoreMatchController extends ChangeNotifier {
  // ── Config ─────────────────────────────────────────────────────────────────

  static const int _customerId = 6060;
  static const double _latitude = 11.0050728;
  static const double _longitude = 76.9508513;

  static const String _baseUrl = 'https://fiesta.nearle.app/live/api/v1/mob';
  static const String _tenantsEndpoint = '$_baseUrl/tenants/getcustomertenants';
  static const String _productsEndpoint = '$_baseUrl/products/getproductsbysubcategory';

  static const String _groqApiKey =
      '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';
  static const String _groqUrl =
      'https://api.groq.com/openai/v1/chat/completions';

  // ── State ──────────────────────────────────────────────────────────────────

  bool loading = false;
  String? error;

  List<StoreInfo> stores = [];
  final Map<String, List<StoreProduct>> _storeProducts = {};
  List<MatchedItem> matchedResults = [];

  // ── Public entry point ────────────────────────────────────────────────────

  Future<void> matchAiItems(List<GroceryItem> aiItems) async {
    if (aiItems.isEmpty) return;

    loading = true;
    error = null;
    matchedResults = [];
    notifyListeners();

    try {
      // Step 1 — fetch all nearby stores
      await _fetchStores();

      if (stores.isEmpty) {
        error = 'No stores found nearby.';
        loading = false;
        notifyListeners();
        return;
      }

      // Step 2 — fetch products for every store concurrently
      await _fetchAllStoreProducts();

      // Step 3 — AI matching
      await _performAiMatching(aiItems);

      // Step 4 — print results
      _printMatchResults();

    } catch (e, st) {
      error = 'Something went wrong: $e';
      debugPrint('[StoreMatch] Error: $e\n$st');
    } finally {
      loading = false;
      notifyListeners();
    }
  }

  // ── Step 1: Fetch stores ──────────────────────────────────────────────────

  Future<void> _fetchStores() async {
    final uri = Uri.parse(_tenantsEndpoint).replace(queryParameters: {
      'customerid': '$_customerId',
      'tenant': '0',
      'latitude': '$_latitude',
      'longitude': '$_longitude',
      'categoryid': '0',
    });

    debugPrint('[StoreMatch] Fetching stores → $uri');

    final res = await http.get(uri);
    if (res.statusCode != 200) {
      throw Exception('Stores API error ${res.statusCode}');
    }

    final json = jsonDecode(res.body) as Map<String, dynamic>;
    if (json['status'] != true) {
      throw Exception('Stores API returned status=false: ${json['message']}');
    }

    final details = (json['details'] as List?) ?? [];

    final seen = <String>{};
    stores = details
        .map((d) => StoreInfo.fromJson(d as Map<String, dynamic>))
        .where((s) {
      final key = '${s.tenantId}_${s.locationId}';
      return seen.add(key);
    }).toList();

    debugPrint('[StoreMatch] Fetched ${stores.length} store-location(s).');
  }

  // ── Step 2: Fetch products for every store ────────────────────────────────

  Future<void> _fetchAllStoreProducts() async {
    await Future.wait(stores.map(_fetchProductsForStore));
  }

  Future<void> _fetchProductsForStore(StoreInfo store) async {
    final storeKey = '${store.tenantId}_${store.locationId}';

    final uri = Uri.parse(_productsEndpoint).replace(queryParameters: {
      'categoryid': '${store.categoryId == 0 ? 2 : store.categoryId}',
      'tenantid': '${store.tenantId}',
      'locationid': '${store.locationId}',
    });

    debugPrint('[StoreMatch] Fetching products for ${store.tenantName} → $uri');

    try {
      final res = await http.get(uri);
      if (res.statusCode != 200) return;

      final json = jsonDecode(res.body) as Map<String, dynamic>;
      if (json['status'] != true || json['data'] == null) return;

      final details = (json['data']['details'] as List?) ?? [];

      final products = <StoreProduct>[];
      for (final cat in details) {
        final subcatName = (cat['subcategoryname'] as String?) ?? '';
        final productList = (cat['products'] as List?) ?? [];
        for (final p in productList) {
          products.add(
            StoreProduct.fromJson(p as Map<String, dynamic>, subcatName),
          );
        }
      }

      _storeProducts[storeKey] = products;
      debugPrint(
        '[StoreMatch] ${store.tenantName} (${store.locationName}): '
            '${products.length} products loaded.',
      );
    } catch (e) {
      debugPrint('[StoreMatch] Failed to fetch products for $storeKey: $e');
    }
  }

  // ── Step 3: AI matching ───────────────────────────────────────────────────

  Future<void> _performAiMatching(List<GroceryItem> aiItems) async {
    // Build the AI item list string
    final aiItemsList = aiItems
        .map((item) => '- ${item.name} (${item.quantity})')
        .join('\n');

    // Build the store products list string
    // Format: "storeName — locationName|productName"
    final productLines = <String>[];
    for (final store in stores) {
      final storeKey = '${store.tenantId}_${store.locationId}';
      final products = _storeProducts[storeKey] ?? [];
      final storeLabel = '${store.tenantName} — ${store.locationName}';
      for (final p in products) {
        productLines.add('$storeLabel|${p.productName}');
      }
    }

    if (productLines.isEmpty) {
      debugPrint('[StoreMatch] No store products to match against.');
      matchedResults = aiItems
          .map((item) => MatchedItem(aiItem: item, storeMatches: {}))
          .toList();
      return;
    }

    final productListString = productLines.join('\n');

    debugPrint('[StoreMatch] Sending ${aiItems.length} AI items and '
        '${productLines.length} store products to Groq for matching...');

    final prompt = '''
You are a strict grocery product matching engine.

RULES:
- Match each AI item ONLY to products whose name is genuinely the same item.
- "Chicken Biryani" must ONLY match products containing "biryani" or "chicken biryani".
- "Curd" must NEVER match "Biryani". "Eggs" must NEVER match "Rice".
- Be flexible with brand names: "Basmati Rice" matches "India Gate Basmati Rice 1kg".
- If you are not confident, return an empty matches array.
- The "matches" array must contain EXACT product names copied from the STORE PRODUCTS list below. Do NOT paraphrase or modify them.
- Return ONLY a raw JSON array. No markdown, no explanation, nothing else.

Each element must have:
- "aiItem": the exact AI item name from the list below
- "matches": array of exact product name strings from the store products list

═══════════════════════
AI ITEMS (user is looking for):
$aiItemsList

═══════════════════════
STORE PRODUCTS (format: storeName|productName):
$productListString
═══════════════════════

Return format example:
[
  {"aiItem": "Chicken Biryani", "matches": ["Chicken Biryani (Full)", "Biryani Chicken"]},
  {"aiItem": "Eggs", "matches": ["Farm Fresh Eggs 6pcs"]},
  {"aiItem": "Pizza", "matches": []}
]
''';

    try {
      final res = await http.post(
        Uri.parse(_groqUrl),
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer $_groqApiKey',
        },
        body: jsonEncode({
          'model': 'llama-3.3-70b-versatile',
          'max_tokens': 1000,
          'messages': [
            {'role': 'user', 'content': prompt},
          ],
        }),
      );

      if (res.statusCode != 200) {
        throw Exception('Groq matching API error ${res.statusCode}: ${res.body}');
      }

      final data = jsonDecode(res.body) as Map<String, dynamic>;
      final content = data['choices'][0]['message']['content'] as String;

      // Extract JSON array from response
      final start = content.indexOf('[');
      final end = content.lastIndexOf(']') + 1;
      if (start == -1 || end == 0) {
        throw Exception('No JSON array in Groq matching response');
      }

      final List parsed = jsonDecode(content.substring(start, end)) as List;

      // Build productName (lowercase) → (storeLabel, StoreProduct) lookup
      // Key: "storeName — locationName|productname_lowercase"
      // This avoids cross-store collisions if two stores have the same product name
      final productLookup = <String, MapEntry<String, StoreProduct>>{};
      for (final store in stores) {
        final storeKey = '${store.tenantId}_${store.locationId}';
        final products = _storeProducts[storeKey] ?? [];
        final storeLabel = '${store.tenantName} — ${store.locationName}';
        for (final p in products) {
          final lookupKey = '$storeLabel|${p.productName.toLowerCase().trim()}';
          productLookup[lookupKey] = MapEntry(storeLabel, p);
        }
      }

      // Build MatchedItem list from AI response
      matchedResults = aiItems.map((aiItem) {
        final entry = parsed.firstWhere(
              (e) =>
          (e['aiItem'] as String).toLowerCase().trim() ==
              aiItem.name.toLowerCase().trim(),
          orElse: () => null,
        );

        if (entry == null) {
          return MatchedItem(aiItem: aiItem, storeMatches: {});
        }

        final matchedNames = ((entry['matches'] as List?) ?? [])
            .map((e) => (e as String).trim())
            .toList();

        // Group matched products by store name
        final storeMatches = <String, List<StoreProduct>>{};

        for (final matchedName in matchedNames) {
          final matchedNameLower = matchedName.toLowerCase();

          // Search across all stores for this product name
          for (final store in stores) {
            final storeKey = '${store.tenantId}_${store.locationId}';
            final storeLabel = '${store.tenantName} — ${store.locationName}';
            final lookupKey = '$storeLabel|$matchedNameLower';

            final found = productLookup[lookupKey];
            if (found != null) {
              storeMatches
                  .putIfAbsent(found.key, () => [])
                  .add(found.value);
            }
          }
        }

        return MatchedItem(aiItem: aiItem, storeMatches: storeMatches);
      }).toList();

    } catch (e, st) {
      debugPrint('[StoreMatch] AI matching failed: $e\n$st');
      // Fallback: return all items with no matches rather than crashing
      matchedResults = aiItems
          .map((item) => MatchedItem(aiItem: item, storeMatches: {}))
          .toList();
    }
  }

  // ── Step 4: Print results ─────────────────────────────────────────────────

  void _printMatchResults() {
    debugPrint('\n${'═' * 60}');
    debugPrint('  AI ITEM → STORE MATCH RESULTS (AI-powered)');
    debugPrint('${'═' * 60}');

    for (final result in matchedResults) {
      debugPrint('\n▶ AI Item: "${result.aiItem.name}" '
          '(${result.aiItem.quantity}) ${result.aiItem.emoji}');

      if (!result.hasMatches) {
        debugPrint('  ✗ No matching products found in any store.');
        continue;
      }

      result.storeMatches.forEach((storeName, products) {
        debugPrint('  ✔ $storeName');
        for (final p in products) {
          final discount = p.discountValue > 0
              ? ' [${p.discountValue.toStringAsFixed(0)}% off → ₹${p.finalPrice.toStringAsFixed(2)}]'
              : '';
          final status = p.isAvailable ? '✅' : '⚠ outofstock';
          debugPrint(
            '      • ${p.productName} | ₹${p.productCost}'
                '$discount | ${p.productUnit} | $status',
          );
        }
      });
    }

    debugPrint('\n${'═' * 60}');
    final matchedCount = matchedResults.where((r) => r.hasMatches).length;
    debugPrint(
      '  SUMMARY: ${matchedCount}/${matchedResults.length} AI items matched '
          'across ${stores.length} stores.',
    );
    debugPrint('${'═' * 60}\n');
  }

  // ── Helpers ───────────────────────────────────────────────────────────────

  void reset() {
    stores = [];
    _storeProducts.clear();
    matchedResults = [];
    error = null;
    loading = false;
    notifyListeners();
  }

  List<MatchedItem> get foundItems =>
      matchedResults.where((r) => r.hasMatches).toList();

  List<MatchedItem> get notFoundItems =>
      matchedResults.where((r) => !r.hasMatches).toList();
}