<?php
// File: smm/app/lib/Auth.php

class Auth {
    private $db;

    public function __construct(PDO $pdo) {
        $this->db = $pdo;
    }

    /**
     * Proses login pengguna.
     *
     * @param string $emailOrUsername Email atau username pengguna.
     * @param string $password Password pengguna.
     * @return array|bool Data pengguna jika berhasil, false jika gagal.
     */
    public function login(string $emailOrUsername, string $password) { // Menghapus : bool|array
        try {
            $sql = "SELECT id, username, email, password, full_name, role, status, phone_number
                    FROM users
                    WHERE email = ? OR username = ?
                    LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$emailOrUsername, $emailOrUsername]);
            $user = $stmt->fetch();

            if ($user && password_verify($password, $user['password'])) {
                if ($user['status'] === 'active') {
                    // Pastikan sesi sudah dimulai sebelum memanipulasi $_SESSION
                    if (session_status() == PHP_SESSION_NONE) {
                        session_start();
                    }
                    session_regenerate_id(true);
                    $_SESSION['user_id'] = $user['id'];
                    $_SESSION['username'] = $user['username'];
                    $_SESSION['user_role'] = $user['role'];
                    $_SESSION['user_email'] = $user['email'];
                    $_SESSION['user_fullname'] = $user['full_name'];
                    $_SESSION['user_phone'] = $user['phone_number'];
                    $_SESSION['is_logged_in'] = true;
                    unset($user['password']);
                    return $user;
                } else {
                    // Akun tidak aktif
                    return false;
                }
            } else {
                // Kredensial salah
                return false;
            }
        } catch (\PDOException $e) {
            error_log("Auth::login DB Error for " . $emailOrUsername . ": " . $e->getMessage());
            return false;
        }
    }

    /**
     * Cek apakah username sudah terdaftar.
     *
     * @param string $username Username yang akan dicek.
     * @return bool True jika sudah terdaftar, false jika belum.
     */
    public function isUsernameTaken(string $username): bool {
        try {
            $sql = "SELECT id FROM users WHERE username = ? LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$username]);
            return $stmt->fetch() !== false;
        } catch (\PDOException $e) {
            error_log("Auth::isUsernameTaken DB Error for " . $username . ": " . $e->getMessage());
            return true; // Anggap sudah terpakai jika ada error DB untuk keamanan
        }
    }

    /**
     * Cek apakah email sudah terdaftar.
     *
     * @param string $email Email yang akan dicek.
     * @return bool True jika sudah terdaftar, false jika belum.
     */
    public function isEmailTaken(string $email): bool {
         try {
            $sql = "SELECT id FROM users WHERE email = ? LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute([$email]);
            return $stmt->fetch() !== false;
        } catch (\PDOException $e) {
            error_log("Auth::isEmailTaken DB Error for " . $email . ": " . $e->getMessage());
            return true; // Anggap sudah terpakai jika ada error DB
        }
    }

    /**
     * Registrasi pengguna baru.
     *
     * @param array $userData Data pengguna.
     * @return bool True jika berhasil, false jika gagal.
     */
    public function register(array $userData): bool {
        $sql = "INSERT INTO users (username, email, password, full_name, role, status, phone_number, created_at, updated_at)
                VALUES (:username, :email, :password, :full_name, :role, :status, :phone_number, NOW(), NOW())";
        try {
            $stmt = $this->db->prepare($sql);
            $role = $userData['role'] ?? 'member';
            $status = $userData['status'] ?? 'active';
            $params = [
                ':username' => $userData['username'],
                ':email' => $userData['email'],
                ':password' => $userData['password'], // Diasumsikan sudah di-hash sebelumnya
                ':full_name' => $userData['full_name'] ?? null,
                ':role' => $role,
                ':status' => $status,
                ':phone_number' => $userData['phone_number'] ?? null
            ];
            return $stmt->execute($params);
        } catch (\PDOException $e) {
            error_log("Auth::register DB Error for " . ($userData['email'] ?? 'N/A') . ": " . $e->getMessage() . " | Code: " . $e->getCode());
            return false;
        }
    }

    /**
     * Cek apakah pengguna sudah login.
     *
     * @return bool True jika sudah login, false jika belum.
     */
    public static function isLoggedIn(): bool {
        if (session_status() === PHP_SESSION_NONE) { session_start(); }
        return isset($_SESSION['is_logged_in']) && $_SESSION['is_logged_in'] === true;
    }

    /**
     * Proses logout pengguna.
     */
    public static function logout(): void {
        if (session_status() === PHP_SESSION_NONE) { session_start(); }
        $_SESSION = array();
        if (ini_get("session.use_cookies")) {
            $params = session_get_cookie_params();
            setcookie(session_name(), '', time() - 42000,
                $params["path"], $params["domain"],
                $params["secure"], $params["httponly"]
            );
        }
        session_destroy();
    }

    /**
     * Ambil data pengguna berdasarkan ID.
     *
     * @param int $userId ID pengguna.
     * @return array|false Data pengguna jika ditemukan, false jika tidak.
     */
    public function getUserById(int $userId) { // Menghapus : array|false
        try {
            $sql = "SELECT id, username, email, full_name, role, status, phone_number, created_at, balance, api_key
                    FROM users
                    WHERE id = :user_id LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
            $stmt->execute();
            return $stmt->fetch(PDO::FETCH_ASSOC);
        } catch (\PDOException $e) {
            error_log("Auth::getUserById DB Error for ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }

    /**
     * Update profil pengguna.
     *
     * @param int $userId ID pengguna.
     * @param array $data Data yang akan diupdate.
     * @return bool True jika berhasil, false jika gagal.
     */
    public function updateProfile(int $userId, array $data): bool {
        if (empty($data)) {
            return false;
        }

        $allowedFields = ['full_name', 'phone_number'];
        $setClauses = [];
        $params = [':user_id' => $userId];

        foreach ($data as $key => $value) {
            if (in_array($key, $allowedFields)) {
                $setClauses[] = "`" . $key . "` = :" . $key;
                $params[':' . $key] = ($value === '') ? null : $value;
            }
        }

        if (empty($setClauses)) {
            return false; 
        }
        $setClauses[] = "updated_at = NOW()";

        $sql = "UPDATE users SET " . implode(', ', $setClauses) . " WHERE id = :user_id";

        try {
            $stmt = $this->db->prepare($sql);
            return $stmt->execute($params);
        } catch (\PDOException $e) {
            error_log("Auth::updateProfile DB Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }

    /**
     * Update password pengguna.
     *
     * @param int $userId ID pengguna.
     * @param string $currentPassword Password saat ini.
     * @param string $newPassword Password baru.
     * @return bool|string True jika berhasil, 'invalid_current_password' jika password saat ini salah, false jika error lain.
     */
    public function updatePassword(int $userId, string $currentPassword, string $newPassword) { // Menghapus : bool|string
        try {
            $sqlSelect = "SELECT password FROM users WHERE id = :user_id LIMIT 1";
            $stmtSelect = $this->db->prepare($sqlSelect);
            $stmtSelect->bindParam(':user_id', $userId, PDO::PARAM_INT);
            $stmtSelect->execute();
            $user = $stmtSelect->fetch();

            if (!$user) {
                return false; // Pengguna tidak ditemukan
            }

            if (!password_verify($currentPassword, $user['password'])) {
                return 'invalid_current_password';
            }

            $newHashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
            if ($newHashedPassword === false) {
                 throw new \RuntimeException("Password hashing failed.", 500);
            }

            $sqlUpdate = "UPDATE users SET password = :new_password, updated_at = NOW() WHERE id = :user_id";
            $stmtUpdate = $this->db->prepare($sqlUpdate);
            $stmtUpdate->bindParam(':new_password', $newHashedPassword);
            $stmtUpdate->bindParam(':user_id', $userId, PDO::PARAM_INT);

            return $stmtUpdate->execute();

        } catch (\PDOException $e) {
            error_log("Auth::updatePassword DB Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        } catch (\RuntimeException $e) {
             error_log("Auth::updatePassword Runtime Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }

    /**
     * Hapus pengguna oleh admin.
     *
     * @param int $userId ID pengguna yang akan dihapus.
     * @return bool True jika berhasil, false jika gagal.
     */
    public function deleteUser(int $userId): bool {
        if ($userId <= 0) {
            return false;
        }
        // Pertimbangkan untuk menambahkan pengecekan apakah pengguna yang dihapus bukan admin super
        // atau apakah ada ketergantungan data lain sebelum menghapus.
        $sql = "DELETE FROM users WHERE id = :user_id";
        try {
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
            return $stmt->execute();
        } catch (\PDOException $e) {
            error_log("Auth::deleteUser DB Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }

    /**
     * Update data pengguna oleh admin.
     *
     * @param int $userId ID pengguna.
     * @param array $data Data yang akan diupdate.
     * @return bool True jika berhasil, false jika gagal.
     */
    public function updateUserByAdmin(int $userId, array $data): bool {
        if (empty($data) || $userId <= 0) {
            return false;
        }

        $allowedFields = ['full_name', 'phone_number', 'balance', 'role', 'status'];
        $setClauses = [];
        $params = [':user_id' => $userId];

        $newPassword = $data['password'] ?? null; // Ambil password baru jika ada
        unset($data['password']); // Hapus dari $data utama agar tidak masuk loop biasa

        foreach ($data as $key => $value) {
            if (in_array($key, $allowedFields)) {
                $setClauses[] = "`" . $key . "` = :" . $key;
                 if ($key === 'balance') {
                     $params[':' . $key] = filter_var($value, FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
                     if ($params[':' . $key] === null) $params[':' . $key] = 0.0; 
                 } else {
                     $params[':' . $key] = ($value === '') ? null : $value;
                 }
            }
        }

        if ($newPassword !== null && $newPassword !== '') {
            $hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
            if ($hashedPassword === false) {
                error_log("Admin Update User: Password hashing failed for user ID " . $userId);
                return false; 
            }
            $setClauses[] = "`password` = :new_password";
            $params[':new_password'] = $hashedPassword;
        }

        if (empty($setClauses)) {
            return true; // Tidak ada yang diupdate, anggap berhasil
        }
        $setClauses[] = "updated_at = NOW()";

        $sql = "UPDATE users SET " . implode(', ', $setClauses) . " WHERE id = :user_id";

        try {
            $stmt = $this->db->prepare($sql);
            return $stmt->execute($params);
        } catch (\PDOException $e) {
            error_log("Auth::updateUserByAdmin DB Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Validasi API Key.
     * Mengembalikan user_id jika valid dan aktif, selain itu false.
     *
     * @param string $apiKey Kunci API yang akan divalidasi.
     * @return int|false ID pengguna jika valid, false jika tidak.
     */
    public function validateApiKey(string $apiKey) { // Menghapus : int|false
        if (empty($apiKey)) {
            return false;
        }
        try {
            $sql = "SELECT id FROM users WHERE api_key = :api_key AND status = 'active' LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':api_key', $apiKey);
            $stmt->execute();
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($user && isset($user['id'])) {
                return (int)$user['id'];
            }
            return false;
        } catch (\PDOException $e) {
            error_log("Auth::validateApiKey DB Error: " . $e->getMessage());
            return false; 
        }
    }

    /**
     * Menghasilkan API Key baru yang unik.
     *
     * @return string API Key yang baru.
     */
    public function generateApiKey(): string {
        return bin2hex(random_bytes(32)); 
    }

    /**
     * Menyimpan atau memperbarui API Key untuk pengguna.
     *
     * @param int $userId ID pengguna.
     * @param string $apiKey Kunci API yang akan disimpan.
     * @return bool True jika berhasil, false jika gagal.
     */
    public function setUserApiKey(int $userId, string $apiKey): bool {
        try {
            $sql = "UPDATE users SET api_key = :api_key, updated_at = NOW() WHERE id = :user_id";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':api_key', $apiKey);
            $stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
            return $stmt->execute();
        } catch (\PDOException $e) {
            error_log("Auth::setUserApiKey DB Error for User ID " . $userId . ": " . $e->getMessage());
            return false;
        }
    }

     /**
     * Mengambil API Key pengguna saat ini.
     *
     * @param int $userId ID pengguna.
     * @return string|null API Key jika ada, null jika tidak.
     */
    public function getUserApiKey(int $userId) { // Menghapus : ?string
        try {
            $sql = "SELECT api_key FROM users WHERE id = :user_id LIMIT 1";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
            $stmt->execute();
            $result = $stmt->fetchColumn();
            return $result ?: null;
        } catch (\PDOException $e) {
            error_log("Auth::getUserApiKey DB Error for User ID " . $userId . ": " . $e->getMessage());
            return null;
        }
    }
}
?>
