<?php
declare(strict_types=1);

/**
 * Minimal SMTP mailer (implicit SSL or STARTTLS) with attachments.
 * No external dependencies.
 */

function smtp_send(array $cfg, array $mail): void
{
  $host = (string)($cfg['host'] ?? '');
  $port = (int)($cfg['port'] ?? 0);
  $secure = (string)($cfg['secure'] ?? 'ssl'); // ssl | tls | none
  $username = (string)($cfg['username'] ?? '');
  $password = (string)($cfg['password'] ?? '');
  $timeout = (int)($cfg['timeout'] ?? 20);
  $helo = (string)($cfg['helo'] ?? (gethostname() ?: 'localhost'));

  if ($host === '' || $port <= 0) {
    throw new RuntimeException('SMTP host/port eksik.');
  }
  if ($username === '' || $password === '') {
    throw new RuntimeException('SMTP username/password eksik.');
  }

  $transport = '';
  if ($secure === 'ssl') {
    $transport = 'ssl://';
  }

  $fp = stream_socket_client(
    $transport . $host . ':' . $port,
    $errno,
    $errstr,
    $timeout,
    STREAM_CLIENT_CONNECT
  );
  if (!$fp) {
    throw new RuntimeException("SMTP baglanti hatasi: {$errstr} ({$errno})");
  }

  stream_set_timeout($fp, $timeout);
  smtp_expect($fp, 220);

  smtp_write($fp, 'EHLO ' . $helo);
  smtp_expect($fp, 250);

  if ($secure === 'tls') {
    smtp_write($fp, 'STARTTLS');
    smtp_expect($fp, 220);
    if (!stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
      throw new RuntimeException('STARTTLS basarisiz.');
    }
    smtp_write($fp, 'EHLO ' . $helo);
    smtp_expect($fp, 250);
  }

  smtp_write($fp, 'AUTH LOGIN');
  smtp_expect($fp, 334);
  smtp_write($fp, base64_encode($username));
  smtp_expect($fp, 334);
  smtp_write($fp, base64_encode($password));
  smtp_expect($fp, 235);

  $from = (string)($mail['from_email'] ?? '');
  $toList = $mail['to'] ?? [];
  if ($from === '' || !is_array($toList) || count($toList) === 0) {
    throw new RuntimeException('From/To eksik.');
  }

  smtp_write($fp, 'MAIL FROM:<' . $from . '>');
  smtp_expect_any($fp, [250, 251]);

  foreach ($toList as $rcpt) {
    $rcpt = (string)$rcpt;
    if ($rcpt === '') continue;
    smtp_write($fp, 'RCPT TO:<' . $rcpt . '>');
    smtp_expect_any($fp, [250, 251]);
  }

  smtp_write($fp, 'DATA');
  smtp_expect($fp, 354);

  $raw = (string)($mail['raw'] ?? '');
  if ($raw === '') {
    throw new RuntimeException('E-posta govdesi bos.');
  }

  // Dot-stuffing
  $raw = preg_replace('/^\./m', '..', $raw);
  if (!str_ends_with($raw, "\r\n")) {
    $raw .= "\r\n";
  }

  fwrite($fp, $raw);
  fwrite($fp, "\r\n.\r\n");
  smtp_expect($fp, 250);

  smtp_write($fp, 'QUIT');
  fclose($fp);
}

function smtp_write($fp, string $line): void
{
  fwrite($fp, $line . "\r\n");
}

function smtp_read($fp): string
{
  $out = '';
  while (($line = fgets($fp, 515)) !== false) {
    $out .= $line;
    if (preg_match('/^\d{3} /', $line)) {
      break;
    }
  }
  return $out;
}

function smtp_expect($fp, int $code): string
{
  $resp = smtp_read($fp);
  $got = (int)substr($resp, 0, 3);
  if ($got !== $code) {
    throw new RuntimeException("SMTP hata: beklenen {$code}, gelen {$got}. Yanit: {$resp}");
  }
  return $resp;
}

function smtp_expect_any($fp, array $codes): string
{
  $resp = smtp_read($fp);
  $got = (int)substr($resp, 0, 3);
  if (!in_array($got, $codes, true)) {
    throw new RuntimeException("SMTP hata: beklenen [" . implode(',', $codes) . "], gelen {$got}. Yanit: {$resp}");
  }
  return $resp;
}

function mail_build_message(array $mail): string
{
  $fromEmail = (string)($mail['from_email'] ?? '');
  $fromName  = (string)($mail['from_name'] ?? '');
  $toList    = $mail['to'] ?? [];
  $subject   = (string)($mail['subject'] ?? '');
  $bodyText  = (string)($mail['body'] ?? '');
  $attachments = $mail['attachments'] ?? [];

  $eol = "\r\n";

  if ($bodyText !== '') {
    $bodyText = preg_replace("/\r\n|\r|\n/", $eol, $bodyText);
  }

  $headers = [];
  $headers[] = 'From: ' . mail_format_address($fromEmail, $fromName);
  $headers[] = 'To: ' . mail_format_address_list($toList);
  $headers[] = 'Subject: ' . mail_encode_header($subject);
  $headers[] = 'Date: ' . date('r');
  $headers[] = 'Message-ID: <' . bin2hex(random_bytes(12)) . '@' . (gethostname() ?: 'localhost') . '>';
  $headers[] = 'MIME-Version: 1.0';

  if (is_array($attachments) && count($attachments) > 0) {
    $boundary = 'b1_' . bin2hex(random_bytes(8));
    $headers[] = 'Content-Type: multipart/mixed; boundary="' . $boundary . '"';

    $parts = [];
    $parts[] = '--' . $boundary . $eol .
      'Content-Type: text/plain; charset="UTF-8"' . $eol .
      'Content-Transfer-Encoding: 8bit' . $eol . $eol .
      ($bodyText !== '' ? $bodyText : ' ') . $eol;

    foreach ($attachments as $att) {
      $name = (string)($att['name'] ?? 'file.bin');
      $mime = (string)($att['mime'] ?? 'application/octet-stream');
      $content = (string)($att['content'] ?? '');
      $b64 = chunk_split(base64_encode($content));

      $parts[] = '--' . $boundary . $eol .
        'Content-Type: ' . $mime . '; name="' . $name . '"' . $eol .
        'Content-Transfer-Encoding: base64' . $eol .
        'Content-Disposition: attachment; filename="' . $name . '"' . $eol . $eol .
        $b64 . $eol;
    }

    $parts[] = '--' . $boundary . '--' . $eol;
    $body = implode('', $parts);
  } else {
    $headers[] = 'Content-Type: text/plain; charset="UTF-8"';
    $headers[] = 'Content-Transfer-Encoding: 8bit';
    $body = $bodyText;
  }

  return implode($eol, $headers) . $eol . $eol . $body;
}

function mail_encode_header(string $text): string
{
  if (preg_match('/[^\x20-\x7E]/', $text)) {
    return '=?UTF-8?B?' . base64_encode($text) . '?=';
  }
  return $text;
}

function mail_format_address(string $email, string $name = ''): string
{
  $email = trim($email);
  $name = trim($name);
  if ($name === '') {
    return $email;
  }
  return '"' . str_replace('"', '', mail_encode_header($name)) . "\" <{$email}>";
}

function mail_format_address_list(array $list): string
{
  $out = [];
  foreach ($list as $addr) {
    $addr = (string)$addr;
    if ($addr !== '') {
      $out[] = $addr;
    }
  }
  return implode(', ', $out);
}
