#!/usr/bin/env php
<?php
/**
 * Sync foto wajah dari folder (hasil download Google Drive) ke writable/faces/{student_id}.jpg
 * dan generate embedding (vektor) lalu simpan ke student_faces — alur proper untuk verifikasi wajah.
 *
 * Pemakaian: php sync_face_photos.php --source=C:\path\to\folder [--no-embedding]
 * Konvensi: nama file mengandung NISN (contoh: 1234567890.jpg atau 01. 1234567890.jpg)
 * Lihat: backend/docs/SYNC_FOTO_WAJAH.md
 */

$options = getopt('', ['source:', 'no-embedding']);
$source  = $options['source'] ?? null;
$noEmbedding = isset($options['no-embedding']);
if (empty($source) || ! is_dir($source)) {
    echo "Pemakaian: php sync_face_photos.php --source=/path/to/folder [--no-embedding]\n";
    echo "  --no-embedding  Skip generate vektor (hanya copy file + face_hash).\n";
    echo "Folder harus berisi subfolder (XII-1, XII-2, dll) dengan file gambar bernama NISN.jpg\n";
    exit(1);
}

$backendRoot = dirname(__DIR__);
$envFile     = $backendRoot . DIRECTORY_SEPARATOR . '.env';
if (! is_file($envFile)) {
    $envFile = $backendRoot . DIRECTORY_SEPARATOR . 'env';
}
$env = [];
if (is_file($envFile)) {
    $lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || strpos($line, '#') === 0) {
            continue;
        }
        if (strpos($line, '=') !== false) {
            [$key, $val] = explode('=', $line, 2);
            $key = trim($key);
            $val = trim($val, " \t\"'");
            $env[$key] = $val;
        }
    }
}

$hostname = $env['database.default.hostname'] ?? $env['database_default_hostname'] ?? 'localhost';
$database = $env['database.default.database'] ?? $env['database_default_database'] ?? '';
$username = $env['database.default.username'] ?? $env['database_default_username'] ?? 'root';
$password = $env['database.default.password'] ?? $env['database_default_password'] ?? '';
$port     = (int) ($env['database.default.port'] ?? $env['database_default_port'] ?? 3306);

if ($database === '') {
    echo "Database name tidak ditemukan di .env\n";
    exit(1);
}

try {
    $dsn = "mysql:host={$hostname};port={$port};dbname={$database};charset=utf8mb4";
    $pdo = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (Throwable $e) {
    echo "Koneksi DB gagal: " . $e->getMessage() . "\n";
    exit(1);
}

$facesDir = $backendRoot . DIRECTORY_SEPARATOR . 'writable' . DIRECTORY_SEPARATOR . 'faces';
if (! is_dir($facesDir)) {
    mkdir($facesDir, 0755, true);
    echo "Folder dibuat: {$facesDir}\n";
}

$faceServiceUrl   = rtrim($env['FACE_SERVICE_URL'] ?? $env['face_service_url'] ?? 'http://localhost:5000', '/');
$embeddingDim     = (int) ($env['FACE_EMBEDDING_DIM'] ?? $env['face_embedding_dim'] ?? 512);
$minFaceSize      = (float) ($env['FACE_MIN_SIZE'] ?? $env['face_min_size'] ?? 80);
$minBlur          = (float) ($env['FACE_MIN_BLUR'] ?? $env['face_min_blur'] ?? 30);
$minBrightness    = (float) ($env['FACE_MIN_BRIGHTNESS'] ?? $env['face_min_brightness'] ?? 0.2);

/**
 * Generate embedding dari file gambar lalu simpan ke student_faces (source=formal).
 * Menggantikan embedding formal yang sudah ada untuk student_id ini.
 * Return: 'ok' | 'skip' | 'fail'
 */
$generateAndSaveEmbedding = function (PDO $pdo, string $destPath, int $studentId) use ($faceServiceUrl, $embeddingDim, $minFaceSize, $minBlur, $minBrightness): string {
    if (! is_file($destPath)) {
        return 'fail';
    }
    $mime = mime_content_type($destPath) ?: 'image/jpeg';
    $ch = curl_init($faceServiceUrl . '/embed');
    if ($ch === false) {
        return 'fail';
    }
    $cfile = new CURLFile($destPath, $mime, basename($destPath));
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => ['image' => $cfile],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_HTTPHEADER     => [],
    ]);
    $response = curl_exec($ch);
    $status   = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($response === false || $status !== 200) {
        return 'fail';
    }
    $data = json_decode($response, true);
    if (! is_array($data) || empty($data['embedding']) || ! is_array($data['embedding'])) {
        return 'fail';
    }
    $embedding = $data['embedding'];
    if (count($embedding) !== $embeddingDim) {
        return 'skip';
    }
    $facesCount = (int) ($data['faces_count'] ?? 0);
    $faceSize   = (float) ($data['face_size'] ?? 0);
    $blur       = (float) ($data['blur'] ?? 0);
    $brightness = (float) ($data['brightness'] ?? 0);
    if ($facesCount !== 1) {
        return 'skip';
    }
    if ($faceSize < $minFaceSize || $blur < $minBlur || $brightness < $minBrightness) {
        return 'skip';
    }
    $qualityScore = isset($data['quality_score']) ? (float) $data['quality_score'] : null;
    $embeddingJson = json_encode(array_map('floatval', $embedding));

    $pdo->prepare('DELETE FROM student_faces WHERE student_id = ? AND source = ?')->execute([$studentId, 'formal']);
    $ins = $pdo->prepare('INSERT INTO student_faces (student_id, embedding, source, quality_score, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())');
    $ins->execute([$studentId, $embeddingJson, 'formal', $qualityScore]);
    return 'ok';
};

$extensions = ['jpg', 'jpeg', 'png'];
$countOk    = 0;
$countSkip  = 0;
$countFail  = 0;
$countEmbedOk   = 0;
$countEmbedSkip = 0;
$countEmbedFail = 0;

$it = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
    RecursiveIteratorIterator::SELF_FIRST
);

foreach ($it as $file) {
    if (! $file->isFile()) {
        continue;
    }
    $path = $file->getPathname();
    $ext  = strtolower($file->getExtension());
    if (! in_array($ext, $extensions, true)) {
        continue;
    }

    // Nama file bisa berupa "1234567890.jpg" atau "01. 1234567890.jpg"
    $baseName = preg_replace('/\.(jpg|jpeg|png)$/i', '', $file->getFilename());
    $baseName = trim($baseName);
    if ($baseName === '') {
        continue;
    }

    // Ambil deretan digit minimal 8 angka pertama sebagai NISN
    $nisn = null;
    if (preg_match('/(\d{8,})/', $baseName, $m) === 1) {
        $nisn = $m[1];
    }
    if ($nisn === null || $nisn === '') {
        echo "  [skip] Tidak ditemukan NISN di nama file: {$baseName} ({$path})\n";
        $countSkip++;
        continue;
    }

    $stmt = $pdo->prepare('SELECT id FROM students WHERE nisn = ? LIMIT 1');
    $stmt->execute([$nisn]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (! $row) {
        echo "  [skip] NISN tidak ada di DB: {$nisn} ({$path})\n";
        $countSkip++;
        continue;
    }
    $studentId = (int) $row['id'];
    $dest      = $facesDir . DIRECTORY_SEPARATOR . $studentId . '.jpg';

    // Resize + kompres dengan GD: lebar max 600px, quality 75
    $srcImage = null;
    if ($ext === 'jpg' || $ext === 'jpeg') {
        $srcImage = @imagecreatefromjpeg($path);
    } elseif ($ext === 'png') {
        $srcImage = @imagecreatefrompng($path);
    }

    if ($srcImage === false || $srcImage === null) {
        // Fallback: langsung copy jika GD gagal
        if (copy($path, $dest)) {
            $hash = md5_file($dest);
            $update = $pdo->prepare('UPDATE students SET face_hash = ? WHERE id = ?');
            $update->execute([$hash, $studentId]);
            echo "  [ok-copy] {$nisn} -> student_id {$studentId}";
            $countOk++;
            if (! $noEmbedding) {
                $emb = $generateAndSaveEmbedding($pdo, $dest, $studentId);
                if ($emb === 'ok') { $countEmbedOk++; echo ' + embedding'; }
                elseif ($emb === 'skip') { $countEmbedSkip++; echo ' (embed skip)'; }
                else { $countEmbedFail++; echo ' (embed fail)'; }
            }
            echo "\n";
        } else {
            echo "  [fail] Gagal copy (GD error): {$path} -> {$dest}\n";
            $countFail++;
        }
        continue;
    }

    $width  = imagesx($srcImage);
    $height = imagesy($srcImage);
    $maxWidth = 600;
    if ($width > $maxWidth) {
        $ratio     = $maxWidth / $width;
        $newWidth  = $maxWidth;
        $newHeight = (int) round($height * $ratio);
    } else {
        $newWidth  = $width;
        $newHeight = $height;
    }

    $dstImage = imagecreatetruecolor($newWidth, $newHeight);
    imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);

    if (imagejpeg($dstImage, $dest, 75)) {
        imagedestroy($srcImage);
        imagedestroy($dstImage);
        $hash = md5_file($dest);
        $update = $pdo->prepare('UPDATE students SET face_hash = ? WHERE id = ?');
        $update->execute([$hash, $studentId]);
        echo "  [ok] {$nisn} -> student_id {$studentId}";
        $countOk++;
        if (! $noEmbedding) {
            $emb = $generateAndSaveEmbedding($pdo, $dest, $studentId);
            if ($emb === 'ok') { $countEmbedOk++; echo ' + embedding'; }
            elseif ($emb === 'skip') { $countEmbedSkip++; echo ' (embed skip)'; }
            else { $countEmbedFail++; echo ' (embed fail)'; }
        }
        echo "\n";
    } else {
        imagedestroy($srcImage);
        imagedestroy($dstImage);
        echo "  [fail] Gagal save JPG: {$path} -> {$dest}\n";
        $countFail++;
    }
}

echo "\nSelesai. OK: {$countOk}, Skip (NISN tidak ada): {$countSkip}, Gagal: {$countFail}\n";
if (! $noEmbedding) {
    echo "Embedding: OK: {$countEmbedOk}, Skip (kualitas/tidak 1 wajah): {$countEmbedSkip}, Gagal: {$countEmbedFail}\n";
}
