<?php
/*
** Job Arranger Manager
** Copyright (C) 2023 Daiwa Institute of Research Ltd. All Rights Reserved.
**
** Licensed to the Apache Software Foundation (ASF) under one or more
** contributor license agreements. See the NOTICE file distributed with
** this work for additional information regarding copyright ownership.
** The ASF licenses this file to you under the Apache License, Version 2.0
** (the "License"); you may not use this file except in compliance with
** the License. You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
**/

namespace App\Controllers;

use Rakit\Validation\Validator;
use App\Utils\Controller;
use App\Utils\Core;
use App\Utils\Constants;
use App\Controllers\Api\LoginApi;
use App\Controllers\Api\ZabbixApi;
use App\Models\GeneralSettingModel;
use App\Models\UserModel;
use App\Services\UserService;
use App\Utils\Util;

/**
 * This controller is used to manage the user authentication.
 *
 * @version    6.1.0
 * @since      Class available since version 6.1.0
 */
class Users extends Controller
{
    private $generalSettingModel;
    private $userModel;
    /**
     * @var Validator
     */
    private $validator;



    public function __construct()
    {
        parent::__construct();
        
        $this->generalSettingModel = new GeneralSettingModel();
        $this->userModel = new UserModel();
        $this->logger = Core::logger();
        $this->validator = new Validator();
    }

    public function apiCheck()
    {
        echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "Authentication is success.", Constants::API_RESPONSE_MESSAGE_OPERATION_SUCCESS]);
    }

    public function getExternalLoginLink()
    {
        echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "Get External Login Link is okay.", Constants::API_RESPONSE_DATA => ZBX_REDIRECT_URL]);
    }

    public function session()
    {
        // Get the session key from the POST request body
        $postData = json_decode(file_get_contents("php://input"), true);
        $sessionId = isset($postData['session_key']) ? $postData['session_key'] : null;

        if ($sessionId) {
            $token = bin2hex(random_bytes(16)); // Secure token generation

            // Store session data securely (e.g., in a file)
            $result = $this->storeSessionData($token, $sessionId);
            if($result){
                echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "Session data is successfully stored.", Constants::API_RESPONSE_DATA => $token]);
            }else{
                echo Util::response(Constants::API_RESPONSE_TYPE_INCOMPLETE, [Constants::API_RESPONSE_MESSAGE => "An unexpected error occurred. Please check the Job Arranger Manager setup and try again.", Constants::API_RESPONSE_DATA => $token]);
            }
        } else {
            // Handle case when the session key is missing in the POST body
            echo Util::response(Constants::API_RESPONSE_TYPE_500, [Constants::API_RESPONSE_MESSAGE => "Session code is not included in the request body."]);
            $this->logger->error("Session key is not included in the request body.", ['controller' => __METHOD__]);
        }
    }

    // Store session data securely in a file
    private function storeSessionData($token, $data)
    {
        $appDir = dirname(__DIR__);
        $directory = $appDir . Constants::TEMP_SESSION_PATH;

        if (!is_dir($directory)) {
            if (!mkdir($directory, 0777, true)) {
                $this->logger->error("Failed to create directory.", ['controller' => __METHOD__]);
                return false;
            }
        }

        // Define file path
        $filePath = $directory . "{$token}.json";

        $jsonData = json_encode($data);
        if ($jsonData === false) {
            $this->logger->error("Failed to encode data to JSON.", ['controller' => __METHOD__]);
            return false;
        }

        if (file_put_contents($filePath, $jsonData) === false) {
            $this->logger->error("Failed to write data to file.", ['controller' => __METHOD__]);
            return false;
        }

        return true;
    }

    public function getSessionData($token)
    {

        $appDir = dirname(__DIR__);
        $directory = $appDir . Constants::TEMP_SESSION_PATH;

        $filePath = $directory . "{$token}.json";

        if (file_exists($filePath)) {
            $sessionData = file_get_contents($filePath);
            unlink($filePath);
            return $sessionData;
        } else {
            return null;
        }
    }

    public function redirectLogin()
    {
        $checkTable = $this->userModel->checkTableExist();
        if($checkTable){
            $param_check = $this->userModel->checkJaParameterTable();
        }
        // $index_check = $this->userModel->checkJaIndexTable();
        if(!$param_check || !$checkTable){
            $response_detail = [
                Constants::AJAX_MESSAGE_DETAIL =>  Constants::DETAIL_DB_INIT_ERROR,
                Constants::AJAX_MESSAGE_OBJECTID => "",
                Constants::AJAX_MESSAGE_DETAIL_TXT => Constants::MESSAGE[Constants::DETAIL_DB_INIT_ERROR],
            ];
            echo Util::response(Constants::API_RESPONSE_TYPE_501, $response_detail);
            $this->logger->error("Zabbix api response format error.", ['controller' => __METHOD__]);
            return;
        }

        $json = file_get_contents('php://input');
        $data = Util::jsonDecode($json);
        if ($data == false) {
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => Constants::SERVICE_MESSAGE_JSON_INVALID]);
            $this->logger->info(Constants::SERVICE_MESSAGE_JSON_INVALID, ['controller' => __METHOD__]);
            return;
        }
        $validation = $this->validator->validate($data["params"], [
            'token' => 'required',
        ]);

        if ($validation->fails()) {
            $errors = $validation->errors();
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => $errors->firstOfAll()]);
            $this->logger->info(Constants::SERVICE_MESSAGE_VALIDATION_FAIL . implode(", ", $errors->firstOfAll()), ['controller' => __METHOD__]);
        } else {
            $token = $data["params"]["token"];
            if ($token) {
                    $decodeSession = json_decode(base64_decode($token));
                    $result = LoginApi::checkAuthentication($decodeSession);
                    $this->logger->info('Check auth fail error'. $result);
                    if ($result == false) {
                        $error = error_get_last();
        
                        // Log detailed error information if the connection failed
                        if (isset($error['message'])) {
                            // Logging error details with the message and URL attempted
                            $this->logger->error("Connection failed: " . $error['message']);
                        }
                        echo Util::response(Constants::API_RESPONSE_ZABBIX_ERROR_CONNECTION_WEB, [Constants::API_RESPONSE_MESSAGE => $error['message']]);
                        $this->logger->error("An error occurred while connecting to Zabbix Web.", ['controller' => __METHOD__]);
                        return;
                    }
                    if ($result == LoginApi::JSON_FAILED_FLAG) {
                        echo Util::response(Constants::API_RESPONSE_TYPE_500, [Constants::API_RESPONSE_MESSAGE => "Zabbix api response format error."]);
                        $this->logger->error("Zabbix api response format error.", ['controller' => __METHOD__]);
                        return;
                    }
                    $final = json_decode($result);
                    if (isset($final->result->sessionid)) {
                        $this->logger->info('check authentication is success.', ['controller' => __METHOD__]);

                        $lang_cut = explode("_", $final->result->lang);
                        $lang = ($lang_cut[1] == "JP") ? "JP" : "EN";

                        $groupResult = json_decode(ZabbixApi::GetUserGroup($final->result->sessionid, $final->result->userid));

                        if (isset($groupResult->error)) {
                            $groupResultArray = [];
                        } else {
                            $groupResultArray = $groupResult->result;
                        }

                        $heartbeatIntervalTime = $this->generalSettingModel->getParameterValue("HEARTBEAT_INTERVAL_TIME");

                        $data = [
                            "userName" => $final->result->username,
                            "language" => strtoupper($lang),
                            "sessionId" => $final->result->sessionid,
                            "appVersion" => Constants::APP_VERSION,
                            "userId" => $final->result->userid,
                            "userType" => $final->result->type,
                            "userLangFull" => strtolower($final->result->lang),
                            "hasUserGroup" => (count($groupResultArray) > 0),
                            "groupList" => $groupResultArray,
                            "heartbeatIntervalTime" => (int) $heartbeatIntervalTime
                        ];
                        $this->logger->info('User redirect process is finished.', ['controller' => __METHOD__, 'user' => $final->result->username]);
                        echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "Authentication is success.", Constants::API_RESPONSE_DATA => $data]);
                    } else {
                        $this->logger->info($result, ['controller' => __METHOD__]);
                        $this->logger->info($final->error->data, ['controller' => __METHOD__]);
                        echo Util::response(Constants::API_RESPONSE_TYPE_INCOMPLETE, [Constants::API_RESPONSE_MESSAGE => $final->error->data]);
                    }

            } else {
                echo Util::response(Constants::API_RESPONSE_TYPE_500, [Constants::API_RESPONSE_MESSAGE => "Session data is not included."]);
                $this->logger->error("Session data is not included.", ['controller' => __METHOD__]);
            }
        }
    }


    /**
     * It validates user information and login.
     *
     * @since   Method available since version 6.1.0
     */
    public function login()
    {
        $this->logger->info('User login process is started.', ['controller' => __METHOD__]);
        $param_check = $this->userModel->checkJaParameterTable();
        $index_check = $this->userModel->checkJaIndexTable();
        if(!$param_check || !$index_check){
            $response_detail = [
                Constants::AJAX_MESSAGE_DETAIL =>  Constants::DETAIL_DB_INIT_ERROR,
                Constants::AJAX_MESSAGE_OBJECTID => "",
                Constants::AJAX_MESSAGE_DETAIL_TXT => Constants::MESSAGE[Constants::DETAIL_DB_INIT_ERROR],
            ];
            echo Util::response(Constants::API_RESPONSE_TYPE_501, $response_detail);
            $this->logger->error("Zabbix api response format error.", ['controller' => __METHOD__]);
            return;
        }
        $json = file_get_contents('php://input');
        $data = Util::jsonDecode($json);
        if ($data == false) {
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => Constants::SERVICE_MESSAGE_JSON_INVALID]);
            $this->logger->info(Constants::SERVICE_MESSAGE_JSON_INVALID, ['controller' => __METHOD__]);
            return;
        }
        $validation = $this->validator->validate($data["params"], [
            'username' => 'required',
            'password' => 'required',
        ]);

        if ($validation->fails()) {
            $errors = $validation->errors();
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => $errors->firstOfAll()]);
            $this->logger->info(Constants::SERVICE_MESSAGE_VALIDATION_FAIL . implode(", ", $errors->firstOfAll()), ['controller' => __METHOD__]);
        } else {
            $result = LoginApi::AuthPathAPI($data["params"]);
            if ($result == false) {
                $error = error_get_last();
        
                // Log detailed error information if the connection failed
                if (isset($error['message'])) {
                    // Logging error details with the message and URL attempted
                    $this->logger->error("Connection failed: " . $error['message']);
                }
                echo Util::response(Constants::API_RESPONSE_ZABBIX_ERROR_CONNECTION_WEB, [Constants::API_RESPONSE_MESSAGE => $error['message']]);
                $this->logger->error("An error occurred while connecting to Zabbix Web.", ['controller' => __METHOD__]);
                return;
            }
            if($result == LoginApi::JSON_FAILED_FLAG){
                echo Util::response(Constants::API_RESPONSE_TYPE_500, [Constants::API_RESPONSE_MESSAGE => "Zabbix api response format error."]);
                $this->logger->error("Zabbix api response format error.", ['controller' => __METHOD__]);
                return;
            }
            $final = json_decode($result);
            if (isset($final->result->sessionid)) {
                $this->logger->info('Authentication is success.', ['controller' => __METHOD__]);

                $lang_cut = explode("_", $final->result->lang);
                $lang = ($lang_cut[1] == "JP") ?  "JP" : "EN";

                $groupResult = json_decode(ZabbixApi::GetUserGroup($final->result->sessionid, $final->result->userid));
                
                if(isset($groupResult->error)){
                    $groupResultArray = [];
                }else{
                    $groupResultArray = $groupResult->result;
                }

                $heartbeatIntervalTime = $this->generalSettingModel->getParameterValue("HEARTBEAT_INTERVAL_TIME");

                $data = [
                    "userName" => $final->result->username,
                    "language" => strtoupper($lang),
                    "sessionId" => $final->result->sessionid,
                    "appVersion" => Constants::APP_VERSION,
                    "userId" => $final->result->userid,
                    "userType" => $final->result->type,
                    "userLangFull" => strtolower($final->result->lang),
                    "hasUserGroup" => (count($groupResultArray) > 0),
                    "groupList" => $groupResultArray,
                    "heartbeatIntervalTime" => (int) $heartbeatIntervalTime
                ];
                $this->logger->info('User login process is finished.', ['controller' => __METHOD__, 'user' => $final->result->username]);
                echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "Authentication is success.", Constants::API_RESPONSE_DATA => $data]);
            } else {
                $this->logger->info($final->error->data, ['controller' => __METHOD__]);
                echo Util::response(Constants::API_RESPONSE_TYPE_INCOMPLETE, [Constants::API_RESPONSE_MESSAGE => $final->error->data]);
            }
        }
    }

    /**
     * It removes the user session id to logout the system.
     *
     * @since   Method available since version 6.1.0
     */
    public function logout()
    {
        $this->logger->info('User logout process is started.', ['controller' => __METHOD__]);

        $json = file_get_contents('php://input');
        $data = Util::jsonDecode($json);
        if ($data == false) {
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => Constants::SERVICE_MESSAGE_JSON_INVALID]);
            $this->logger->info(Constants::SERVICE_MESSAGE_JSON_INVALID, ['controller' => __METHOD__, 'user' => $_SESSION['userInfo']['userName']]);
            return;
        }
        $validation = $this->validator->validate($data, [
            'sessionId' => 'required',
        ]);

        if ($validation->fails()) {
            $errors = $validation->errors();
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => $errors->firstOfAll()]);
            $this->logger->info(Constants::SERVICE_MESSAGE_VALIDATION_FAIL . implode(", ", $errors->firstOfAll()), ['controller' => __METHOD__, 'user' => $_SESSION['userInfo']['userName']]);
        } else {
            $result = LoginApi::logoutAPI($data["sessionId"]);
            $final = json_decode($result);
            if (isset($final->error)) {
                echo Util::response(Constants::API_RESPONSE_TYPE_INCOMPLETE, [Constants::API_RESPONSE_MESSAGE => $final->error->data]);
                $this->logger->info($final->error->data, ['controller' => __METHOD__, 'user' => $_SESSION['userInfo']['userName']]);
            } else {
                if ($final->result) {
                    echo Util::response(Constants::API_RESPONSE_TYPE_OK, [Constants::API_RESPONSE_MESSAGE => "User is successfully logout"]);
                    $this->logger->info("User logout process is successfully finished.", ['controller' => __METHOD__, 'user' => $_SESSION['userInfo']['userName']]);
                }
            }
        }
    }

    public function getUsernamesFromZabbix()
    {
        // Step 1: Read JSON input and decode
        $json = file_get_contents('php://input');
        $data = Util::jsonDecode($json);

        if ($data == false) {
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => Constants::SERVICE_MESSAGE_JSON_INVALID]);
            $this->logger->info(Constants::SERVICE_MESSAGE_JSON_INVALID, ['controller' => __METHOD__]);
            return;
        }

        // Step 2: Validate userId parameter
        $validation = $this->validator->validate($data["params"], [
            'userId' => 'required|numeric',
        ]);

        if ($validation->fails()) {
            $errors = $validation->errors();
            echo Util::response(Constants::API_RESPONSE_TYPE_BAD_REQUEST, [Constants::API_RESPONSE_MESSAGE => $errors->firstOfAll()]);
            $this->logger->info(Constants::SERVICE_MESSAGE_VALIDATION_FAIL . implode(", ", $errors->firstOfAll()), ['controller' => __METHOD__]);
            return;
        }

        // Step 3: Get userId from params
        $userId = $data["params"]["userId"];

        try {
                // Step 4: Call the model to get a single username
                $username = $this->userModel->getUsernamesFromSameGroup($userId);

                if ($username) {
                    echo Util::response(Constants::API_RESPONSE_TYPE_OK, [
                        Constants::API_RESPONSE_MESSAGE => "Fetched Zabbix username successfully.",
                        Constants::API_RESPONSE_DATA => $username
                    ]);
                    $this->logger->info('Fetched Zabbix username.', ['controller' => __METHOD__, 'userId' => $userId]);
                } else {
                    echo Util::response(Constants::API_RESPONSE_TYPE_OK, [
                        Constants::API_RESPONSE_MESSAGE => "No Zabbix username found.",
                        Constants::API_RESPONSE_DATA => null
                    ]);
                    $this->logger->info('No Zabbix username found.', ['controller' => __METHOD__, 'userId' => $userId]);
                }
            } catch (Exception $e) {
                echo Util::response(Constants::API_RESPONSE_TYPE_500, [
                    Constants::API_RESPONSE_MESSAGE => $e->getMessage()
                ]);
                $this->logger->error('Failed to fetch Zabbix username: ' . $e->getMessage(), [
                    'controller' => __METHOD__, 'userId' => $userId
                ]);
            }
    }
}
