.
 */
declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Monolog\Handler\RotatingFileHandler;
/**
 * Class DebugController
 */
class DebugController extends Controller
{
    use GetConfigurationData;
    /**
     * DebugController constructor.
     */
    public function __construct()
    {
        parent::__construct();
        $this->middleware(IsDemoUser::class)->except(['displayError']);
    }
    /**
     * Show all possible errors.
     *
     * @throws FireflyException
     */
    public function displayError(): void
    {
        app('log')->debug('This is a test message at the DEBUG level.');
        app('log')->info('This is a test message at the INFO level.');
        Log::notice('This is a test message at the NOTICE level.');
        app('log')->warning('This is a test message at the WARNING level.');
        app('log')->error('This is a test message at the ERROR level.');
        Log::critical('This is a test message at the CRITICAL level.');
        Log::alert('This is a test message at the ALERT level.');
        Log::emergency('This is a test message at the EMERGENCY level.');
        throw new FireflyException('A very simple test error.');
    }
    /**
     * Clear log and session.
     *
     * @return Redirector|RedirectResponse
     *
     * @throws FireflyException
     */
    public function flush(Request $request)
    {
        app('preferences')->mark();
        $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range', 'temp-mfa-secret', 'temp-mfa-codes']);
        app('log')->debug('Call cache:clear...');
        Artisan::call('cache:clear');
        app('log')->debug('Call config:clear...');
        Artisan::call('config:clear');
        app('log')->debug('Call route:clear...');
        Artisan::call('route:clear');
        app('log')->debug('Call twig:clean...');
        try {
            Artisan::call('twig:clean');
        } catch (\Exception $e) {  // intentional generic exception
            throw new FireflyException($e->getMessage(), 0, $e);
        }
        app('log')->debug('Call view:clear...');
        Artisan::call('view:clear');
        return redirect(route('index'));
    }
    /**
     * Show debug info.
     *
     * @return Factory|View
     *
     * @throws FireflyException
     */
    public function index()
    {
        $table = $this->generateTable();
        $table = str_replace(["\n", "\t", '  '], '', $table);
        $now   = now(config('app.timezone'))->format('Y-m-d H:i:s');
        // get latest log file:
        $logger = Log::driver();
        // PHPstan doesn't recognize the method because of its polymorphic nature.
        $handlers = $logger->getHandlers(); // @phpstan-ignore-line
        $logContent = '';
        foreach ($handlers as $handler) {
            if ($handler instanceof RotatingFileHandler) {
                $logFile = $handler->getUrl();
                if (null !== $logFile && file_exists($logFile)) {
                    $logContent = file_get_contents($logFile);
                }
            }
        }
        if ('' !== $logContent) {
            // last few lines
            $logContent = 'Truncated from this point <----|'.substr((string)$logContent, -16384);
        }
        return view('debug', compact('table', 'now', 'logContent'));
    }
    /**
     * Flash all types of messages.
     *
     * @return Redirector|RedirectResponse
     */
    public function testFlash(Request $request)
    {
        $request->session()->flash('success', 'This is a success message.');
        $request->session()->flash('info', 'This is an info message.');
        $request->session()->flash('warning', 'This is a warning.');
        $request->session()->flash('error', 'This is an error!');
        return redirect(route('home'));
    }
    private function generateTable(): string
    {
        // system information:
        $system = $this->getSystemInformation();
        $docker = $this->getBuildInfo();
        $app    = $this->getAppInfo();
        $user   = $this->getuserInfo();
        return (string)view('partials.debug-table', compact('system', 'docker', 'app', 'user'));
    }
    private function getSystemInformation(): array
    {
        $maxFileSize   = app('steam')->phpBytes((string)ini_get('upload_max_filesize'));
        $maxPostSize   = app('steam')->phpBytes((string)ini_get('post_max_size'));
        $drivers       = \DB::availableDrivers();
        $currentDriver = \DB::getDriverName();
        return [
            'db_version'      => app('fireflyconfig')->get('db_version', 1)->data,
            'php_version'     => PHP_VERSION,
            'php_os'          => PHP_OS,
            'interface'       => \PHP_SAPI,
            'bcscale'         => bcscale(),
            'display_errors'  => ini_get('display_errors'),
            'error_reporting' => $this->errorReporting((int)ini_get('error_reporting')),
            'upload_size'     => min($maxFileSize, $maxPostSize),
            'all_drivers'     => $drivers,
            'current_driver'  => $currentDriver,
        ];
    }
    private function getBuildInfo(): array
    {
        $return = [
            'is_docker'       => env('IS_DOCKER', false),
            'build'           => '(unknown)',
            'build_date'      => '(unknown)',
            'base_build'      => '(unknown)',
            'base_build_date' => '(unknown)',
        ];
        try {
            if (file_exists('/var/www/counter-main.txt')) {
                $return['build'] = trim((string)file_get_contents('/var/www/counter-main.txt'));
            }
        } catch (\Exception $e) { // @phpstan-ignore-line
            app('log')->debug('Could not check build counter, but thats ok.');
            app('log')->warning($e->getMessage());
        }
        try {
            if (file_exists('/var/www/build-date-main.txt')) {
                $return['build_date'] = trim((string)file_get_contents('/var/www/build-date-main.txt'));
            }
        } catch (\Exception $e) { // @phpstan-ignore-line
            app('log')->debug('Could not check build date, but thats ok.');
            app('log')->warning($e->getMessage());
        }
        if ('' !== (string)env('BASE_IMAGE_BUILD')) {
            $return['base_build'] = env('BASE_IMAGE_BUILD');
        }
        if ('' !== (string)env('BASE_IMAGE_DATE')) {
            $return['base_build_date'] = env('BASE_IMAGE_DATE');
        }
        return $return;
    }
    private function getAppInfo(): array
    {
        $userGuard = config('auth.defaults.guard');
        $config         = app('fireflyconfig')->get('last_rt_job', 0);
        $lastTime       = (int)$config->data;
        $lastCronjob    = 'never';
        $lastCronjobAgo = 'never';
        if ($lastTime > 0) {
            $carbon         = Carbon::createFromTimestamp($lastTime);
            $lastCronjob    = $carbon->format('Y-m-d H:i:s');
            $lastCronjobAgo = $carbon->locale('en')->diffForHumans(); // @phpstan-ignore-line
        }
        return [
            'debug'              => var_export(config('app.debug'), true),
            'audit_log_channel'  => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
            'default_language'   => (string)config('firefly.default_language'),
            'default_locale'     => (string)config('firefly.default_locale'),
            'remote_header'      => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A',
            'remote_mail_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_email') : 'N/A',
            'stateful_domains'   => implode(', ', config('sanctum.stateful')),
            // the dates for the cron job are based on the recurring cron job's times.
            // any of the cron jobs will do, they always run at the same time.
            // but this job is the oldest, so the biggest chance it ran once
            'last_cronjob'     => $lastCronjob,
            'last_cronjob_ago' => $lastCronjobAgo,
        ];
    }
    private function getuserInfo(): array
    {
        $userFlags = $this->getUserFlags();
        // user info
        $userAgent = request()->header('user-agent');
        // set languages, see what happens:
        $original       = setlocale(LC_ALL, '0');
        $localeAttempts = [];
        $parts          = app('steam')->getLocaleArray(app('steam')->getLocale());
        foreach ($parts as $code) {
            $code = trim($code);
            app('log')->debug(sprintf('Trying to set %s', $code));
            $result                = setlocale(LC_ALL, $code);
            $localeAttempts[$code] = $result === $code;
        }
        setlocale(LC_ALL, (string)$original);
        return [
            'user_id'         => auth()->user()->id,
            'user_count'      => User::count(),
            'user_flags'      => $userFlags,
            'user_agent'      => $userAgent,
            'locale_attempts' => $localeAttempts,
            'locale'          => app('steam')->getLocale(),
            'language'        => app('steam')->getLanguage(),
            'view_range'      => app('preferences')->get('viewRange', '1M')->data,
        ];
    }
    private function getUserFlags(): string
    {
        $flags = [];
        /** @var User $user */
        $user = auth()->user();
        // has liabilities
        if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) {
            $flags[] = ':credit_card:';
        }
        // has piggies
        if ($user->piggyBanks()->count() > 0) {
            $flags[] = ':pig:';
        }
        // has stored reconciliations
        $type = TransactionType::whereType(TransactionType::RECONCILIATION)->first();
        if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) {
            $flags[] = ':ledger:';
        }
        // has used importer?
        // has rules
        if ($user->rules()->count() > 0) {
            $flags[] = ':wrench:';
        }
        // has recurring transactions
        if ($user->recurrences()->count() > 0) {
            $flags[] = ':clock130:';
        }
        // has groups
        if ($user->objectGroups()->count() > 0) {
            $flags[] = ':bookmark_tabs:';
        }
        // uses bills
        if ($user->bills()->count() > 0) {
            $flags[] = ':email:';
        }
        return implode(' ', $flags);
    }
}