import 'dart:convert';
import 'dart:io';

import 'package:carddex_demo/native/phash_loader.dart';
import 'package:carddex_demo/services/hash_library.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as p;

const _imageExts = {'.jpg', '.jpeg', '.png', '.webp', '.bmp'};

/// Same pipeline as CardCropper.cropAndCorrect so library hashes match
/// the same conditions that query hashes are computed under.
const _cropW = 280;
const _cropH = 390;
const _borderTrim = 10;

Future<void> main(List<String> args) async {
  final root = Directory.current.path;
  final assetsDir = Directory(p.join(root, 'test_assets'));
  final outHashes = p.join(root, 'data', 'hashes.json');
  final outCatalog = p.join(root, 'data', 'catalog.json');
  final libPath = _resolveLib(root, args);

  if (!assetsDir.existsSync()) {
    stderr.writeln('test_assets/ not found at ${assetsDir.path}');
    exit(1);
  }

  final native = PhashNative(hostLibraryPath: libPath);
  final files = assetsDir
      .listSync()
      .whereType<File>()
      .where((f) => _imageExts.contains(p.extension(f.path).toLowerCase()))
      .toList()
    ..sort((a, b) => p.basename(a.path).compareTo(p.basename(b.path)));

  stdout.writeln('hashgen — ${files.length} images from ${assetsDir.path}');
  stdout.writeln('libphash — $libPath');

  final catalogItems = <Map<String, String>>[];
  final hashItems = <HashEntry>[];

  for (final file in files) {
    final filename = p.basename(file.path);
    final cardId = p.basenameWithoutExtension(filename);
    try {
      final raw = await file.readAsBytes();
      var decoded = img.decodeImage(raw);
      if (decoded == null) {
        stderr.writeln('skip $cardId: decode failed');
        continue;
      }
      decoded = img.bakeOrientation(decoded);
      // Match the App pipeline: resize → trim border → re-encode JPEG q=98.
      var resized = img.copyResize(decoded,
          width: _cropW, height: _cropH,
          interpolation: img.Interpolation.cubic);
      resized = img.copyCrop(resized,
          x: _borderTrim,
          y: _borderTrim,
          width: _cropW - _borderTrim * 2,
          height: _cropH - _borderTrim * 2);
      final hashedBytes = img.encodeJpg(resized, quality: 98);

      final hash = native.hashFromBytes(hashedBytes);
      hashItems.add(HashEntry(cardId: cardId, hash: hash, filename: filename));
      catalogItems.add({
        'card_id': cardId,
        'name': cardId,
        'image': 'test_assets/$filename',
        'filename': filename,
      });
      stdout.writeln('  $cardId');
    } catch (e) {
      stderr.writeln('skip $cardId: $e');
    }
  }

  final library = HashLibrary(
    version: 'assets-v1',
    algorithm: 'rgb-phash-v1',
    hashBits: 105,
    items: hashItems,
  );
  await library.saveFile(outHashes);

  final catalog = {
    'version': 'assets-v1',
    'algorithm': 'rgb-phash-v1',
    'items': catalogItems,
  };
  final catalogFile = File(outCatalog);
  await catalogFile.parent.create(recursive: true);
  await catalogFile.writeAsString(
    '${const JsonEncoder.withIndent('  ').convert(catalog)}\n',
  );

  stdout.writeln('saved ${hashItems.length} hashes → $outHashes');
  stdout.writeln('catalog → $outCatalog');
}

String _resolveLib(String root, List<String> args) {
  for (var i = 0; i < args.length; i++) {
    if (args[i] == '--lib' && i + 1 < args.length) {
      return p.normalize(p.absolute(args[i + 1]));
    }
  }
  final dylib = p.join(root, 'native', 'phash', 'build', 'libphash.dylib');
  final so = p.join(root, 'native', 'phash', 'build', 'libphash.so');
  if (File(dylib).existsSync()) return dylib;
  if (File(so).existsSync()) return so;
  stderr.writeln(
    'libphash not found. Build first:\n'
    '  cmake -S native/phash -B native/phash/build -DPHASH_BUILD_SHARED=ON\n'
    '  cmake --build native/phash/build',
  );
  exit(1);
}
