<?php

namespace Velis\Debugger;

use ArrayObject;
use Exception;
use Throwable;
use Velis\App;
use Velis\App\Config;
use Velis\App\User as AppUser;
use Velis\Db\Exception as DbException;
use Velis\Debug;
use Velis\Exception as VelisException;
use Velis\Mail\Mail;
use Velis\Mail\Transport\AwsSes;
use Velis\Model\DataObject\NoColumnsException;
use Velis\Session\SessionInterface;
use Velis\User\TrackerEntry;

class ErrorMailFactory
{
    public function __construct(
        private readonly ?AppUser $user,
        private readonly ?SessionInterface $session,
        private readonly Config $config,
    ) {
    }

    /**
     * @throws NoColumnsException
     */
    public function createMail(Throwable $e): Mail
    {
        $details = '<br /><br /><b>Id sesji:</b> ' . $this->user?->getSessionId() . '<br />';
        $details .= '<b>Id użytkownika:</b> ' . $this->user?->id() . '<br />';
        $details .= '<b>Użytkownik:</b> ' . $this->user?->getFullName() . '<br /><br />';

        if ($e instanceof DbException) {
            $details .= "<hr>\n<b>SQL:</b> <pre>" . $e->getSQL() . "</pre> \n <hr>";

            $params = $e->getParams();
            if (sizeof((array) $params)) {
                $details .= "\n <b>Params:</b> \n";
                $details .= Debug::dump($params instanceof ArrayObject ? $params->getArrayCopy() : $params, true);
            }
        }

        try {
            $tracking = TrackerEntry::getList(null, ['user_id' => $this->user?->id()], null, 5);
        } catch (Exception) {
            $tracking = null;
        }

        $details .= '<hr><b>Backtrace:</b>';
        $dbPassword = $this->config->get('db')->get('password');
        $replacedPassword = str_replace($dbPassword, '***', $e->getTraceAsString());
        $details .= '<pre style="font-size: 12px">' . $replacedPassword . '</pre></div>';

        $details .= "<b>Filter: </b>\n";
        $filter = App::$registry['filter']?->getRawCopy();
        unset($filter['error_handler']);

        $details .= Debug::dump($filter, true) . '<br /><hr><br />';

        if ($e instanceof VelisException) {
            $details .= "<b>" . $e->getMessage() . "</b>\n";
            $details .= Debug::dump($e->getDebugInfo(), true) . '<br /><hr><br />';
        }

        if ($this->user?->getSessionId() && $this->session?->get('settings')) {
            $details .= '<b>Ustawienia: </b><br />' . Debug::dump($this->session?->get('settings'), true);
        }

        if ($this->user?->getSessionId()) {
            $details .= '<hr><b>Uprawnienia: </b><br />' . Debug::dump($this->session?->get('userPrivs'), true);
        }

        if ($tracking) {
            $details .= '<br><table border="1"><tr><td style="text-align: center" colspan="2">';
            $details .= '<b>User tracking</b></td></tr>';
            foreach ($tracking as $track) {
                $details .= '<tr><td>' . $track->url . '</td><td>' . $track->date_added . '</td></tr>';
            }
            $details .= '</table><br /><hr><br />';
        }

        $htmlMsg = $this->getHtmlEmailContent($e);
        $atBody = '<html><head><meta http-equiv="Content-type" content="text/html; charset=utf-8" /></head><body>'
            . $htmlMsg . $details . '</body></html>';

        $notificationsConfig = $this->config->get('notifications');
        $errorNotificationsConfig = $notificationsConfig->get('error');
        if ($errorNotificationsConfig && !AwsSes::isEnabled()) {
            $account = Mail::getErrorNotificationAccount();
            $from = $errorNotificationsConfig->get('email_address');
        } else {
            $account = null;
            $from = $notificationsConfig->get('emailFrom');
        }

        $mail = new Mail($account);
        $mail->setEncoding('utf-8');

        $params['body_text'] = $this->getRawEmailContent($e);
        $params['body_html'] = $htmlMsg;
        $params['attachments'] = ['error_details.html' => str_replace("\n", "\r\n", $atBody)];

        $mail
            ->setContents($params)
            ->setSubject($this->getSubject($e))
            ->setFrom($from, 'Error ' . $this->config->get('settings')->get('instanceAcro'))
            ->addTo($this->config->get('errorReporting')->get('recipient'))
        ;

        return $mail;
    }

    private function getRawEmailContent(Throwable $e): string
    {
        $msg = '* TIME: ' . date('Y-m-d H:i:s') . "\n";
        $msg .= '* CLASS: ' . get_class($e) . "\n";
        $msg .= '* MESSAGE: ' . $e->getMessage() . "\n";
        $msg .= '* FILE: ' . $e->getFile() . "\n";
        $msg .= '* LINE: ' . $e->getLine() . "\n";
        $msg .= '* BROWSER: ' . $_SERVER['HTTP_USER_AGENT'] . "\n";
        $msg .= '* URL: ' . App::getBaseUrl() . $_SERVER['REQUEST_URI'] . "\n";

        if ($e instanceof DbException) {
            $msg .= "\nSQL: " . $e->getSQL() . "\n";

            $params = $e->getParams();
            if (sizeof((array) $params)) {
                $msg .= "Params: \n";
                $msg .= print_r($params instanceof ArrayObject ? $params->getArrayCopy() : $params, true);
            }
        }

        $msg .= "\n*Details in attachment* \n";

        return $msg;
    }

    private function getHtmlEmailContent(Throwable $e): string
    {
        $htmlMsg = '<ul>';
        $htmlMsg .= '<li> TIME: <code>' . date('Y-m-d H:i:s') . '</code></li>';
        $htmlMsg .= '<li> CLASS: <code>' . get_class($e) . '</code></li>';
        if ($e instanceof DbException) {
            $htmlMsg .= '<li> MSG: <code>' . $e->getError() . '</code></li>';
        } else {
            $htmlMsg .= '<li> MSG: <code>' . $e->getMessage() . '</code></li>';
        }
        $htmlMsg .= '<li> FILE: <code>' . $e->getFile() . '</code></li>';
        $htmlMsg .= '<li> LINE: <code>' . $e->getLine() . '</code></li>';
        $htmlMsg .= '<li> BROWSER: <code>' . $_SERVER['HTTP_USER_AGENT'] . '</code></li>';
        $htmlMsg .= '<li> URL: <code>' . App::getBaseUrl() . $_SERVER['REQUEST_URI'] . '</code></ul>';

        return $htmlMsg;
    }

    private function getSubject(Throwable $e): string
    {
        $subject = 'Exception - ' . $e->getMessage();

        if ($e instanceof DbException) {
            $subject = 'Exception - ' . $e->getError();
        } elseif ($e instanceof VelisException) {
            $message = $e->getMessage();
            $info = $e->getDebugInfo();
            $errTypes = ['Duplicated queries', 'Unlimited query'];
            foreach ($errTypes as $errType) {
                if (str_contains($message, $errType)) {
                    if ($errType == 'Duplicated queries') {
                        $subject = 'DQ - ';
                        $query = $info['queries'][0]['sql'];
                    } else {
                        $subject = 'Unlimited query - ';
                        $query = $info['query'];
                    }

                    preg_match('/(FROM|INTO|UPDATE) (\w+\.\w+)/i', $query, $matches);
                    $table = $matches[2];
                    $subject .= $info['module'] . '/' . $info['controller'] . '/' . $info['action'] . ' - ' . $table;
                }
            }
        }

        return $subject;
    }
}
