<?php
/*
 *   $Id$
 *
 *   AbanteCart, Ideal OpenSource Ecommerce Solution
 *   http://www.AbanteCart.com
 *
 *   Copyright © 2011-2025 Belavier Commerce LLC
 *
 *   This source file is subject to Open Software License (OSL 3.0)
 *   License details are bundled with this package in the file LICENSE.txt.
 *   It is also available at this URL:
 *   <http://www.opensource.org/licenses/OSL-3.0>
 *
 *  UPGRADE NOTE:
 *    Do not edit or add to this file if you wish to upgrade AbanteCart to newer
 *    versions in the future. If you wish to customize AbanteCart for your
 *    needs, please refer to http://www.AbanteCart.com for more information.
 */

use JetBrains\PhpStorm\NoReturn;

if (!defined('DIR_CORE')) {
    header('Location: static_pages/');
}

class AError
{

    /**
     * error code
     *
     * @var int
     */
    public $code;

    /**
     *  error message
     *
     * @var string
     */
    public $msg;

    /**
     * registry to provide access to cart objects
     *
     * @var object Registry
     */
    protected $registry = null;

    /**
     * array of error descriptions by code
     *
     * @var array
     */
    protected $error_descriptions;

    protected $version;

    /**
     * error constructor.
     *
     * @param  $msg - error message
     * @param  $code - error code
     */
    public function __construct($msg, $code = AC_ERR_USER_ERROR)
    {

        $backtrace = debug_backtrace();

        $this->code = $code;
        $this->msg = $msg;
        $trace_limit = 5;
        $this->msg .= "\nTrace:\n";
        foreach ($backtrace as $k => $b) {
            if ($k >= $trace_limit) {
                break;
            }
            $file = $b['file'] ?? '';
            $line = $b['line'] ?? '';
            $this->msg .= "\t#" . $k . " " . $file . ' on line ' . $line . "\n";
        }

        if (class_exists('Registry')) {
            $this->registry = Registry::getInstance();
        }
        //TODO: use registry object instead?? what if registry not accessible?
        $this->error_descriptions = $GLOBALS['error_descriptions'];
        if (!isset($this->error_descriptions[0])) {
            $this->error_descriptions[0] = 'Unknown Error';
        }

        $this->version = 'AbanteCart core v.' . VERSION;
    }

    /**
     * add an error message to the debug log
     *
     * @return AError
     */
    public function toDebug()
    {
        ADebug::error(
            $this->error_descriptions[$this->code],
            $this->code,
            $this->msg
        );
        return $this;
    }

    /**
     * write an error message to the log file
     *
     * @return AError
     * @throws AException
     */
    public function toLog()
    {
        if (!is_object($this->registry) || !$this->registry->has('log')) {
            if (class_exists('ALog')) {
                $log = new ALog(DIR_SYSTEM . 'logs' . DS . 'error.txt');
            } else {
                //we have error way a head of system start
                echo $this->error_descriptions[$this->code] . ':  ' . $this->msg;
                return $this;
            }
        } else {
            $log = $this->registry->get('log');
        }
        $log->write($this->error_descriptions[$this->code] . ':  ' . $this->version . ' ' . $this->msg);
        return $this;
    }

    /**
     * add an error message to messages
     *
     * @param string $subject
     *
     * @return AError
     * @throws AException
     */
    public function toMessages($subject = '')
    {
        if (is_object($this->registry) && $this->registry->has('messages')) {
            /** @var AMessage $messages */
            $messages = $this->registry->get('messages');
            $title = $subject ?: $this->error_descriptions[$this->code];
            $messages->saveError($title, $this->msg);
        }
        return $this;
    }

    /**
     * send an error message to mail
     *
     * @return AError
     */
    public function toMail()
    {
        //This is for future development
        return $this;
    }

    /**
     * add error message to JSON output
     *
     * @param string $status_text_and_code - any human-readable text string with 3 digits at the end to represent HTTP response code
     *                                     For ex.
     *                                     VALIDATION_ERROR_406
     *
     * @param array $err_data - array with error text and params to control ajax
     *                                     error_code -> HTTP error code if missing in $status_text_and_code
     *                                     error_title -> Title for error dialog and header (error constant used be default)
     *                                     error_text -> Error message ( Class construct used by default )
     *                                     show_dialog -> true to show dialog with error
     *                                     reset_value -> true to reset values in a field (if applicable)
     *                                     reload_page -> true to reload page after dialog close
     *                                     TODO: Add redirect_url on dialog close
     *
     * @throws AException
     */
    #[NoReturn]
    public function toJSONResponse($status_text_and_code, $err_data = [])
    {
        //detect HTTP response status code based on readable text status
        preg_match('/(\d+)$/', $status_text_and_code, $match);
        if (!$match[0]) {
            if (empty($err_data['error_code'])) {
                $err_data['error_code'] = 400;
            }
        } else {
            $err_data['error_code'] = (int)$match[0];
        }

        if (empty($err_data['error_title'])) {
            $err_data['error_title'] = $this->error_descriptions[$this->code];
        }
        if (empty($err_data['error_text'])) {
            $err_data['error_text'] = $this->msg;
        }
        $http_header_txt = 'HTTP/1.1 ' . (int)$err_data['error_code'] . ' ' . $err_data['error_title'];

        if (is_object($this->registry) && $this->registry->has('response')) {
            /** @var AResponse $response */
            $response = $this->registry->get('response');
            /** @var ALoader $load */
            $load = $this->registry->get('load');
            $response->addHeader($http_header_txt);
            $response->addJSONHeader();
            $load->library('json');
            $response->setOutput(AJson::encode($err_data));
            $response->output();
        } else {
            //for some reason we do not have a registry. do direct output and exit
            if (!headers_sent()) {
                header($http_header_txt);
                header('Content-Type: application/json');
            }
            include_once(DIR_CORE . 'lib' . DS . 'json.php');
            echo AJson::encode($err_data);
        }
        exit;
    }
}