import 'dart:io';
import 'dart:typed_data';

import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:tane/db/database.dart';
import 'package:tane/db/encrypted_executor.dart';

/// Security regression guard for "no plaintext at rest, ever" (CLAUDE.md).
///
/// Writes a Variety whose label is a unique sentinel, then reads the raw
/// database file bytes and asserts the sentinel does NOT appear — proving
/// SQLCipher actually encrypted the data.
///
/// Requires a real SQLCipher library. On a host without it (no
/// `libsqlcipher.so`), the test is skipped with a clear reason; CI installs
/// SQLCipher so the guard runs there. It also runs on-device via integration
/// tests.
void main() {
  test('sentinel label is absent from the raw encrypted DB file', () async {
    const sentinel = 'GRANDMA_TOMATO_SENTINEL_9f3a1c';
    const keyHex =
        '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff';

    final dir = await Directory.systemTemp.createTemp('tane_enc_test');
    final file = File(p.join(dir.path, 'inventory.sqlite'));
    addTearDown(() => dir.delete(recursive: true));

    final db = AppDatabase(openEncryptedExecutor(file, keyHex));
    try {
      await db
          .into(db.varieties)
          .insert(
            VarietiesCompanion.insert(
              id: 'sentinel-row',
              label: sentinel,
              createdAt: 0,
              updatedAt: '0',
              lastAuthor: 'test',
            ),
          );
    } on Object catch (e) {
      // SQLCipher not available in this environment (e.g. dev host without
      // libsqlcipher). Skip rather than pass silently.
      markTestSkipped('SQLCipher unavailable, cannot verify encryption: $e');
      await db.close();
      return;
    }
    await db.close();

    final bytes = await file.readAsBytes();
    expect(
      _containsAscii(bytes, sentinel),
      isFalse,
      reason: 'Label leaked as plaintext — SQLCipher encryption is not active.',
    );
    // Sanity: a real SQLCipher file does not start with the plain-SQLite header.
    expect(
      _containsAscii(bytes.sublist(0, 16), 'SQLite format 3'),
      isFalse,
      reason: 'File has a plaintext SQLite header — not encrypted.',
    );
  });
}

bool _containsAscii(Uint8List haystack, String needle) {
  final n = needle.codeUnits;
  if (n.isEmpty || haystack.length < n.length) return false;
  for (var i = 0; i <= haystack.length - n.length; i++) {
    var match = true;
    for (var j = 0; j < n.length; j++) {
      if (haystack[i + j] != n[j]) {
        match = false;
        break;
      }
    }
    if (match) return true;
  }
  return false;
}
