/// Utility-funktioner och domänlogik för kvittoimport.
///
/// Separerade från UI-lagret för att möjliggöra testning och återanvändning.
library;

import '../../admin/domain/admin_category_node.dart';

// ── Enhetskonvertering ────────────────────────────────────────────────────────

// Alla massvärden normaliseras till gram (g).
const _massToGrams = <String, double>{
  'mg': 0.001,
  'g': 1.0,
  'hg': 100.0,
  'kg': 1000.0,
};

// Alla volymvärden normaliseras till milliliter (ml).
const _volToMl = <String, double>{
  'ml': 1.0,
  'cl': 10.0,
  'dl': 100.0,
  'l': 1000.0,
};

/// Konverterar [quantity] från [fromUnit] till [toUnit].
///
/// Stöder mass (mg/g/hg/kg) och volym (ml/cl/dl/l).
/// Returnerar null om konverteringen inte kan göras.
double? convertQuantity(double quantity, String fromUnit, String toUnit) {
  final from = fromUnit.trim().toLowerCase();
  final to = toUnit.trim().toLowerCase();
  if (from.isEmpty || to.isEmpty) return null;
  if (from == to) return quantity;

  // Massa
  if (_massToGrams.containsKey(from) && _massToGrams.containsKey(to)) {
    return quantity * _massToGrams[from]! / _massToGrams[to]!;
  }

  // Volym
  if (_volToMl.containsKey(from) && _volToMl.containsKey(to)) {
    return quantity * _volToMl[from]! / _volToMl[to]!;
  }

  return null;
}

// ── Paketenhetskänning ────────────────────────────────────────────────────────

const _packageUnits = <String>{
  'paket', 'forpackning', 'forp', 'forp.', 'förpackning',
  'förp', 'förp.', 'fp', 'pkt', 'pack', 'pak', 'st', 'styck',
};

bool isPackageLikeUnit(String? unit) {
  if (unit == null) return false;
  return _packageUnits.contains(unit.trim().toLowerCase());
}

// ── Storleksextraktion från rånamn ────────────────────────────────────────────

({double packQuantity, String packUnit})? extractPackageSizeFromRawName(
  String rawName,
) {
  final match = RegExp(
    r'(\d+(?:[\.,]\d+)?)\s*(ml|cl|dl|l|g|kg)\b',
    caseSensitive: false,
  ).firstMatch(rawName);
  if (match == null) return null;
  final value = double.tryParse(match.group(1)!.replaceAll(',', '.'));
  final sizeUnit = match.group(2)!.toLowerCase();
  if (value == null) return null;
  return (packQuantity: value, packUnit: sizeUnit);
}

double? extractMultipackCountFromRawName(String rawName) {
  final match = RegExp(
    r'(\d+)\s*[x×]\s*\d+(?:[\.,]\d+)?\s*(ml|cl|dl|l|g|kg)\b',
    caseSensitive: false,
  ).firstMatch(rawName);
  if (match == null) return null;
  return double.tryParse(match.group(1)!);
}

double? extractPieceCountFromRawName(String rawName) {
  final match = RegExp(
    r'(\d+(?:[\.,]\d+)?)\s*st\b',
    caseSensitive: false,
  ).firstMatch(rawName);
  if (match == null) return null;
  return double.tryParse(match.group(1)!.replaceAll(',', '.'));
}

// ── Paketfältsinferens ────────────────────────────────────────────────────────

typedef PackageFields = ({
  double? packQuantity,
  String? packUnit,
  double packageCount,
  double? totalQuantity,
  String? totalUnit,
});

PackageFields inferPackageFields({
  required String rawName,
  required double? quantity,
  required String? unit,
}) {
  final normalizedUnit = unit?.trim().toLowerCase();
  final safeCount = (quantity != null && quantity > 0) ? quantity : 1.0;
  final extracted = extractPackageSizeFromRawName(rawName);
  final multipackCount = extractMultipackCountFromRawName(rawName);
  final pieceCount = extractPieceCountFromRawName(rawName);

  // Exempel: "SALAMI PEPPAR 150G 2st" ska ge pack=150g och antal förpackningar=2.
  if (extracted != null && pieceCount != null && pieceCount > 0) {
    return (
      packQuantity: extracted.packQuantity,
      packUnit: extracted.packUnit,
      packageCount: pieceCount,
      totalQuantity: extracted.packQuantity * pieceCount,
      totalUnit: extracted.packUnit,
    );
  }

  // Om rånamnet innehåller storlek (t.ex. "5dl") och enhet saknas eller är
  // paketliknande — använd extraherad storlek.
  if (extracted != null &&
      (normalizedUnit == null ||
          normalizedUnit.isEmpty ||
          isPackageLikeUnit(normalizedUnit))) {
    final packageCount = multipackCount != null && multipackCount > 0
        ? multipackCount
        : safeCount;
    return (
      packQuantity: extracted.packQuantity,
      packUnit: extracted.packUnit,
      packageCount: packageCount,
      totalQuantity: extracted.packQuantity * packageCount,
      totalUnit: extracted.packUnit,
    );
  }

  if (quantity == null || normalizedUnit == null || normalizedUnit.isEmpty) {
    return (
      packQuantity: null,
      packUnit: null,
      packageCount: 1,
      totalQuantity: quantity,
      totalUnit: unit,
    );
  }

  if (isPackageLikeUnit(normalizedUnit) && extracted != null) {
    final packageCount = pieceCount != null && pieceCount > 0
        ? pieceCount
        : quantity;
    return (
      packQuantity: extracted.packQuantity,
      packUnit: extracted.packUnit,
      packageCount: packageCount,
      totalQuantity: extracted.packQuantity * packageCount,
      totalUnit: extracted.packUnit,
    );
  }

  return (
    packQuantity: quantity,
    packUnit: normalizedUnit,
    packageCount: 1,
    totalQuantity: quantity,
    totalUnit: normalizedUnit,
  );
}

// ── Talformatering ────────────────────────────────────────────────────────────

String formatCompactNumber(double value) {
  if (value == value.roundToDouble()) return value.toStringAsFixed(0);
  return value
      .toStringAsFixed(3)
      .replaceFirst(RegExp(r'0+$'), '')
      .replaceFirst(RegExp(r'\.$'), '');
}

String formatSwedishNumber(double value) =>
    formatCompactNumber(value).replaceAll('.', ',');

// ── Namnomvandling ────────────────────────────────────────────────────────────

/// Konverterar VERSALER-produktnamn till Title Case:
/// - Token med `/` lämnas oförändrade (t.ex. KY/KAL)
/// - Token som börjar med siffra görs lowercase (t.ex. 284g)
/// - Övriga token: Första bokstav versal, resten gemen
String normalizeProductName(String raw) {
  return raw.trim().split(' ').map((token) {
    if (token.isEmpty) return token;
    if (token.contains('/')) return token;
    if (RegExp(r'^\d').hasMatch(token)) return token.toLowerCase();
    return token[0].toUpperCase() + token.substring(1).toLowerCase();
  }).join(' ');
}

// ── Kategoriträd-lookup ───────────────────────────────────────────────────────

/// Hjälpklass för snabb lookup av kategori-sökväg via index.
///
/// Bygg en gång från trädet och återanvänd för alla rader.
class CategoryLookup {
  final Map<int, String> _pathByid;

  CategoryLookup._(this._pathByid);

  factory CategoryLookup.fromTree(List<AdminCategoryNode> tree) {
    final map = <int, String>{};
    void walk(List<AdminCategoryNode> nodes, List<String> parents) {
      for (final node in nodes) {
        final path = [...parents, node.name];
        map[node.id] = path.join(' > ');
        walk(node.children, path);
      }
    }
    walk(tree, const []);
    return CategoryLookup._(map);
  }

  /// Returnerar full sökväg för [categoryId], eller null om okänd.
  String? pathFor(int? categoryId) =>
      categoryId == null ? null : _pathByid[categoryId];

  bool get isEmpty => _pathByid.isEmpty;
}
