<?php
/**
 * license-php/api/auth-login.php
 * POST /api/auth/login (PHP Native)
 * Authoritative MySQL-first authentication with secure JWT tokens.
 * Single Source of Truth: presensi_client_db (Siswa, Guru, Tendik).
 */

require_once __DIR__ . '/../lib/Response.php';
require_once __DIR__ . '/../lib/LicenseCrypto.php';
require_once __DIR__ . '/../lib/repositories/SiswaRepository.php';
require_once __DIR__ . '/../lib/repositories/GuruRepository.php';
require_once __DIR__ . '/../lib/repositories/TendikRepository.php';

Response::handleCors();

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    Response::json(405, ['success' => false, 'message' => 'Method Not Allowed']);
}

$body = Response::getJsonBody();
$identifier = trim((string)($body['identifier'] ?? ''));
$password   = trim((string)($body['password'] ?? ''));

if (!$identifier || !$password) {
    Response::json(400, ['success' => false, 'code' => 'INVALID_REQUEST', 'message' => 'Username dan password wajib diisi']);
}

$config = require __DIR__ . '/../config/config.php';

// Detect tenant context from domain or local configuration
$host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'] ?? '';
$domain = LicenseCrypto::cleanDomain($host);

$tenantId = 'client_local';
$tenantsFile = dirname(__DIR__, 2) . '/data/tenants.json';
if (file_exists($tenantsFile)) {
    $tData = json_decode(file_get_contents($tenantsFile), true) ?: [];
    foreach ($tData as $tid => $t) {
        if (!$t) continue;
        if (LicenseCrypto::cleanDomain($t['domain'] ?? '') === $domain) {
            $tenantId = $tid;
            break;
        }
        if (!empty($t['aliases']) && is_array($t['aliases'])) {
            foreach ($t['aliases'] as $alias) {
                if (LicenseCrypto::cleanDomain($alias) === $domain) {
                    $tenantId = $tid;
                    break 2;
                }
            }
        }
    }
}

$valid = false;
$userRole = 'user';
$userName = $identifier;
$cleanLowerId = strtolower(ltrim(trim((string)$identifier), "'"));

// Helper: Verify password with multi-algorithm support & JIT Bcrypt Rehashing
$needsRehash = false;
$isDev = (($config['environment'] ?? 'production') !== 'production');

$verifyCredential = function($stored, $input, $role, $nisnVal = '') use ($isDev, &$needsRehash) {
    if (!$stored) {
        if ($role === 'siswa' && ($input === '12345' || $input === $nisnVal)) {
            $needsRehash = true;
            return true;
        }
        if ($isDev && $input === '12345') {
            $needsRehash = true;
            return true;
        }
        return false;
    }
    // 1. password_verify (Bcrypt / Argon2 standard)
    if (password_verify($input, $stored)) {
        return true;
    }
    // 2. sha256$salt$hash check (Legacy compatibility with JIT rehash flag)
    if (strpos($stored, 'sha256$') === 0) {
        $parts = explode('$', $stored);
        if (count($parts) === 3) {
            if (hash_equals($parts[2], hash('sha256', $input . $parts[1]))) {
                $needsRehash = true;
                return true;
            }
        }
    }
    // 3. Plaintext match ONLY permitted in non-production development environments
    if ($isDev && $stored === $input) {
        $needsRehash = true;
        return true;
    }
    return false;
};

// =============================================================================
// 1. PRIMARY IDENTITY SOURCE OF TRUTH: MySQL Repositories (presensi_client_db)
// =============================================================================
$matchedRepo = null;
$matchedIdentifier = null;
$mysqlErrorOccurred = false;
$guruAdminExists = false;

try {
    $siswaRepo  = new SiswaRepository();
    $guruRepo   = new GuruRepository();
    $tendikRepo = new TendikRepository();

    // A. Check Siswa by NISN or Barcode
    $siswa = $siswaRepo->findByNisn($cleanLowerId) ?: $siswaRepo->findByBarcode($cleanLowerId);
    if ($siswa && ($siswa['status'] ?? 'aktif') === 'aktif') {
        if ($verifyCredential($siswa['password'] ?? '', $password, 'siswa', $siswa['nisn'] ?? '')) {
            $valid = true;
            $userRole = 'siswa';
            $userName = $siswa['nama'] ?? $cleanLowerId;
            $matchedRepo = $siswaRepo;
            $matchedIdentifier = $siswa['nisn'];
        }
    }

    // B. Check Guru by Username, NIP, or Barcode
    if (!$valid) {
        $guru = $guruRepo->findByUsername($cleanLowerId) ?: ($guruRepo->findByNip($cleanLowerId) ?: $guruRepo->findByBarcode($cleanLowerId));
        if ($guru) {
            if ($guru['username'] === 'admin' || strtolower($guru['jabatan'] ?? '') === 'administrator') {
                $guruAdminExists = true;
            }
            if (($guru['status'] ?? 'aktif') === 'aktif') {
                if ($verifyCredential($guru['password'] ?? '', $password, 'guru')) {
                    $valid = true;
                    $isOp = (strpos(strtolower($guru['jabatan'] ?? ''), 'operator') !== false);
                    if ($guru['username'] === 'admin' || strtolower($guru['jabatan'] ?? '') === 'administrator') {
                        $userRole = 'admin';
                    } elseif ($isOp) {
                        $userRole = 'operator_absensi';
                    } else {
                        $userRole = 'guru';
                    }
                    $userName = $guru['nama'] ?? $cleanLowerId;
                    $matchedRepo = $guruRepo;
                    $matchedIdentifier = $guru['username'];
                }
            }
        }
    }

    // C. Check Tendik by Username, NIP, or Barcode
    if (!$valid) {
        $tendik = $tendikRepo->findByUsername($cleanLowerId) ?: ($tendikRepo->findByNip($cleanLowerId) ?: $tendikRepo->findByBarcode($cleanLowerId));
        if ($tendik && ($tendik['status'] ?? 'aktif') === 'aktif') {
            if ($verifyCredential($tendik['password'] ?? '', $password, 'tendik')) {
                $valid = true;
                $isOperator = (strpos(strtolower($tendik['jabatan'] ?? ''), 'operator') !== false);
                if (!$isOperator) {
                    try {
                        $db = ClientDatabase::getConnection();
                        $stmt = $db->prepare("SELECT setting_value FROM app_settings WHERE setting_key = 'attendance_operators' LIMIT 1");
                        $stmt->execute();
                        $rawOps = $stmt->fetchColumn();
                        if ($rawOps) {
                            $opList = json_decode($rawOps, true);
                            if (is_array($opList) && in_array(strtolower($tendik['username']), array_map('strtolower', $opList), true)) {
                                $isOperator = true;
                            }
                        }
                    } catch (Throwable $opErr) {
                        // Fail closed: keep $isOperator false on query error
                    }
                }
                $userRole = $isOperator ? 'operator_absensi' : 'tendik';
                $userName = $tendik['nama'] ?? $cleanLowerId;
                $matchedRepo = $tendikRepo;
                $matchedIdentifier = $tendik['username'];
            }
        }
    }

    // JIT Password Rehash Upgrade to Bcrypt
    if ($valid && $needsRehash && $matchedRepo && $matchedIdentifier) {
        try {
            $newBcryptHash = password_hash($password, PASSWORD_BCRYPT);
            $matchedRepo->update($matchedIdentifier, ['password' => $newBcryptHash]);
        } catch (Throwable $rehashErr) {
            error_log('[JIT_REHASH_ERROR] ' . $rehashErr->getMessage());
        }
    }
} catch (Throwable $e) {
    $mysqlErrorOccurred = true;
    error_log('[AUTH_MYSQL_ERROR] ' . $e->getMessage());
}

// FAIL CLOSED: If MySQL service threw an exception, immediately reject authentication
if ($mysqlErrorOccurred) {
    Response::json(500, [
        'success' => false,
        'code'    => 'DATABASE_ERROR',
        'message' => 'Layanan database tidak dapat diakses. Autentikasi ditolak demi keamanan (fail-closed).'
    ]);
}

// =============================================================================
// 2. PERSISTENT ADMINISTRATIVE AUTHENTICATION GATE (SEC-AUTH-02 Remediation)
// MySQL is the SOLE identity authority in production.
// This fallback NEVER overrides an existing MySQL administrator ($guruAdminExists)
// and NEVER activates in production environments ($isDev === false).
// =============================================================================
if (!$valid && $cleanLowerId === 'admin' && !$guruAdminExists && $isDev) {
    $adminPassFile = dirname(__DIR__) . '/data/admin-password.json';
    $customAdminHash = null;
    if (file_exists($adminPassFile)) {
        $apData = json_decode(file_get_contents($adminPassFile), true);
        if ($apData) {
            $customAdminHash = $apData['passwordHash'] ?? null;
        }
    }

    if ($customAdminHash) {
        $adminMatch = false;
        if (strpos($customAdminHash, 'sha256$') === 0) {
            $parts = explode('$', $customAdminHash);
            if (count($parts) === 3 && hash_equals($parts[2], hash('sha256', $password . $parts[1]))) {
                $adminMatch = true;
                // JIT Upgrade: Migrate legacy SHA-256 hash to Bcrypt in admin-password.json
                try {
                    $newBcrypt = password_hash($password, PASSWORD_BCRYPT);
                    file_put_contents($adminPassFile, json_encode([
                        'username'     => 'admin',
                        'passwordHash' => $newBcrypt,
                        'updatedAt'    => date('Y-m-d H:i:s')
                    ], JSON_PRETTY_PRINT), LOCK_EX);
                } catch (Throwable $e) {
                    error_log('[ADMIN_JIT_REHASH_ERROR] ' . $e->getMessage());
                }
            }
        } elseif (password_verify($password, $customAdminHash)) {
            $adminMatch = true;
        }
        if ($adminMatch) {
            $valid = true;
            $userRole = 'admin';
            $userName = ($tenantId === 'client_001') ? 'Administrator SMP Negeri 1 Samudera' : 'Administrator SMANSA';
        }
    }
}

if (!$valid) {
    Response::json(401, ['success' => false, 'code' => 'INVALID_CREDENTIALS', 'message' => 'Username atau password salah']);
}

// Sign JWT Auth Token
$privKeyPath = $config['keys']['private'];
if (!file_exists($privKeyPath)) {
    $privCandidates = [
        getenv('LICENSE_PRIVATE_KEY_PATH'),
        dirname(__DIR__, 2) . '/keys/license_private.key',
        dirname(__DIR__, 3) . '/keys/license_private.key',
        dirname(__DIR__, 4) . '/keys/license_private.key',
        dirname(__DIR__, 5) . '/keys/license_private.key',
        (isset($_SERVER['DOCUMENT_ROOT']) ? dirname($_SERVER['DOCUMENT_ROOT']) . '/keys/license_private.key' : null),
        dirname(__DIR__, 3) . '/private/license-private.pem',
        dirname(__DIR__, 2) . '/private/license-private.pem'
    ];
    foreach ($privCandidates as $cand) {
        if ($cand && file_exists($cand)) {
            $privKeyPath = $cand;
            break;
        }
    }
}

$token = '';
if (file_exists($privKeyPath)) {
    $privKeyPem = file_get_contents($privKeyPath);
    $tokenPayload = [
        'sub'       => $identifier,
        'username'  => $identifier,
        'nama'      => $userName,
        'role'      => $userRole,
        'tenant_id' => $tenantId,
        'type'      => 'access',
        'iat'       => time(),
        'exp'       => time() + 86400
    ];
    $token = LicenseCrypto::signToken($tokenPayload, $privKeyPem);
}

// Resolve live photo if available
$userPhoto = null;
if (!empty($siswa['foto'])) {
    $userPhoto = $siswa['foto'];
} elseif (!empty($guru['foto'])) {
    $userPhoto = $guru['foto'];
} elseif (!empty($tendik['foto'])) {
    $userPhoto = $tendik['foto'];
}

// Set auth cookie (strictly when token is generated)
if ($token) {
    $isHttps = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
    setcookie('presensi_auth', $token, [
        'expires'  => time() + 86400,
        'path'     => '/',
        'secure'   => $isHttps,
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
}

Response::json(200, [
    'success'   => true,
    'tenant_id' => $tenantId,
    'role'      => $userRole,
    'username'  => $identifier,
    'nama'      => $userName,
    'foto'      => $userPhoto,
    'token'     => $token
]);
