<?php

namespace App\Services;

use Config\Email as EmailConfig;
use RuntimeException;

/**
 * Sends OTP mail through CI4 Email/SMTP, or a development mailbox directory.
 * Never logs the OTP value.
 */
class OtpMailer implements OtpMailerInterface
{
    public function __construct(
        private readonly EmailConfig $config = new EmailConfig(),
    ) {
    }

    public function isConfigured(): bool
    {
        if (trim($this->config->fromEmail) === '') {
            return false;
        }

        $protocol = strtolower($this->config->protocol);

        if ($protocol === 'mailbox') {
            return $this->mailboxAllowed();
        }

        if ($protocol === 'smtp') {
            return trim($this->config->SMTPHost) !== '';
        }

        return in_array($protocol, ['mail', 'sendmail'], true);
    }

    public function send(string $toEmail, string $otp): void
    {
        if (! $this->isConfigured()) {
            throw new RuntimeException('OTP mail is not configured.');
        }

        $subject = 'Your TIEASY verification code';
        $body    = "Your TIEASY verification code is {$otp}.\n\n"
            . "This code expires in 10 minutes. If you did not request it, you can ignore this email.\n";

        if (strtolower($this->config->protocol) === 'mailbox') {
            if (! $this->mailboxAllowed()) {
                throw new RuntimeException('OTP mailbox is not available in production.');
            }

            $this->writeMailbox($toEmail, $subject, $body);

            return;
        }

        $email = service('email');
        $email->setFrom($this->config->fromEmail, $this->config->fromName ?: 'TIEASY');
        $email->setTo($toEmail);
        $email->setSubject($subject);
        $email->setMessage($body);

        if (! $email->send(false)) {
            $debug = $email->printDebugger(['headers']);
            $email->clear();
            log_message('error', 'OTP email send failed: {0}', [self::stripOtp($debug, $otp)]);

            throw new RuntimeException('OTP email send failed.');
        }

        $email->clear();
    }

    /**
     * Local/dev mailbox files must never be the production delivery path.
     */
    public function mailboxAllowed(?string $environment = null): bool
    {
        return ($environment ?? ENVIRONMENT) !== 'production';
    }

    private function writeMailbox(string $toEmail, string $subject, string $body): void
    {
        $dir = rtrim(WRITEPATH, '/\\') . DIRECTORY_SEPARATOR . 'mailbox';
        if (! is_dir($dir) && ! mkdir($dir, 0750, true) && ! is_dir($dir)) {
            throw new RuntimeException('OTP mailbox is not writable.');
        }

        $safeTo = preg_replace('/[^a-z0-9._-]+/i', '_', $toEmail) ?? 'user';
        $file   = $dir . DIRECTORY_SEPARATOR . date('YmdHis') . '-' . $safeTo . '-' . bin2hex(random_bytes(3)) . '.txt';

        $contents = "To: {$toEmail}\nSubject: {$subject}\n\n{$body}";
        if (file_put_contents($file, $contents) === false) {
            throw new RuntimeException('OTP mailbox write failed.');
        }
    }

    private static function stripOtp(string $debug, string $otp): string
    {
        return str_replace($otp, '******', $debug);
    }
}
