<?php

namespace App\Controllers;
use Config\Database;
use CodeIgniter\RESTful\ResourceController;
use App\Controllers\PermissionController;

class UserController extends ResourceController
{
    /*
        Created: 2025-02-27
        Author: Kārlis Grīnvalds
        Last edited: 2026-05-06
        Edited by: Kārlis Grīnvalds
    */

    // Used for creating new user accounts and starting, checking and ending user sessions

    // Creates a new user profile
    public function CreateUser() {
        // Checks posted data
        if (empty($this->request->getPost())) {
            return $this->fail(["error" => "Where data?"], 400);
        }
        // Checks posted data for illegal characters
        $username = $this->request->getPost("username");
        $this->CheckCharacters(unpack("C*", $username), "username");
        $email = $this->request->getPost("email");
        $this->CheckCharacters(unpack("C*", $email), "email");
        $password = $this->request->getPost("password");
        $this->CheckCharacters(unpack("C*", $password), "password");
        if (strlen($username) < 4 || strlen($username) > 20) {
            return $this->fail(["error" => "Username not allowed!", "csrfHash" => csrf_hash()], 403);
        }
        if (strlen($email) < 5 || strlen($email) >= 50) {
            return $this->fail(["error" => "Email not allowed!", "csrfHash" => csrf_hash()], 403);
        }
        if (strlen($password) < 8) {
            return $this->fail(["error" => "Password too short!", "csrfHash" => csrf_hash()], 403);
        }
        $db = Database::connect();
        // Does the given username exist
        $findExistingData = $db->query('CALL find_user_by_name('.$db->escape($username).')')->getResult();
        if (!empty($findExistingData)) {
            return $this->fail(["error" => "Username already exists!", "csrfHash" => csrf_hash()], 403);
        }
        // Does the given email exist
        $email = array_values((array) $db->query('SELECT post_email('.$db->escape($email).')')->getResult()[0])[0];
        if ($email == 0) {
            return $this->fail(["error" => "Email already exists!", "csrfHash" => csrf_hash()], 403);
        }
        // Creates salted hashed password
        $baseString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $salt = substr(str_shuffle($baseString), 0, 10);
        $password = array_values((array) $db->query('SELECT post_password('.$db->escape(password_hash($password.$salt, PASSWORD_DEFAULT)).', '.$db->escape(strval($salt)).')')->getResult()[0])[0];
        // Prepares data for insertion into user table
        $data = [
            "username" => strval($username),
            "email" => strval($email),
            "password" => strval($password)
        ];
        $db->query('CALL post_user('.$db->escape(json_encode($data)).')');
        return $this->respond(["message" => "Created successfully!", "csrfHash" => csrf_hash()], 201);
    }

    // Start user session (log in)
    public function LogInUser() {
        // Checks posted data
        if (empty($this->request->getPost())) {
            return $this->fail(["error" => "Where data?", "csrfHash" => csrf_hash()], 400);
        }
        // Checks posted data for illegal characters
        $email = $this->request->getPost("email");
        $this->CheckCharacters(unpack("C*", $email), "email");
        $password = $this->request->getPost("password");
        $this->CheckCharacters(unpack("C*", $password), "password");
        $db = Database::connect();
        // Finds email
        $findMail = $db->query('CALL find_email('.$db->escape($email).')')->getResult();
        if (empty($findMail)) {
            return $this->fail(["error" => "Incorrect email or password!", "csrfHash" => csrf_hash()], 406);
        }
        // Finds password using email id
        $findPass = $db->query('CALL get_password_using_email('.$findMail[0]->id.')')->getResult();
        if (empty($findPass)) {
            return $this->fail(["error" => "Incorrect email or password!", "csrfHash" => csrf_hash()], 406);
        }
        // Does the given password matched user's password
        if (!password_verify($password.$findPass[0]->salt, $findPass[0]->password)) {
            return $this->fail(["error" => "Incorrect email or password!", "csrfHash" => csrf_hash()], 406);
        }
        // Finds user using password and email id
        $findUser = $db->query('CALL find_user('.$findMail[0]->id.', '.$findPass[0]->id.')')->getResult();
        if (empty($findUser)) {
            return $this->fail(["error" => "Incorrect email or password!", "csrfHash" => csrf_hash()], 406);
        }
        // Starts a session which stores user data - id and username
        $session = service("session");
        $session->set("userdata", $findUser[0]);
        return $this->respond(["message" => "Logged in successfully!", "csrfHash" => csrf_hash()], 200);
    }

    // Checks user's session data
    public function CheckSession() {
        $session = service("session");
        // If session exists, then the function will return user data - id and username
        if ($session->has("userdata")) {
            return $this->respond($session->get("userdata"), 200);
        }
        return $this->respond(["message" => "User not logged in!"], 200);
    }

    // Ends user's session
    public function EndSession() {
        $session = service("session");
        $session->destroy();
        return $this->respond(["message" => "Logged out successfully!", "csrfHash" => csrf_hash()], 200);
    }

    // Check for any illegal characters in an array of ASCII values, function parameters are array of all characters from a string and type for which characters to check for
    public function CheckCharacters($array = null, $type = null) {
        foreach ($array as $num) {
            $noerror = false;
            if ($type != "password") {
                // 0-9
                if ($num >= 48 && $num <= 57) {
                    $noerror = true;
                }
                // A-Z
                if ($num >= 65 && $num <= 90) {
                    $noerror = true;
                }
                // a-z
                if ($num >= 97 && $num <= 122) {
                    $noerror = true;
                }
                if ($type == "email") {
                    // . @ - _
                    if ($num == 46 || $num == 64 || $num == 45 || $num == 95) {
                        $noerror = true;
                    }
                }
            }
            else {
                // Checks all characters except for space and delete for password
                if ($num >= 33 && $num <= 126) {
                    $noerror = true;
                }
            }
            // If any illegal character is found, then it sends an error
            if ($noerror != true) {
                return $this->fail(["error" => "This ".$type." is not allowed!"], 403);
            }
        }
    }

    public function CheckUserPermissions() {
        $test = new PermissionController();
        $perms = $test->GetUserPermissions($this->request->getGet("topic"));
        $select = $this->request->getGet("select");
        $response = [];
        if (sizeof($perms) <= 0) {
            return $this->respond($response, 200);
        }
        foreach ($select as $option) {
            $response[$option] = $perms[$option];
        }
        return $this->respond($response, 200);
    }
}