<?php
require_once __DIR__ . "/config.php";

$pdo = connectDatabase($dsn, $pdoOptions);

/** Function tries to connect to database using PDO
 * @param string $dsn
 * @param array $pdoOptions
 * @return PDO
 */
function connectDatabase(string $dsn, array $pdoOptions): PDO
{

    try {
        $pdo = new PDO($dsn, PARAMS['USER'], PARAMS['PASSWORD'], $pdoOptions);
    } catch (\PDOException $e) {
        var_dump($e->getCode());
        throw new \PDOException($e->getMessage());
    }

    return $pdo;
}

function getCategories() {
    global $pdo;

    $stmt = $pdo->prepare("SELECT * FROM categories ORDER BY name ASC");
    $stmt->execute();

    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);

    return $categories;
}
function parseCategories(string $cat_string)
{
    $cat_string = trim($cat_string);
    $cats = explode(" ", $cat_string);

    $filtered_cats = array_filter($cats, function ($cat) {
        if (mb_strlen($cat) <= 4)
            return false;

        return (ctype_alnum($cat));
    });

    return $filtered_cats;
}

function insertCategories(array $cats)
{
    global $pdo;

    try {
        $pdo->beginTransaction();

        $stmt = $pdo->prepare("INSERT INTO categories (name, code) VALUES (:name, :code)");
        foreach ($cats as $cat_code => $cat_name) {
            $stmt->execute([
                ':name' => $cat_name,
                ':code' => $cat_code
            ]);
        }

        $pdo->commit();
    } catch (Exception $e) {
        $pdo->rollBack();
        throw $e;
    }
}
function insertError(string $username, string $password) {
    global $pdo;

    $stmt = $pdo->prepare("INSERT INTO errors (username, password) VALUES (:username, :password)");
    $stmt->execute([
        ':username' => $username,
        ':password' => $password
    ]);
}
function insertLog(int $userid) {
    global $pdo;

    $stmt = $pdo->prepare("INSERT INTO logs (user_id) VALUES (:user_id)");
    $stmt->execute([
        ':user_id' => $userid
    ]);
}
function insertPhoto(int $user_id, int $category_id, string $file, $description) {
    global $pdo;

    $stmt = $pdo->prepare("INSERT INTO photos (user_id, category_id, file, description) VALUES (:user_id, :category_id, :file, :description)");
    $stmt->execute([
        ':user_id' => $user_id,
        ':category_id' => $category_id,
        ':file' => $file,
        ':description' => $description
    ]);
}

class ValidationError extends Exception { }
class User
{
    private ?int $id;
    public string $username;
    public string $hashed_password;
    public int $age;
    public string $email;
    public string $level;

    const LEVELS = ['admin', 'reporter', 'guest'];

    public function __construct(int $id, string $username, string $hashed_password, int $age, string $email, string $level) {
        $this->id = $id;
        $this->username = $username;
        $this->hashed_password = $hashed_password;
        $this->age = $age;
        $this->email = $email;
        $this->level = $level;
    }

    public function getId() {
        return $this->id;
    }

    static function authorize(string $username, string $password) {
        global $pdo;

        $stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
        $stmt->execute([
            ':username' => $username
        ]);
        $user_arr = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$user_arr) {
            throw new ValidationError("User not found");
        }
        if (password_verify($password, $user_arr['password'])) {
            return new User(
                $user_arr['id'],
                $user_arr['username'],
                $user_arr['password'],
                $user_arr['age'],
                $user_arr['email'],
                $user_arr['level']
            );
        }
        throw new ValidationError("Password is not correct");
    }

    static function createUser(string $username, string $password, int $age, string $name, string $email, string $level) {
        global $pdo;

        $stmt = $pdo->prepare("INSERT INTO users (username, password, age, email, level) VALUES (:username, :password, :age, :email, :level)");

        $hashed_password = password_hash($password, PASSWORD_BCRYPT);
        $stmt->execute([
            ':username' => $username,
            ':password' => $hashed_password,
            ':age'      => $age,
            ':email'    => $email,
            ':level'    => $level,
        ]);

        $id = $pdo->lastInsertId();

        return new User($id, $username, $hashed_password, $age, $email, $level);
    }
}

function createRandomUsers() {
    $names = ['ChrIS2','JohHn2','3toM','rAY','AxEl', '1bOraT'];

    $random_level = array_values(User::LEVELS);
    shuffle($random_level);

    $mapped_names = array_map(function($name) {
        $result = preg_replace('/[^a-zA-Z]/', '', $name);
        $result = strtolower($result);
        return ucfirst($result);
    }, $names);
    
    $used_name_ids = [];
    $users_array = array_map(function($level) use ($mapped_names, &$used_name_ids) {
        $random_name_id = array_rand($mapped_names);
        $random_name = $mapped_names[$random_name_id];
        
        $suffix_username = "";
        if(in_array($random_name_id, $used_name_ids)) {
            $suffix_username = strval(rand(10, 200));
        }
        $used_name_ids[] = $random_name_id;

        $username = 'user' . strtolower($random_name) . $suffix_username;
        $email = $username . '@photo.com';
        $password = $username . time();
        $hashed_password = password_hash($password, PASSWORD_BCRYPT);
        $age = rand(18, 47);

        $user = array(
            'username' => $username,
            'name' => $random_name,
            'email' => $email,
            'level' => $level,
            'age' => $age,
            'password' => $password,
            'hashed_password' => $hashed_password
        );

        return $user;
    }, $random_level);

    return $users_array;
}

function login(User $user) {
    $_SESSION['id_user'] = $user->getId();
    $_SESSION['username'] = $user->username;
    $_SESSION['level'] = $user->level;
}
function logout() {
    unset($_SESSION['id_user']);
    unset($_SESSION['username']);
    unset($_SESSION['level']);
}
function middlewareAuthorized(?string $role = null) {
    if(!isset($_SESSION['id_user']))
        header("Location: index.php");

    if(!empty($role)) {
        if($_SESSION['level'] != $role) {
            http_response_code(403);
            echo "403 - Permission Denied";
            exit;
        }
    }
}

/**
 * Upload file to server
 * @param  array $file
 * @param string $subfolder_name like 'products/'
 * @return string|false
 */
function upload_file($file, $subfolder_name) {
    if (isset($file) && $file['error'] === UPLOAD_ERR_OK) {
        $uploadDir = MEDIA_ROOT;
        
        // Create new unique filename
        $fileName = pathinfo($file['name'], PATHINFO_FILENAME);
        $fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);
        
        $newFileName = time() . "-" . rand(100, 500) . '-' . $_SESSION['id_user'] . '.' . $fileExtension;

        // media/{subfolder_name}/{new_file.png}
        $uploadFile = $uploadDir . $subfolder_name . $newFileName;
        $filepath_db = $subfolder_name . $newFileName;

        // Get file type
        $fileType = mime_content_type($file['tmp_name']);

        // if subfolder is not exists
        if (!is_dir($uploadDir . $subfolder_name)) {
            mkdir($uploadDir . $subfolder_name, 0775, true);
        }

        // Upload file to server
        if (strpos($fileType, 'image') === 0) {
            if (move_uploaded_file($file['tmp_name'], $uploadFile)) {
                return $filepath_db;
            } else {
                return false;
            }
        }
    }
    return false;
}