<?php

namespace classes;
require_once WD . '/modules/email/email_c.php';
require_once WD . '/modules/email/helpers/email_template.php';
require_once WD . '/modules/email/templates/email_template_header.php';
require_once WD . '/modules/email/templates/email_template_footer.php';
require_once WD . '/modules/email/templates/email_template_head.php';
require_once WD . '/modules/email/templates/email_template_stripe_invoice.php';
require_once WD . '/modules/email/templates/email_template_booking_confirmation.php';
require_once WD . '/modules/email/templates/email_template_booking_notification.php';
require_once WD . '/modules/email/templates/email_template_wash_certificate.php';

use email\email_c;
use email\templates\email_template_booking_confirmation;
use email\templates\email_template_booking_notification;
use email\templates\email_template_footer;
use email\templates\email_template_head;
use email\templates\email_template_header;
use email\templates\email_template_stripe_invoice;
use email\templates\email_template_wash_certificate;
use Exception;
use interfaces\email_i;
use JsonException;
use MailerSend\Exceptions\MailerSendAssertException;
use MailerSend\Exceptions\MailerSendException;
use MailerSend\Helpers\Builder\Attachment;
use MailerSend\Helpers\Builder\EmailParams;
use MailerSend\Helpers\Builder\Header;
use MailerSend\Helpers\Builder\Recipient;
use MailerSend\MailerSend;
use objects\bookings_o;
use objects\departments_o;
use objects\users_o;
use Psr\Http\Client\ClientExceptionInterface;

class email implements email_i
{
    /**
     * Configuration for the email service
     * @var email_c
     */
    public email_c $config;


    public function __construct()
    {
        $this->config = new email_c();
    }

    public function testConfigRequest(string $test_recipient): string
    {
        if ($test_recipient) {
            try {
                $this->sendEmail($test_recipient, 'Test recipient', 'Test email', 'This is a test email');
                return 'Email sent successfully to ' . $test_recipient;
            } catch (Exception $e) {
                return 'Email error: ' . $e->getMessage();
            }
        } else {
            return 'No test recipient provided';
        }
    }

    /**
     * Send an email using the preferred email service
     * @param string $to Email address to send the email to
     * @param string $recipient_name Name of the recipient
     * @param string $subject Subject of the email
     * @param string $message Message of the email
     * @throws Exception If an error occurs while sending the email
     */
    public function sendEmail(string $to, string $recipient_name, string $subject, string $message, string $references = null): void
    {
        if ($this->config->mailersend_enabled->getVariableValue()) {
            try {
                // Generate HTML email
                $html = self::generateHtmlHeader();
                // Add email content
                $html .= $message;
                // Add email footer
                $html .= self::generateHtmlFooter();
                $this->sendEmailMailerSend($to, $recipient_name, $subject, $message, $html, $references);
            } catch (JsonException|MailerSendException|ClientExceptionInterface $e) {
                throw new Exception('Error sending email: ' . $e->getMessage());
            }
        } else {
            $this->sendEmailDefault($to, $subject, $message);
        }
    }

    private static function generateHtmlHeader(): false|string
    {
        $email_template_head = (new email_template_head())->generate_html();
        $email_template_header = (new email_template_header())->generate_html();
        return "
        <!DOCTYPE html>
        <html lang='da'>
        <head>
        $email_template_head
        </head>
        <body>
        $email_template_header";
    }

    private static function generateHtmlFooter(): string
    {
        $email_template_footer = (new email_template_footer())->generate_html();
        return "
        $email_template_footer
        </body>
        </html>";
    }

    /**
     * @throws ClientExceptionInterface
     * @throws JsonException
     * @throws MailerSendAssertException
     * @throws MailerSendException
     */
    private function sendEmailMailerSend(string $to, string $recipient_name, string $subject, string $message, string $html = null, string $references = null, array $attachments = []): void
    {
        // Check if the email is blacklisted
        $blacklisted_emails = [
            'invoice.dk@freja.com', // TODO: Make this dynamic.
        ];
        // If the email is blacklisted, return without sending the email
        if (in_array($to, $blacklisted_emails)) {
            return;
        }
        // Send POST request to email service
        $mailersend = new MailerSend(['api_key' => $this->config->mailersend_api_key->getVariableValue()]);

        $recipients = [
            new Recipient($to, $recipient_name)
        ];

        $emailParams = (new EmailParams())
            ->setFrom($this->config->smtp_from->getVariableValue())
            ->setFromName($this->config->smtp_from_name->getVariableValue())
            ->setRecipients($recipients)
            ->setSubject($subject)
            ->setHtml($html ?? $message)
            ->setReplyTo($this->config->smtp_reply_to->getVariableValue())
            ->setReplyToName($this->config->smtp_reply_to_name->getVariableValue());
        if ($attachments) {
            $attachments = array_map(function ($attachment) {
                // Read the data from the path (Attachment[0]) and set the filename (Attachment[1])
                $attachment[0] = file_get_contents($attachment[0]);
                if ($attachment[0] === false) {
                    throw new Exception('Failed to read file: ' . $attachment[0]);
                }
                if (empty($attachment[1])) {
                    throw new Exception('Filename is empty');
                }
                return new Attachment($attachment[0], $attachment[1]);
            }, $attachments);
        }
        if ($attachments) {
            $emailParams->setAttachments($attachments);
        }

        if ($references) {
            $emailParams->setHeaders([
                new Header('References', $references)
            ]);
        }
        $mailersend->email->send($emailParams);
    }

    /**
     * Send an email using the default SMTP service
     * @param string $to Email address to send the email to
     * @param string $subject Subject of the email
     * @param string $message Message of the email
     * @throws Exception If an error occurs while sending the email
     */
    private function sendEmailDefault(string $to, string $subject, string $message): void
    {
        // Send email using default SMTP service
        throw new Exception('Default SMTP service not implemented');
    }

    /**
     * @throws MailerSendException
     * @throws ClientExceptionInterface
     * @throws JsonException
     * @throws MailerSendAssertException
     */
    public function sendStripeInvoiceEmail(string $to, string|null $recipient_name, string $payment_link, $order_id): void
    {
        $recipient_name = $recipient_name ?? 'Kunde';
        $recipient_name = ucfirst($recipient_name);
        // Generate HTML email
        $html = self::generateHtmlHeader();
        // Add email content
        $html .= (new email_template_stripe_invoice(
            $order_id,
            $payment_link,
            $recipient_name
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        $this->sendEmailMailerSend($to, $recipient_name, 'Betalingslink for bestilling #' . $order_id, '', $html);
    }

    /**
     * Send a booking confirmation email
     * @throws MailerSendException
     * @throws ClientExceptionInterface
     * @throws JsonException
     * @throws MailerSendAssertException
     * @throws Exception
     */
    public function sendBookingConfirmationEmail(
        int $booking_id,
    ): void
    {
        // Get the booking details from the database
        $booking = (new bookings_o())->select((int)$booking_id);
        $customer = (new users_o())->getUserByCustomerNumber((int)$booking->customer_number->value());
        $department = (new departments_o())->select((int)$booking->department->value());
        // Generate HTML email
        $html = self::generateHtmlHeader();
        // Add email content
        $html .= (new email_template_booking_confirmation(
            $booking->id,
            $customer->getCustomerName((int)$customer->customer_number->value()),
            $customer->customer_number->value(),
            $booking->contact_email->value(),
            $booking->reference_number->value(),
            $booking->regNrTraekker->value(),
            $booking->regNrTrailer->value(),
            ($booking->washCertificateStatus->value() === 'cancelled' ? 'Nej' : 'Ja'),
            (!empty($booking->washCertificateEmail->value()) ? (string)$booking->washCertificateEmail->value() : ''),
            $booking->date->value(),
            (string)$department->getDepartmentName((int)$department->id),
            (string)$department->getBranding()->address->value(),
            $booking->pickup_bool->value() === 'yes' ? 'Ja' : 'Nej',
            $booking->notes->value(),
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        // Send email
        $this->sendEmailMailerSend(
            $booking->contact_email->value(),
            $customer->getCustomerName((int)$customer->customer_number->value()),
            'Truck Wash Booking ( ID: ' . $booking_id . ', REF: ' . $booking->reference_number->value() . ' )',
            '',
            $html
        );
    }

    /**
     * Send a wash certificate email
     * @throws MailerSendException
     * @throws ClientExceptionInterface
     * @throws JsonException
     * @throws MailerSendAssertException
     */
    public function sendWashCertificateEmail(
        $booking_id,
        $company_name,
        $contact_email,
        $reference_number,
        $wash_certificate_url,
        $wash_certificate_email,
    ): void
    {
        // Generate HTML email
        $html = self::generateHtmlHeader();
        // Add email content
        $html .= (new email_template_wash_certificate(
            $booking_id,
            $company_name,
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        // Attach the wash certificate
        $pdf = (new pdf_store())->download($wash_certificate_url);
        // Send email
        $this->sendEmailMailerSend(
            $wash_certificate_email,
            $company_name,
            'Truck Wash Booking ( ID: ' . $booking_id . ', REF: ' . $reference_number . ' )',
            '',
            $html,
            null,
            [
                [
                    $pdf,
                    'wash_certificate_' . $booking_id . '_' . $reference_number . '.pdf'
                ]
            ]
        );
    }

    /**
     * @throws Exception
     * @throws ClientExceptionInterface
     */
    public function sendBookingNotification(int $booking_id): void
    {
        // Get the booking details from the database
        $booking = (new bookings_o())->select((int)$booking_id);
        $customer = (new users_o())->getUserByCustomerNumber((int)$booking->customer_number->value());
        $department = (new departments_o())->select((int)$booking->department->value());
        // Generate HTML email
        $html = self::generateHtmlHeader();
        $html .= (new email_template_booking_notification(
            $booking->id,
            $customer->getCustomerName((int)$customer->customer_number->value()),
            $customer->customer_number->value(),
            $booking->contact_email->value(),
            $booking->reference_number->value(),
            $booking->regNrTraekker->value(),
            $booking->regNrTrailer->value(),
            ($booking->washCertificateStatus->value() === 'cancelled' ? 'Nej' : 'Ja'),
            (!empty($booking->washCertificateEmail->value()) ? (string)$booking->washCertificateEmail->value() : ''),
            $booking->date->value(),
            (string)$department->getDepartmentName((int)$department->id),
            (string)$department->getBranding()->address->value(),
            $booking->pickup_bool->value() === 'yes' ? 'Ja' : 'Nej',
            $booking->notes->value(),
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        // Send email
        $this->sendEmailMailerSend(
            $department->getBranding()->email->value(),
            $department->getDepartmentName((int)$department->id),
            'Truck Wash Booking ( ID: ' . $booking_id . ', REF: ' . $booking->reference_number->value() . ' )',
            '',
            $html
        );
    }

    /**
     * @throws JsonException
     * @throws Exception
     */
    public function sendOrderBookingConfirmationEmail(\objects\order_bookings_o $order_booking): void
    {
        // Validate the booking object
        $order_booking->requireSelected();
        // Get customer details
        $customer = (new users_o())->getUserByCustomerNumber((int)$order_booking->customer_number->value());
        $customer->requireSelected();
        $recipient_name = $customer->getCustomerName((int)$customer->customer_number->value());
        $recipient_email = $customer->email->value();
        $customer_contact_email = ($recipient_email ?: '');
        // Check if the customer has a booking-specific email provided
        if (!empty($customer->wash_certificate_email->value())) {
            $recipient_email = (string)$customer->wash_certificate_email->value();
        }
        // Get the department details
        $department = (new departments_o())->select((int)$order_booking->department->value());
        $department->requireSelected();
        // Generate HTML email
        $html = self::generateHtmlHeader();
        // Add email content
        $html .= (new email_template_booking_confirmation(
            $order_booking->id,
            $recipient_name,
            $customer->customer_number->value(),
            $customer_contact_email,
            $order_booking->reference->value(),
            $order_booking->reg_1->value(),
            $order_booking->reg_2->value(),
            ($order_booking->containsWashCertificateItem() ? 'Ja' : 'Nej'),
            $recipient_email,
            $order_booking->datetime->value(),
            (string)$department->getDepartmentName((int)$department->id),
            (string)$department->getBranding()->address->value(),
            !!$order_booking->pickup->value() ? 'Ja' : 'Nej',
            $order_booking->note->value()
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        // Send email
        $this->sendEmailMailerSend(
            $recipient_email,
            $department->getDepartmentName((int)$department->id),
            'Truck Wash Booking ( ID: ' . $order_booking->id . ', REF: ' . $order_booking->reference->value() . ' )',
            '',
            $html
        );
    }

    /**
     * @throws JsonException
     * @throws Exception
     */
    public function sendWashCertificateEmailToCustomer(\objects\order_bookings_o $order_booking): void
    {
        // Validate the booking object
        $order_booking->requireSelected();
        if (!$order_booking->hasTransaction()) return; // Only send a wash certificate if the order has been created.
        // Get the order details
        $order = $order_booking->getOrder();
        // Get customer details
        $customer = (new users_o())->getUserByCustomerNumber((int)$order_booking->customer_number->value());
        $customer->requireSelected();
        $recipient_name = $customer->getCustomerName((int)$customer->customer_number->value());
        $recipient_email = $customer->email->value();
        // Check if the customer has a booking-specific email provided
        if (!empty($customer->wash_certificate_email->value())) {
            $recipient_email = (string)$customer->wash_certificate_email->value();
        }
        // Generate HTML email
        $html = self::generateHtmlHeader();
        // Add email content
        $html .= (new email_template_wash_certificate(
            $order_booking->id,
            $recipient_name,
        ))->generate_html();
        // Add email footer
        $html .= self::generateHtmlFooter();
        // Attach the wash certificate
        $attachments = $order->listAttachments();
        $pdf = null;
        foreach ($attachments as $attachment) {
            if ($attachment->isWashCertificate()) {
                $pdf = (new pdf_store())->download($attachment->content->document);
                break;
            }
        }
        if (!$pdf) {
            throw new Exception('Wash certificate PDF not found for order booking ID: ' . $order_booking->id);
        }
        // Send email
        $this->sendEmailMailerSend(
            $recipient_email,
            $recipient_name,
            'Truck Wash Booking ( ID: ' . $order_booking->id . ', REF: ' . $order_booking->reference->value() . ' )',
            '',
            $html,
            null,
            [
                [
                    $pdf,
                    'wash_certificate_' . $order_booking->id . '_' . $order_booking->reference->value() . '.pdf'
                ]
            ]
        );
    }


}