<?php
/*
 * Copyright (c) 2022. Micha Espey <tracer@24unix.net>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace App\Repository;

use App\Service\DatabaseConnection;
use App\Entity\User;
use PDO;
use PDOException;

/**
 * Handles CRUD of User class.
 */
class UserRepository
{
    public function __construct(private readonly DatabaseConnection $databaseConnection)
    {
        // empty body
    }

    public function findAll(string $orderBy = 'nick'): array
    {
        $users = [];
        $sql = "
            SELECT id, nick, password, first, last, is_admin
            FROM " . DatabaseConnection::TABLE_USERS . "
            ORDER BY :order";

        try {
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $statement->bindParam(param: ':order', var: $orderBy);

            $statement->execute();
            while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
                $user = new User(
                    nick: htmlspecialchars(string: $result['nick']),
                    password: $result['password'],
                    first: htmlspecialchars(string: $result['first']),
                    last: htmlspecialchars(string: $result['last']),
                    id: $result['id'],
                    isAdmin: $result['is_admin']);
                $users[] = $user;
            }
            return $users;
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }


    public function findByID(int $id): ?User
    {
        $sql = "
            SELECT id, nick, password, first, last, is_admin
            FROM " . DatabaseConnection::TABLE_USERS . "
            WHERE id = :id";

        try {
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $statement->bindParam(param: ':id', var: $id);
            $statement->execute();
            if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
                return new User(
                    nick: htmlspecialchars(string: $result['nick']),
                    password: $result['password'],
                    first: htmlspecialchars(string: $result['first']),
                    last: htmlspecialchars(string: $result['last']),
                    id: $result['id'],
                    isAdmin: $result['is_admin']);
            } else {
                return null;
            }
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }

    public function findByNick(string $nick): ?User
    {
        $nick = strtolower(string: $nick);

        $sql = "
            SELECT id, nick, password, first, last, is_admin
            FROM " . DatabaseConnection::TABLE_USERS . "
            WHERE nick = :nick";

        try {
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $statement->bindParam(param: ':nick', var: $nick);
            $statement->execute();
            if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
                return new User(
                    nick: htmlspecialchars(string: $result['nick']),
                    password: $result['password'],
                    first: htmlspecialchars(string: $result['first']),
                    last: htmlspecialchars(string: $result['last']),
                    id: $result['id'],
                    isAdmin: $result['is_admin']);
            } else {
                return null;
            }
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }

    public function insert(User $user): bool|string
    {
        $sql = "
            INSERT INTO " . DatabaseConnection::TABLE_USERS . " (nick, password, first, last, is_admin)
            VALUES (:nick, :password, :first, :last, :is_admin)";

        try {
            $nick = $user->getNick();
            $password = $user->getPassword();
            $first = $user->getFirst();
            $last = $user->getLast();
            $isAdmin = $user->isAdmin() ? 1 : 0;
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $statement->bindParam(param: ':nick', var: $nick);
            $statement->bindParam(param: ':password', var: $password);
            $statement->bindParam(param: ':first', var: $first);
            $statement->bindParam(param: ':last', var: $last);
            $statement->bindParam(param: ':is_admin', var: $isAdmin);
            $statement->execute();

            return $this->databaseConnection->getConnection()->lastInsertId();
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }


    public function update(User $user): bool
    {
        $id = $user->getId();
        $nick = $user->getNick();
        $first = $user->getFirst();
        $last = $user->getLast();
        $isAdmin = $user->isAdmin() ? 1 : 0;

        if ($user->getPassword()) {
            $password = $user->getPassword();
        } else {
            $current = $this->findByID(id: $id);
            $password = $current->getPassword();
        }

        $sql = "
			UPDATE " . DatabaseConnection::TABLE_USERS . " SET
				nick = :nick,
				password = :password,
				first = :first,
				last = :last,
				is_admin = :is_admin
            WHERE id = :id";

        try {
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $statement->bindParam(param: 'id', var: $id);
            $statement->bindParam(param: 'nick', var: $nick);
            $statement->bindParam(param: 'password', var: $password);
            $statement->bindParam(param: 'first', var: $first);
            $statement->bindParam(param: 'last', var: $last);
            $statement->bindParam(param: 'is_admin', var: $isAdmin);
            return $statement->execute();
        } catch (PDOException $e) {
            echo $e->getMessage();
            return false;
        }
    }


    public function delete(User $user): bool
    {
        $sql = "
            DELETE FROM " . DatabaseConnection::TABLE_USERS . "
            WHERE id = :id";

        try {
            $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
            $id = $user->getId();
            $statement->bindParam(param: 'id', var: $id);
            return $statement->execute();
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }

}