<?php

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

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

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

    // Creates a new user / user profile
    public function CreateUser() {
        $db = Database::connect();
        // Checks if there's posted data
        if (empty($this->request->getPost())) {
            return $this->fail(["error" => "Where data?"], 400);
        }
        // Checks the 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");
        // Checks if the username already exists and adds a new entry into table if the username doesn't exist
        $findExistingData = $db->query('CALL find_user_by_name('.$db->escape($username).')')->getResult();
        if (!empty($findExistingData)) {
            return $this->fail(["error" => "Username already exists!"], 403);
        }
        // Checks if the email already exists and adds a new entry into table if the email doesn't 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!"], 403);
        }
        // Checks if the password exists. If not, then creates a new entry
        $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!"], 201);
    }

    // Logs user in the website / starts the user's session
    public function LogInUser() {
        $db = Database::connect();
        // Checks if there's posted data
        if (empty($this->request->getPost())) {
            return $this->fail(["error" => "Where data?"], 400);
        }
        // Checks the 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");
        // Finds the given email
        $findMail = $db->query('CALL find_email('.$db->escape($email).')')->getResult();
        if (empty($findMail)) {
            return $this->fail(["error" => "Incorrect email or password!"], 406);
        }
        // Finds user and password using the found email
        $findPass = $db->query('CALL get_password_using_email('.$findMail[0]->id.')')->getResult();
        if (empty($findPass)) {
            return $this->fail(["error" => "Incorrect email or password!"], 406);
        }
        // If the password was found then it gets checked if the given password and the user's password are the same
        if (!password_verify($password.$findPass[0]->salt, $findPass[0]->password)) {
            return $this->fail(["error" => "Incorrect email or password!"], 406);
        }
        // Finds if that user exists with the given email and password
        $findUser = $db->query('CALL find_user('.$findMail[0]->id.', '.$findPass[0]->id.')')->getResult();
        if (empty($findUser)) {
            return $this->fail(["error" => "Incorrect email or password!"], 406);
        }
        // Starts a session which stores user data - id and username
        $session = service("session");
        $session->set("userdata", $findUser[0]);
        $session->close();
        return $this->respond(["message" => "Logged in successfully!"], 200);
    }

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

    // Ends user's session which will disable certain features on the website
    public function EndSession() {
        $session = service("session");
        $session->destroy();
        $session->close();
        return $this->respond(["message" => "Logged out successfully!"], 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);
            }
        }
    }
}