#!/usr/bin/env python3
"""Minimal SMTP test server: speaks just enough real SMTP (EHLO, STARTTLS,
AUTH LOGIN, MAIL/RCPT/DATA) to genuinely exercise Mailer's native client,
instead of trusting the PHP code's protocol handling on faith. Not a mock
in the sense of stubbing the interface — it's a real socket-level server.

Usage: mock_smtp_server.py <port> <out_jsonl_path> [--tls cert.pem key.pem] [--require-auth user pass] [--reject-auth]
Logs one JSON line per accepted message to out_jsonl_path, then keeps
listening (handles connections sequentially; fine for tests).
"""
import socket, ssl, sys, json, base64, threading, time

def serve(port, out_path, tls_cert=None, tls_key=None, require_auth=None, reject_auth=False):
    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind(('127.0.0.1', port))
    srv.listen(5)

    def handle(conn):
        raw_sock = conn  # keep a handle to the real socket object for the STARTTLS upgrade
        f = conn.makefile('rwb', buffering=0)
        def send(line): f.write((line + '\r\n').encode())
        def readline(): return f.readline().decode(errors='replace').rstrip('\r\n')

        send('220 mock.local ESMTP')
        mail_from = rcpt_to = None
        authed = require_auth is None
        while True:
            line = readline()
            if line == '':
                return
            upper = line.upper()
            if upper.startswith('EHLO') or upper.startswith('HELO'):
                send('250-mock.local greets you')
                if tls_cert and not isinstance(conn, ssl.SSLSocket):
                    send('250-STARTTLS')
                send('250-AUTH LOGIN')
                send('250 8BITMIME')
            elif upper == 'STARTTLS' and tls_cert:
                send('220 ready to start TLS')
                ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
                ctx.load_cert_chain(tls_cert, tls_key)
                conn = ctx.wrap_socket(raw_sock, server_side=True)
                f = conn.makefile('rwb', buffering=0)
            elif upper == 'AUTH LOGIN':
                send('334 VXNlcm5hbWU6')  # "Username:"
                user = base64.b64decode(readline()).decode()
                send('334 UGFzc3dvcmQ6')  # "Password:"
                pw = base64.b64decode(readline()).decode()
                if reject_auth or (require_auth and (user, pw) != require_auth):
                    send('535 Authentication failed')
                    authed = False
                else:
                    send('235 Authentication successful')
                    authed = True
            elif upper.startswith('MAIL FROM'):
                mail_from = line.split(':', 1)[1].strip()
                send('250 OK')
            elif upper.startswith('RCPT TO'):
                rcpt_to = line.split(':', 1)[1].strip()
                send('250 OK')
            elif upper == 'DATA':
                send('354 End data with <CR><LF>.<CR><LF>')
                lines = []
                while True:
                    l = readline()
                    if l == '.':
                        break
                    lines.append(l)
                raw_body = '\n'.join(lines)
                with open(out_path, 'a') as out:
                    out.write(json.dumps({
                        'mail_from': mail_from, 'rcpt_to': rcpt_to,
                        'authed': authed, 'raw': raw_body,
                        'tls': isinstance(conn, ssl.SSLSocket),
                    }) + '\n')
                send('250 OK: queued')
            elif upper == 'QUIT':
                send('221 Bye')
                return
            else:
                send('500 unrecognized command')

    while True:
        conn, _ = srv.accept()
        try:
            handle(conn)
        except Exception as e:
            sys.stderr.write(f'mock_smtp_server error: {e}\n')
        finally:
            try: conn.close()
            except Exception: pass

if __name__ == '__main__':
    port = int(sys.argv[1])
    out_path = sys.argv[2]
    tls_cert = tls_key = None
    require_auth = None
    reject_auth = '--reject-auth' in sys.argv
    args = sys.argv[3:]
    if '--tls' in args:
        i = args.index('--tls')
        tls_cert, tls_key = args[i+1], args[i+2]
    if '--require-auth' in args:
        i = args.index('--require-auth')
        require_auth = (args[i+1], args[i+2])
    serve(port, out_path, tls_cert, tls_key, require_auth, reject_auth)
