<?php

namespace RtmMail;

use RtmMailVendor\Dependencies\PHPMailer\PHPMailer\PHPMailer;
use RtmMail\Helpers\LogHelper;
use RtmMail\Mailing\Providers\SmtpHandler;
use RtmMail\Utils\LoggerUtils;

class Mailer
{
    public function send($to, $subject, $message, $headers = '', $attachments = [])
    {
        $args = compact('to', 'subject', 'message', 'headers', 'attachments');
        $logId = $this->save($args);

        if (empty($logId)) {
            return false;
        }

        $settings = get_option('rtm_mail_settings');
        $blockEmails = $settings['block_mails'] ?? false;
        if ($blockEmails) {
            return true;
        }
        $smtp_settings = get_option('rtm_mail_smtp_settings');
        $smtp_enabled = $smtp_settings['smtp_enabled'] ?? false;

        if (!$smtp_enabled) {
            return __('SMTP is not enabled');
        }

        $provider = $smtp_settings['smtp_provider'];
        $handler = null;

        if ($provider === 'other') {
            $handler = new SmtpHandler();

            global $wp_version;

            if (version_compare($wp_version, '5.4.99') > 0) {
                require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
                require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
                require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
                $mailer = new PHPMailer(true);
            } else {
                require_once ABSPATH . WPINC . '/class-phpmailer.php';
                $mailer = new PHPMailer(true);
            }

            $handler->setMailer($mailer);
        }

        if ($handler === null) {
            return false;
        }
        $mail_log = LogHelper::get([
            'post_per_page' => null,
            'where' => [
                'id' => [
                    'type' => '=',
                    'value' => $logId,
                ]
            ]
        ]);

        $mail_data = $mail_log[0] ?? null;

        $handler->setLogId($logId);

        $sender_options = $mail_data['headers']['from'] ?? null;
        if ($sender_options !== null && count(explode('<', $sender_options)) > 1) {
            $send_address = substr(explode('<', $sender_options)[1], 0, -1);
            $send_title = substr(explode('<', $sender_options)[0], 0, -1);
            $handler->setFrom($send_title, $send_address);
        } else {
            $handler->setFrom('', $mail_data['sender']);
        }
        foreach ($mail_data['receiver'] as $to_address) {
            if (!empty($to_address)) {
                $handler->addAddress($to_address);
            }
        }
        // Set all cc addresses
        foreach ($mail_data['cc'] as $cc_address) {
            if (!empty($cc_address)) {
                $handler->addAddress($cc_address, 'cc');
            }
        }
        // Set all bcc addresses
        foreach ($mail_data['bcc'] as $bcc_address) {
            if (!empty($bcc_address)) {
                $handler->addAddress($bcc_address, 'bcc');
            }
        }
        // Set mailing headers
        foreach ($mail_data['headers'] as $name => $value) {
            $handler->setHeader($name, $value);
        }
        // Set body and subject
        $handler->setSubject($mail_data['subject']);
        $handler->setBody($mail_data['body']);

        // Set attachments
        foreach ($mail_data['attachments'] as $attachment) {
            $handler->addAttachment($attachment);
        }

        return $handler->send();
    }
    public function save($args): int
    {
        if (empty($args['headers']) || substr($args["headers"][0], 0, 2) !== 'Id') {
            $settings = get_option('rtm_mail_settings');

            if (is_string($args['to'])) {
                $args['to'] = explode(';', str_replace(',', ';', $args['to']));
            }

            $args['headers'] = is_string($args['headers']) ? explode("\n", str_replace("\\r\\n", "\n", $args['headers'])) : $args['headers'];
            $args['backtrace'] = debug_backtrace();
            $settings['addresses'] = $settings['addresses'] ?? [];

            foreach ($settings['addresses'] as $address) {
                if (!empty($address['address'])) {
                    if ($address['type'] === 'recipient') {
                        $args['to'][] = $address['address'];
                    } else {
                        $args['headers'][] = ucfirst($address['type']) . ': ' . $address['address'];
                    }
                }
            }

            $mail_data = $this->format($args);
            $log_id = LogHelper::save($mail_data);

            Logger::info(
                "Mail has been logged",
                "A new email has been logged by the plugin with given id: #{$log_id}",
                'CustomMailer::catchMail',
                'Log',
                [
                    "mail_log" => $log_id,
                    "request"  => LoggerUtils::filterRequest([
                        'HEADERS' => getallheaders(),
                        'POST'    => $_POST,
                        'GET'     => $_GET,
                    ]),
                    "user"     => LoggerUtils::getUser(),
                ]
            );
            return $log_id;
        }

        return (int) explode(": ", $args["headers"][0])[1]; // <= Log Id
    }

    /**
     * format - formats the mail data to save as a log item
     * @param $args
     * @return array (formatted mail data)
     */
    private function format($args)
    {
        $settings = get_option('rtm_mail_settings');
        $sender_options = $settings['sender_options'] ?? null;
        // Return value array
        $formatted_data = [];
        // Set default sender options if it exists otherwise set the wordpress admin mail
        $formatted_data['sender'] = ($sender_options != null && !empty($sender_options['address'])) ? $sender_options['address'] : get_option('admin_email');
        $sender_title = ($sender_options != null && !empty($sender_options['title'])) ? $sender_options['title'] : get_bloginfo('name');
        $formatted_data['receiver'] = [];
        $formatted_data['cc'] = [];
        $formatted_data['bcc'] = [];
        $formatted_data['subject'] = $args['subject'];
        $formatted_data['body'] = stripslashes(htmlspecialchars_decode($args['message']));
        $formatted_data['attachments'] = [];
        $formatted_data['headers'] = [];
        $formatted_data['backtrace'] = $args['backtrace'] ?? [];
        $formatted_data['created'] = date('Y-m-d H:i:s', time());
        $formatted_data['receiver_status'] = [];

        $args['to'] = $args['to'] ?? [];
        $args['headers'] = $args['headers'] ?? [];
        $args['attachments'] = $args['attachments'] ?? [];

        // Check for every 'to' if it's not empty
        if (is_array($args['to'])) {
            foreach ($args['to'] as $receiver_mail) {
                if (!empty($receiver_mail)) {
                    $formatted_data['receiver'][] = $receiver_mail;
                    // Set receiver status default
                    $formatted_data['receiver_status'][$receiver_mail] = [
                        'opened' => false,
                        'sent' => false,
                    ];
                }
            }
        } else {
            $formatted_data['receiver'][] = $args['to'];
        }


        // Check for every header if the email is not empty and add it to the formatted data
        if (is_array($args['headers'])) {
            foreach ($args['headers'] as $header) {
                if (strpos(strtolower($header), 'from:') !== false) {
                    // Remove header key and trim quotes
                    $from_email = trim(str_replace('from: ', '', strtolower($header)), '\'"');
                    if (!empty($from_email)) {
                        if (count(explode('<', $from_email)) > 1) {
                            $new_mail = rtrim(explode('<', $from_email)[1], '>');
                            $sender_title = explode('<', $from_email)[0];
                            $formatted_data['sender'] = $new_mail;
                        } else {
                            $formatted_data['sender'] = $from_email;
                        }
                    }
                } else if (strpos(strtolower($header), 'bcc:') !== false) {
                    // Remove header key and trim quotes
                    $bcc_email = trim(str_replace('bcc: ', '', strtolower($header)), '\'"');
                    if (!empty($bcc_email)) {
                        $formatted_data['bcc'][] = $bcc_email;
                    }
                } else if (strpos(strtolower($header), 'cc:') !== false) {
                    // Remove header key and trim quotes
                    $cc_email = trim(str_replace('cc: ', '', strtolower($header)), '\'"');
                    if (!empty($cc_email)) {
                        $formatted_data['cc'][] = $cc_email;
                    }
                } else {
                    $header_data = explode(':', str_replace(' ', '', strtolower($header)));
                    $formatted_data['headers'][$header_data[0]] = $header_data[1] ?? '';
                }
            }
        } else {
            if (strpos(strtolower($args['headers']), 'from:') !== false) {
                // Remove header key and trim quotes
                $from_email = trim(str_replace('from: ', '', strtolower($args['headers'])), '\'"');
                if (!empty($from_email)) {
                    if (count(explode('<', $from_email)) > 1) {
                        $new_mail = rtrim(explode('<', $from_email)[1], '>');
                        $sender_title = explode('<', $from_email)[0];
                        $formatted_data['sender'] = $new_mail;
                    } else {
                        $formatted_data['sender'] = $from_email;
                    }
                }
            } else if (strpos(strtolower($args['headers']), 'bcc:') !== false) {
                // Remove header key and trim quotes
                $bcc_email = trim(str_replace('bcc: ', '', strtolower($args['headers'])), '\'"');
                if (!empty($bcc_email)) {
                    $formatted_data['bcc'][] = $bcc_email;
                }
            } else if (strpos(strtolower($args['headers']), 'cc:') !== false) {
                // Remove header key and trim quotes
                $cc_email = trim(str_replace('cc: ', '', strtolower($args['headers'])), '\'"');
                if (!empty($cc_email)) {
                    $formatted_data['cc'][] = $cc_email;
                }
            } else {
                $header_data = explode(':', str_replace(' ', '', strtolower($args['headers'])));
                $formatted_data['headers'][$header_data[0]] = $header_data[1] ?? '';
            }
        }

        // Set the default from header
        $formatted_data['headers']['from'] = $sender_title . ' <' . $formatted_data['sender'] . '>';

        // Set attachments
        $upload = wp_upload_dir();
        $upload_dir = $upload['basedir'] . '/rtm-mail-attachments';
        // Create new dir directory if it doesn't exist
        if (!is_dir($upload_dir)) {
            mkdir($upload_dir, 0755);
        }

        // Loop through every attachment and add it to the formatted data
        $save_attachments = apply_filters('rtm_mail_save_attachments', true);
        $exclude_name = apply_filters('rtm_mail_exclude_attachment_name', []);
        $exclude_extension = apply_filters('rtm_mail_exclude_attachment_extension', []);
        foreach ($args['attachments'] as $attachment) {
            if ($save_attachments) {
                $date = date('Y_m_d H_i_s', time());
                $file_name = substr($attachment, strrpos($attachment, '/'));
                $extension = explode('.', $file_name)[1];
                // Look if the file_name is a part of the exclude_name array
                if (in_array($file_name, $exclude_name) || in_array($extension, $exclude_extension)) {
                    $formatted_data['attachments'][] = $attachment;
                    continue;
                }
                // new file directory and name based on date so its unique (rtm-mail/attachments/filename-YYYY_mm_dd_hh_mm_ss.ext)
                $new_file = $upload_dir . str_replace('.' . $extension, '', $file_name) . '-' . str_replace(' ', '', $date) . '.' . $extension;
                if (copy($attachment, $new_file)) {
                    $formatted_data['attachments'][] = $new_file;
                } else {
                    printf(__('WP Mail Logger FATAL ERROR: Couldn\'t copy file %s to directory. Attachment is not added to logged mail', 'rtm-mail'), $new_file);
                }
            } else {
                $formatted_data['attachments'][] = $attachment;
            }
        }

        return $formatted_data;
    }

    public static function getTrackUrl($log_id, $address)
    {
        return home_url() . '/trackmail/' . $log_id . '/' . $address;
    }

    /**
     * get_backtrace - trace back function calls until the catch_mail call
     * @return array
     */
    private function getBacktrace()
    {
        $result = [];
        $trace = array_reverse(debug_backtrace());
        array_pop($trace); // remove the last call from the trace
        // Loop through every line and add it to the result
        foreach ($trace as $trace_line) {
            if (!isset($trace_line['class']) || $trace_line['class'] !== 'WP_Hook') {
                $call = isset($trace_line['class']) ? $trace_line['class'] . $trace_line['type'] . $trace_line['function'] : $trace_line['function'];
                if (strpos($call, 'require') === false && strpos($call, 'include') === false) {
                    if (isset($trace_line['line']) && isset($trace_line['file'])) {
                        $result[] = [
                            'call' => $call,
                            'line' => $trace_line['line'],
                            'file' => $trace_line['file']
                        ];
                    }
                }
            }
        }
        return $result;
    }
}