<?php

// =====================================================================
// Title: Authentication Class
// Purpose: This class stands outside the Base Class for password resets
// =====================================================================

require_once "vendor/autoload.php";

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

class Authentication {

    // Variables

    private $settings = "";
    private $ghrconnection = "";

    // =================
    // Class Constructor
    // =================

    public function __construct() {

        // Get Settings

        $this->settings = new SimpleXMLElement((file_exists("xml/settings.xml") ? file_get_contents("xml/settings.xml") : "<settings/>"));

        if ($this->settings->count() === 0) {

            error_log("GHR Authentication Class : Unable to initialize settings");
            exit();
        }

        // Open Database Connections
        $this->ghrconnection = $this->connect();
    }

    // ======================================
    // Return a GHR MySQL Database Connection
    // ======================================

    public function connect() {

        if ($this->settings->Environment == 'Production') {
            $host = 'localhost';
        } else {
            $host = '192.168.1.190';
            $host = '100.67.215.67';
        }

        try {

            $database = (string) ($this->settings->DatabaseName ?? "");
            $user = (string) ($this->settings->DatabaseUser ?? "");
            $password = (string) ($this->settings->DatabasePassword ?? "");

            $connection = new PDO("mysql:host={$host};dbname={$database};charset=utf8", $user, $password);

            $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException | Exception $e) {

            $this->debug($e->getMessage());
            exit();
        }

        return $connection;
    }

    // ========================
    // Generate Random Password
    // ========================

    private function generateRandomPassword($length = 12) {

        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()-_+=';
        $password = '';

        for ($i = 0; $i < $length; $i++) {
            $index = rand(0, strlen($characters) - 1);
            $password .= $characters[$index];
        }

        return $password;
    }

    // ===============
    // Debug an Object
    // ===============

    private function debug($object = "") {

        echo "<pre>";
        print_r($object);
        exit();
    }

    // ==============
    // Reset Password
    // ==============

    public function resetPassword() {

        $username = filter_input(INPUT_POST, "user_name") ?: "";

        if (empty($username)) {
            return false;
        }

        $exists = $this->userExists($username);

        if (!$exists) {
            return false;
        }

        $user = $this->getUser($username);

        $password = $this->resetUsersPassword($user->record->user_serial);

        $this->emailUserPasswordResetLink($user, $password);
        $this->journalEntry("{$username} reset their password.");

        return true;
    }

    // ===========
    // User Exists
    // ===========

    private function userExists($username) {

        $SQL = 'select user_name from view_users where user_name = :username';

        $statement = $this->ghrconnection->prepare($SQL);
        $statement->bindValue(':username', $username, PDO::PARAM_STR);
        $statement->execute();

        $user = $statement->fetch(PDO::FETCH_ASSOC);

        return ($user !== false);
    }

    // =================
    // Get a User Object
    // =================

    private function getUser($username) {

        $SQL = 'select user_serial, user_name, user_email from view_users where user_name = :username';

        $statement = $this->ghrconnection->prepare($SQL);
        $statement->bindValue(':username', $username, PDO::PARAM_STR);
        $statement->execute();

        $user = $statement->fetch(PDO::FETCH_ASSOC);

        $xml = new SimpleXMLElement('<users/>');
        $record = $xml->addChild('record');

        $this->arrayToXml($user, $record);

        return $xml;
    }

    private function arrayToXml(array $data, SimpleXMLElement $xml): void {
        foreach ($data as $key => $value) {
            $key = is_numeric($key) ? 'item' : $key;

            if (is_array($value)) {
                $child = $xml->addChild($key);
                $this->arrayToXml($value, $child);
            } else {
                $xml->addChild($key, htmlspecialchars((string) $value));
            }
        }
    }

    // ========================
    // Reset the Users Password
    // ========================

    private function resetUsersPassword($user_serial) {

        $user_random_password = $this->generateRandomPassword();
        $user_temp_password = password_hash($user_random_password, PASSWORD_DEFAULT);

        $SQL = "update users 
                   set user_password        = :user_password,
                       user_change_password = :user_change_password
                 where user_serial          = :user_serial";

        $statement = $this->ghrconnection->prepare($SQL);

        $statement->bindValue(":user_serial", $user_serial);
        $statement->bindValue(":user_password", $user_temp_password);
        $statement->bindValue(":user_change_password", 1);

        $statement->execute();

        return $user_random_password;
    }

    // =================================
    // Email a users Password Reset Link
    // =================================

    public function emailUserPasswordResetLink($user, $password) {

        if (!$user instanceof SimpleXMLElement) {
            return;
        }

        // Prepare the email.

        $XSLParms['USER-TEMP-PASSWORD'] = $password;

        $to = $user->record->user_email;
        $subject = "DEC International DMS  - Password Reset Request";
        $message = $this->applyXSL($user, $this->getXSL("emailPasswordResetLink"), $XSLParms);

        // Send the Email
        $this->sendEmail($to, $subject, $message);
    }

    // =======================================
    // Send an email with optional attachments
    // =======================================

    public function sendEmail($to = "", $subject = "", $message = "", $attachments = "", $attachment_names = "unknown") {

        if (empty($to) or empty($subject) or empty($message)) {

            return false;
        }

        $SmtpHost = $this->settings->SmtpServer;
        $SmtpUsername = $this->settings->SmtpUserName;
        $SmtpPassword = $this->settings->SmtpPassword;
        $SmtpPort = $this->settings->SmtpPort;

        $email = new PHPMailer(true);

        // Only for development

        $message = "<span style='font-family: Calibri, Arial, sans-serif;'>" . $message . "</span>";

        try {

            //Server settings
            $email->isSMTP();
            $email->Host = $SmtpHost;
            $email->SMTPAuth = true;
            $email->Username = $SmtpUsername; // Your Gmail address
            $email->Password = $SmtpPassword;    // App password from Google
//            $email->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // or 'tls'
            $email->Port = $SmtpPort;

            $email->isHTML(true);
            $email->SetFrom("info@dec-international.com", "DEC-International");

            if (is_array($to)) {
                foreach ($to as $address) {
                    $address = trim(str_replace(" ", "", $address));
                    if (empty($address)) {
                        continue;
                    }
                    $email->addAddress($address);
                }
            } else {
                $email->addAddress($to);
            }

            $email->Subject = $subject;
            $email->Body = $message;

            if (is_array($attachments)) {
                foreach ($attachments as $index => $item) {
                    if (!empty($item)) {
                        $email->addStringAttachment($item, ($attachment_names[$index] ?? "unknown"));
                    }
                }
            } else {
                if (!empty($attachments)) {
                    $email->addStringAttachment($attachments, $attachment_names);
                }
            }

            $success = $email->Send();
        } catch (phpMailerException $e) {

            $this->journalEntry($e->errorMessage());
            $success = false;
        } catch (Exception $e) {

            $this->journalEntry($e->getMessage());
            $success = false;
        }

        return $success;
    }

    // ======================
    // Create a Journal Entry
    // ======================

    public function journalEntry($journal_entry = "") {

        if (empty($journal_entry)) {
            return;
        }

        $SQL = "insert into journal
      
                      ( journal_origin,
                        journal_ip,
                        journal_entry )

              VALUES ( :journal_origin,
                       :journal_ip,
                       :journal_entry )";

        $statement = $this->ghrconnection->prepare($SQL);

        $statement->bindValue(":journal_origin", "Authentication Class");
        $statement->bindValue(":journal_ip", $_SERVER["REMOTE_ADDR"] ?? "");
        $statement->bindValue(":journal_entry", $journal_entry);

        $statement->execute();
    }

    // ==========================================
    // Apply an XSL Stylesheet to an XML Document
    // ==========================================

    public function applyXSL($XMLString = "", $XSLString = "", $XSLParms = "") {

        if ($XMLString instanceof SimpleXMLElement) {
            $XMLString = $XMLString->asXML();
        }

        if (empty($XSLString)) {
            trigger_error("No XSL Stylesheet provided.");
        }

        if (!is_array($XSLParms)) {
            $XSLParms = array();
        }

        $XSLParms["RANDOM_NUMBER"] = rand();
        $XSLParms["TODAY_YYYYMMDD"] = date("Y-m-d");
        $XSLParms["TODAY_MMDDYYYY"] = date("m/d/Y");
        $XSLParms["TODAY_HHMM"] = date("Hi");
        $XSLParms["COPYRIGHT"] = date("Y");

        $XML = new DOMDocument();
        $XSL = new DOMDocument();
        $XSLT = new XSLTProcessor();

        if ($XMLString) {
            $XML->loadXML($XMLString);
        }

        if ($XSLString) {
            $XSL->loadXML($XSLString);
        }

        $XSLT->importStylesheet($XSL);

        if ($XSLParms) {
            $XSLT->setParameter("", $XSLParms);
        }

        $document = $XSLT->transformToXml($XML);

        return $document;
    }

    // ========================
    // Return an XSL Stylesheet
    // ========================

    public function getXSL($document = "") {

        if (trim($document) == "") {
            return "";
        }

        $document = "xsl/" . trim($document) . ".xsl";
        $XSL = (file_exists($document) ? file_get_contents($document) : "");

        return $XSL;
    }
}
