<?php

class EmailService
{
    public function send(string $to, string $subject, string $htmlBody, string $textBody = ''): bool
    {
        $to = trim($to);
        if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Destinatario de email inválido.');
        }

        if ($textBody === '') {
            $textBody = $this->htmlToText($htmlBody);
        }

        if (defined('SMTP_HOST') && trim((string)SMTP_HOST) !== '') {
            return $this->sendSmtp($to, $subject, $htmlBody, $textBody);
        }

        return $this->sendMailFunction($to, $subject, $htmlBody, $textBody);
    }

    private function sendMailFunction(string $to, string $subject, string $htmlBody, string $textBody): bool
    {
        $fromEmail = defined('NOTIFICATIONS_EMAIL_FROM') && NOTIFICATIONS_EMAIL_FROM !== '' ? NOTIFICATIONS_EMAIL_FROM : 'no-reply@app.icylab.cl';
        $fromName = defined('NOTIFICATIONS_EMAIL_FROM_NAME') && NOTIFICATIONS_EMAIL_FROM_NAME !== '' ? NOTIFICATIONS_EMAIL_FROM_NAME : 'ICYLAB OS';
        $boundary = 'b1_' . bin2hex(random_bytes(12));

        $headers = [];
        $headers[] = 'MIME-Version: 1.0';
        $headers[] = 'From: ' . $this->encodeHeader($fromName) . ' <' . $fromEmail . '>';
        $headers[] = 'Content-Type: multipart/alternative; boundary="' . $boundary . '"';

        $body = '--' . $boundary . "\r\n";
        $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
        $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $body .= $textBody . "\r\n\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= "Content-Type: text/html; charset=UTF-8\r\n";
        $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $body .= $htmlBody . "\r\n\r\n";
        $body .= '--' . $boundary . '--';

        $ok = mail($to, $this->encodeHeader($subject), $body, implode("\r\n", $headers));
        if (!$ok) {
            throw new RuntimeException('mail() no pudo aceptar el mensaje.');
        }

        return true;
    }

    private function sendSmtp(string $to, string $subject, string $htmlBody, string $textBody): bool
    {
        $host = trim((string)SMTP_HOST);
        $port = defined('SMTP_PORT') && SMTP_PORT ? (int)SMTP_PORT : 587;
        $secure = defined('SMTP_SECURE') ? strtolower(trim((string)SMTP_SECURE)) : 'tls';
        $timeout = 30;

        $transportHost = ($secure === 'ssl' || $secure === 'smtps' || $port === 465) ? 'ssl://' . $host : $host;
        $errno = 0;
        $errstr = '';
        $socket = stream_socket_client($transportHost . ':' . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT);
        if (!$socket) {
            throw new RuntimeException('No fue posible conectar al SMTP: ' . $errstr);
        }

        stream_set_timeout($socket, $timeout);

        try {
            $this->smtpExpect($socket, [220]);
            $ehloName = defined('SMTP_EHLO_DOMAIN') && SMTP_EHLO_DOMAIN !== '' ? SMTP_EHLO_DOMAIN : 'app.icylab.cl';
            $this->smtpCommand($socket, 'EHLO ' . $ehloName, [250]);

            if ($secure === 'tls' || $secure === 'starttls') {
                $this->smtpCommand($socket, 'STARTTLS', [220]);
                if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                    throw new RuntimeException('No fue posible iniciar TLS con el servidor SMTP.');
                }
                $this->smtpCommand($socket, 'EHLO ' . $ehloName, [250]);
            }

            $username = defined('SMTP_USERNAME') ? trim((string)SMTP_USERNAME) : '';
            $password = defined('SMTP_PASSWORD') ? (string)SMTP_PASSWORD : '';
            if ($username !== '') {
                $this->smtpCommand($socket, 'AUTH LOGIN', [334]);
                $this->smtpCommand($socket, base64_encode($username), [334]);
                $this->smtpCommand($socket, base64_encode($password), [235]);
            }

            $fromEmail = defined('NOTIFICATIONS_EMAIL_FROM') && NOTIFICATIONS_EMAIL_FROM !== '' ? NOTIFICATIONS_EMAIL_FROM : $username;
            if ($fromEmail === '') {
                throw new RuntimeException('Falta configurar NOTIFICATIONS_EMAIL_FROM o SMTP_USERNAME.');
            }

            $message = $this->buildMimeMessage($to, $subject, $htmlBody, $textBody, $fromEmail);

            $this->smtpCommand($socket, 'MAIL FROM:<' . $fromEmail . '>', [250]);
            $this->smtpCommand($socket, 'RCPT TO:<' . $to . '>', [250, 251]);
            $this->smtpCommand($socket, 'DATA', [354]);
            fwrite($socket, $message . "\r\n.\r\n");
            $this->smtpExpect($socket, [250]);
            $this->smtpCommand($socket, 'QUIT', [221]);
        } finally {
            fclose($socket);
        }

        return true;
    }

    private function buildMimeMessage(string $to, string $subject, string $htmlBody, string $textBody, string $fromEmail): string
    {
        $fromName = defined('NOTIFICATIONS_EMAIL_FROM_NAME') && NOTIFICATIONS_EMAIL_FROM_NAME !== '' ? NOTIFICATIONS_EMAIL_FROM_NAME : 'ICYLAB OS';
        $boundary = 'b1_' . bin2hex(random_bytes(12));

        $headers = [];
        $headers[] = 'Date: ' . date(DATE_RFC2822);
        $headers[] = 'To: <' . $to . '>';
        $headers[] = 'From: ' . $this->encodeHeader($fromName) . ' <' . $fromEmail . '>';
        $headers[] = 'Subject: ' . $this->encodeHeader($subject);
        $headers[] = 'MIME-Version: 1.0';
        $headers[] = 'Content-Type: multipart/alternative; boundary="' . $boundary . '"';

        $body = implode("\r\n", $headers) . "\r\n\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
        $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $body .= $this->dotEscape($textBody) . "\r\n\r\n";
        $body .= '--' . $boundary . "\r\n";
        $body .= "Content-Type: text/html; charset=UTF-8\r\n";
        $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $body .= $this->dotEscape($htmlBody) . "\r\n\r\n";
        $body .= '--' . $boundary . '--';

        return $body;
    }

    private function smtpCommand($socket, string $command, array $expectedCodes): string
    {
        fwrite($socket, $command . "\r\n");
        return $this->smtpExpect($socket, $expectedCodes);
    }

    private function smtpExpect($socket, array $expectedCodes): string
    {
        $response = '';
        do {
            $line = fgets($socket, 515);
            if ($line === false) {
                throw new RuntimeException('Respuesta SMTP vacía o conexión cerrada.');
            }
            $response .= $line;
            $code = (int)substr($line, 0, 3);
            $more = isset($line[3]) && $line[3] === '-';
        } while ($more);

        if (!in_array($code, $expectedCodes, true)) {
            throw new RuntimeException('Respuesta SMTP inesperada: ' . trim($response));
        }

        return $response;
    }

    private function htmlToText(string $html): string
    {
        $html = preg_replace('/<br\s*\/?>/i', "\n", $html);
        $html = preg_replace('/<\/p>/i', "\n\n", $html);
        return trim(html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8'));
    }

    private function encodeHeader(string $value): string
    {
        return '=?UTF-8?B?' . base64_encode($value) . '?=';
    }

    private function dotEscape(string $body): string
    {
        return preg_replace('/^\./m', '..', $body);
    }
}
