<?php

namespace Twnt4All\Support;

use Twnt4All\Support\Paths;

/**
 * Class Log
 * @package Twnt4All\lib
 */
class Journal
{
    const LEVEL_ERROR = 'error';
    const LEVEL_ALL   = 'all';
    const LEVEL_OFF   = 'off';

    /**
     * @var string
     */
    private $log_level;

    /**
     * @var string
     */
    public $filename;

    /**
     * Log constructor.
     */
    private function __construct()
    {
        $this->log_level = defined( 'T4A_DEBUG_LOG' ) ? T4A_DEBUG_LOG : static::LEVEL_OFF;
        // Skip filesystem touch when logging is disabled so no empty log files
        // are created and no .htaccess/uploads directory churn happens.
        if ( $this->log_level === static::LEVEL_OFF ) {
            $this->filename = '';
            return;
        }
        $this->filename = Paths::get_log_file_path();
    }

    /**
     * @var Journal
     */
    private static $instance;

    /**
     * Get single instance of this class.
     *
     * @return Journal
     */
    public static function get_instance()
    {
        if ( empty( static::$instance ) ) {
            static::$instance = new static;
        }

        return static::$instance;
    }

    public function log( $is_event, $log )
    {
        if ( $is_event ) {
            $this->write_all( $log );
        } else {
            $this->write_error( $log );
        }
    }

    public function write_error( $log, $caller = null )
    {
        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }

        $log = '[ERROR]' . $log;

        $this->write( $log, $this->log_level === static::LEVEL_ERROR || $this->log_level === static::LEVEL_ALL, $caller );
    }

    public function write_all( $log, $caller = null )
    {
        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }
        $this->write( $log, $this->log_level === static::LEVEL_ALL, $caller );
    }

    /**
     * Writes a log to the plugins debug.log file if $condition is true. Optionally $file and $line can be passed to write file and line information.
     *
     * @param $log
     * @param bool $condition
     * @param $caller
     */
    public function write( $log, $condition = true, $caller = null )
    {
        if ( !$condition || $this->log_level === static::LEVEL_OFF || empty( $this->filename ) )
            return;

        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }

        // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r -- structured file-based journal used by TWINT support; writes to a plugin-owned log file (mode 3), never to the PHP error log or to STDOUT.
        if ( is_array( $log ) || is_object( $log ) ) {
            error_log( $this->get_formatted_log( print_r( $log, true ), $caller ), 3, $this->filename );
        } else {
            error_log( $this->get_formatted_log( $log, $caller ), 3, $this->filename );
        }
        // phpcs:enable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
    }

    public static function error( $log, $caller = null )
    {
        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }
        static::get_instance()->write_error( $log, $caller );
    }

    public static function event( $log, $caller = null )
    {
        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }
        static::get_instance()->write_all( $log, $caller );
    }

    public static function info( $log, $caller = null )
    {
        if ( !$caller ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- captures the caller frame for structured logging; not a debug print.
            $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
            $caller    = array_shift( $backtrace );
        }
        static::get_instance()->write_all( $log, $caller );
    }

    /**
     * Formats the log information and adds time and file information.
     *
     * @param $log
     * @param $caller
     * @return string
     */
    private function get_formatted_log( $log, $caller )
    {
        $file = str_replace( plugin_dir_path( dirname( __FILE__ ) ), '', $caller[ 'file' ] );
        return '[' . gmdate( "F j, Y, g:i:s a e O" ) . '][' . $file . ':' . $caller[ 'line' ] . ']' . $log . PHP_EOL;
    }
}
