<?php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

require_once __DIR__ . '/emailClient.php';

class Auth {
    private $pdo;
    private $key = "your_secret_key_here_change_this";
    private $config;
    private $resend;

    public function __construct($pdo) {
        $this->pdo = $pdo;
        $this->config = require __DIR__ . '/../config/config.php';
        $this->resend = new BrevoMailer($_ENV['BREVO_API_KEY'], $_ENV['BREVO_SENDER_EMAIL']);
        
        // Try to get key from env if available
        $env = getenv('JWT_SECRET');
        if ($env) $this->key = $env;
    }

    private function validateEmail($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("Invalid email format", 400);
        }

        $domain = substr(strrchr($email, "@"), 1);
        if (!in_array($domain, $this->config['allowed_email_domains'])) {
            throw new Exception("Only Gmail and Yahoo emails are allowed", 400);
        }
    }

    private function generateOTP() {
        return str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT);
    }

    public function register($data) {
        $salt = $data['salt'] ?? '';
        $login_hash = $data['loginHash'] ?? '';
        $backupBlobPassword = $data['backupBlobPassword'] ?? '';
        $backupBlobMnemonic = $data['backupBlobMnemonic'] ?? '';
        $public_key = $data['publicKey'] ?? '';
        $username = $data['username'] ?? '';
        $email = $data['email'] ?? '';

        if (empty($username) || empty($email)) {
            throw new Exception("Please include all fields", 400);
        }

        $this->validateEmail($email);

        // Check if username exists
        $stmt = $this->pdo->prepare("SELECT id FROM users WHERE username = ?");
        $stmt->execute([$username]);
        if ($stmt->fetch()) {
            throw new Exception("Username already exists", 400);
        }

        // Check if email exists
        $stmt = $this->pdo->prepare("SELECT id FROM users WHERE email = ?");
        $stmt->execute([$email]);
        if ($stmt->fetch()) {
            throw new Exception("Email already registered", 400);
        }

        // Generate OTP
        $otp = $this->generateOTP();
        $expiry = date('Y-m-d H:i:s', time() + ($this->config['otp_expiry_minutes'] * 60));

        $stmt = $this->pdo->prepare("INSERT INTO users (username, email, password_hash, otp_code, otp_expiry, public_key, backup_blob_mnemonic, backup_blob_password) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
        $stmt->execute([$username, $email, $login_hash, $otp, $expiry, $public_key, $backupBlobMnemonic, $backupBlobPassword]);

        // Send OTP email // ENABLE THIS
        try {
            $this->resend->sendOTP($email, $otp);
        } catch (Exception $e) {
            // If email fails, still return success but log error
            error_log("Failed to send OTP email: " . $e->getMessage());
        }

        return [
            'msg' => 'Registration successful. Please check your email for verification code.',
            'email' => $email
        ];
    }

    // Allow arguments to be null by default
    public function getSaltAndPrivateKey($username = null, $id = null) {
        
        $sql = "";
        $param = null;

        // 1. Determine which parameter to use (ID takes priority if both exist)
        if (!empty($id)) {
            $sql = "SELECT public_key, backup_blob_mnemonic, backup_blob_password FROM users WHERE id = ?";
            $param = $id;
            error_log("getSaltAndPrivateKey fetching by ID: " . $id);
        } elseif (!empty($username)) {
            $sql = "SELECT public_key, backup_blob_mnemonic, backup_blob_password FROM users WHERE username = ?";
            $param = $username;
            error_log("getSaltAndPrivateKey fetching by Username: " . $username);
        } else {
            // 2. Hard fail if neither is provided
            throw new Exception("Username or ID required", 400);
        }

        // 3. Execute the dynamic query
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([$param]);
        $user = $stmt->fetch();

        if (!$user) {
            // detailed error helps debugging
            $identifier = !empty($id) ? "ID: $id" : "Username: $username";
            throw new Exception("User not found ($identifier)", 404);
        }

        return [
            'publicKey' => $user['public_key'],
            'backupBlobPassword' => $user['backup_blob_password'],
            'backupBlobMnemonic' => $user['backup_blob_mnemonic']
        ];
    }

    public function markAsVerified($user) {
      $stmt = $this->pdo->prepare("UPDATE users SET email_verified = TRUE, otp_code = NULL, otp_expiry = NULL WHERE id = ?");
      $stmt->execute([$user['id']]);
    }

    public function verifyOTP($data) {
        $email = $data['email'] ?? '';
        $otp = $data['otp'] ?? '';

        if (empty($email) || empty($otp)) {
            throw new Exception("Email and OTP required", 400);
        }

        $stmt = $this->pdo->prepare("SELECT id, username, otp_code, otp_expiry FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch();

        if (!$user) {
            throw new Exception("User not found", 404);
        }

        if ($user['otp_code'] !== $otp) {
            throw new Exception("Invalid OTP code", 400);
        }

        if (strtotime($user['otp_expiry']) < time()) {
            throw new Exception("OTP code expired", 400);
        }

        // Mark email as verified
        $this->markAsVerified($user);

        // Generate JWT token
        $payload = [
            'user' => ['id' => $user['id']],
            'iat' => time(),
            'exp' => time() + (3600 * 24 * 7)
        ];

        $token = JWT::encode($payload, $this->key, 'HS256');
        return ['token' => $token, 'userId' => (string)$user['id'], 'username' => $user['username']];
    }

    public function resendOTP($data) {
        $email = $data['email'] ?? '';

        if (empty($email)) {
            throw new Exception("Email required", 400);
        }

        $stmt = $this->pdo->prepare("SELECT id FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch();

        if (!$user) {
            throw new Exception("User not found", 404);
        }

        $stmt = $this->pdo->prepare("SELECT otp_expiry FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $otp_expiry = $stmt->fetchColumn();

        if ($otp_expiry && strtotime($otp_expiry) > time()) {
            throw new Exception("OTP already sent. Please wait before requesting again.", 400);
        }

        $otp = $this->generateOTP();
        $expiry = date('Y-m-d H:i:s', time() + ($this->config['otp_expiry_minutes'] * 60));

        $stmt = $this->pdo->prepare("UPDATE users SET otp_code = ?, otp_expiry = ? WHERE id = ?");
        $stmt->execute([$otp, $expiry, $user['id']]);

        try {
            $this->resend->sendOTP($email, $otp);
        } catch (Exception $e) {
            error_log("Failed to send OTP email: " . $e->getMessage());
            throw new Exception("Failed to send email", 500);
        }

        return ['msg' => 'OTP sent to your email'];
    }

    public function requestPasswordReset($data) {
        $email = $data['email'] ?? '';

        if (empty($email)) {
            throw new Exception("Email required", 400);
        }

        $stmt = $this->pdo->prepare("SELECT id, email_verified FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch();

        if (!$user) {
            // Don't reveal if email exists or not for security
            return ['msg' => 'If that email exists, a reset code has been sent'];
        }

        if (!$user['email_verified']) {
            throw new Exception("Email not verified", 400);
        }

        $otp = $this->generateOTP();
        $expiry = date('Y-m-d H:i:s', time() + ($this->config['otp_expiry_minutes'] * 60));

        $stmt = $this->pdo->prepare("UPDATE users SET otp_code = ?, otp_expiry = ? WHERE id = ?");
        $stmt->execute([$otp, $expiry, $user['id']]);

        try {
            $this->resend->sendOTP($email, $otp);
        } catch (Exception $e) {
            error_log("Failed to send reset email: " . $e->getMessage());
        }

        return ['msg' => 'If that email exists, a reset code has been sent'];
    }

    public function resetPassword($data) {
        $email = $data['email'] ?? '';
        $otp = $data['otp'] ?? '';
        $newPassword = $data['newPassword'] ?? '';

        if (empty($email) || empty($otp) || empty($newPassword)) {
            throw new Exception("All fields required", 400);
        }

        $stmt = $this->pdo->prepare("SELECT id, otp_code, otp_expiry FROM users WHERE email = ?");
        $stmt->execute([$email]);
        $user = $stmt->fetch();

        if (!$user) {
            throw new Exception("Invalid request", 400);
        }

        if ($user['otp_code'] !== $otp) {
            throw new Exception("Invalid OTP code", 400);
        }

        if (strtotime($user['otp_expiry']) < time()) {
            throw new Exception("OTP code expired", 400);
        }

        $hash = password_hash($newPassword, PASSWORD_DEFAULT);
        $stmt = $this->pdo->prepare("UPDATE users SET password_hash = ?, otp_code = NULL, otp_expiry = NULL WHERE id = ?");
        $stmt->execute([$hash, $user['id']]);

        return ['msg' => 'Password reset successful'];
    }

    public function login($data) {
        $username = $data['username'] ?? '';
        $loginHash = $data['loginHash'] ?? '';

        $stmt = $this->pdo->prepare("SELECT id, username, password_hash, email_verified FROM users WHERE username = ?");
        $stmt->execute([$username]);
        $user = $stmt->fetch();

        if (!$user || $loginHash !== $user['password_hash']) {
            throw new Exception("Invalid Credentials", 400);
        }

        if (!$user['email_verified']) {
            throw new Exception("Email not verified. Please verify your email first.", 400);
        }

        $payload = [
            'user' => ['id' => $user['id']],
            'iat' => time(),
            'exp' => time() + (3600 * 24 * 7)
        ];

        $token = JWT::encode($payload, $this->key, 'HS256');
        return ['token' => $token, 'userId' => (string)$user['id']];
    }

    public function authenticate() {
        $token = null;
        $headers = [];
        if (function_exists('apache_request_headers')) {
            $headers = apache_request_headers();
        } else {
            $headers = $this->getEmulatedHeaders();
        }
        error_log("Headers: " . print_r($headers, true)); // Debugging line to check headers
		
		// USAGE:
		debugLog("Script started");
		debugLog($headers); // Log all server vars to see if Auth header exists
		
        if (isset($headers['Authorization'])) {
            $token = str_replace('Bearer ', '', $headers['Authorization']);
        } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) { // Nginx/Built-in server
            $token = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION']);
        } // 3. Check the "Redirect" location (created by .htaccess RewriteRule)
   		  elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
     		$token = trim($_SERVER["REDIRECT_HTTP_AUTHORIZATION"]);
    	}

        if (!$token) {
			debugLog("No token in authorization");
            throw new Exception("No token, authorization denied", 401);
        }
        
        try {
            $decoded = JWT::decode($token, new Key($this->key, 'HS256'));
			debugLog("Token" . $decoded->user->id);
            return $decoded->user->id;
        } catch (Exception $e) {
			debugLog("Token is not valid");
            throw new Exception("Token is not valid", 401);
        }
    }

    public function getUser($userId) {
		debugLog("Finding User " . $userId);
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$userId]);
        $user = $stmt->fetch();
        if (!$user) {
			debugLog("User not found");
			throw new Exception("User not found", 404);
		}
        // Compatibility: Get friends list IDs
        $stmtFriends = $this->pdo->prepare("SELECT friend_id FROM friendships WHERE user_id = ?");
        $stmtFriends->execute([$userId]);
        $user['friends'] = $stmtFriends->fetchAll(PDO::FETCH_COLUMN);

        return $user;
    }

    public function updateProfile($userId) {
        $name = $_POST['name'] ?? null;
        $profilePicture = null;

        // Handle profile picture upload
        if (isset($_FILES['profilePicture']) && $_FILES['profilePicture']['error'] === UPLOAD_ERR_OK) {
            $targetDir = __DIR__ . '/../uploads/profiles/';
            if (!file_exists($targetDir)) mkdir($targetDir, 0777, true);

            $fileName = uniqid() . '_' . basename($_FILES['profilePicture']['name']);
            $targetFile = $targetDir . $fileName;

            if (move_uploaded_file($_FILES['profilePicture']['tmp_name'], $targetFile)) {
                $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
                $host = $_SERVER['HTTP_HOST'];
                $profilePicture = $protocol . $host . '/uploads/profiles/' . $fileName;

                // Delete old profile picture if exists
                $stmt = $this->pdo->prepare("SELECT profile_picture FROM users WHERE id = ?");
                $stmt->execute([$userId]);
                $oldProfile = $stmt->fetch();
                if ($oldProfile && $oldProfile['profile_picture']) {
                    $parts = explode('/uploads/profiles/', $oldProfile['profile_picture']);
                    if (count($parts) > 1) {
                        $localPath = __DIR__ . '/../uploads/profiles/' . $parts[1];
                        if (file_exists($localPath)) unlink($localPath);
                    }
                }
            }
        }

        // Update user profile
        if ($profilePicture) {
            $stmt = $this->pdo->prepare("UPDATE users SET name = ?, profile_picture = ? WHERE id = ?");
            $stmt->execute([$name, $profilePicture, $userId]);
        } else {
            $stmt = $this->pdo->prepare("UPDATE users SET name = ? WHERE id = ?");
            $stmt->execute([$name, $userId]);
        }

        return ["msg" => "Profile updated successfully"];
    }

    public function changePassword($userId, $data) {
        // $salt = $data['salt'] ?? ''; // REMOVE
        $password_hash = $data['loginHash'] ?? '';
        $backupBlobMnemonic = $data['backupBlobMnemonic'] ?? '';
        $backupBlobPassword = $data['backupBlobPassword'] ?? '';
        $public_key = $data['publicKeyHex'] ?? '';
        $username = $data['username'] ?? '';


        if (empty($password_hash) || empty($backupBlobMnemonic) || empty($backupBlobPassword)) {
            throw new Exception("Password hash, and backup blobs are required", 400);
        }

        error_log( print_r($data, true) ); // Debugging line to check incoming data
        error_log("Changing password for user ID: $userId"); // Debugging line to confirm user ID

        // Update password
        $stmt = $this->pdo->prepare("UPDATE users SET password_hash = ?, backup_blob_mnemonic = ?, backup_blob_password = ?, public_key = ? WHERE username = ?");
        // 1. Check if the SQL command succeeded
        if (!$stmt->execute([$password_hash, $backupBlobMnemonic, $backupBlobPassword, $public_key, $username])) {
            throw new Exception("Database error: Could not update user.");
        }

        // 2. Check if any rows were actually touched
        if ($stmt->rowCount() === 0) {
            // This happens if the ID doesn't exist OR if the new data is identical to the old data
            throw new Exception("Update failed: User ID not found or data unchanged.", 404);
        }

        return ["msg" => "Password changed successfully"];
    }

    private function getEmulatedHeaders() {
        $headers = [];
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) == 'HTTP_') {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
            }
        }
        return $headers;
    }
}
