#!/usr/bin/env python3
"""
Upload en activeer de Sieve-mailrule op australius.nl via ManageSieve (RFC 5804).
Poort: 4190 met STARTTLS.

Gebruik: python3 upload_sieve.py
"""

import socket
import ssl
import sys
import base64
import config
from pathlib import Path
SCRIPT_NAME  = "mailrules"
SCRIPT_FILE  = Path(__file__).parent / "mailrules.sieve"

# Als fileinto-paden "INBOX." als prefix nodig hebben op deze server:
# Zet SIEVE_PREFIX op "INBOX." en het script past de paden automatisch aan.
# Zet op "" als de server paden zonder prefix accepteert (meest gangbaar).
SIEVE_PREFIX = ""


class ManageSieveClient:
    """Minimale ManageSieve-client (RFC 5804)."""

    def __init__(self, host: str, port: int):
        self._sock = None
        self._host = host
        self._port = port

    def connect(self):
        raw = socket.create_connection((self._host, self._port), timeout=15)
        self._sock = raw
        greeting = self._readline()
        if not greeting.startswith("OK"):
            raise ConnectionError(f"Onverwachte greeting: {greeting!r}")
        # Lees eventuele capability-regels na de greeting
        self._read_capabilities()

    def starttls(self):
        self._send("STARTTLS")
        resp = self._readline()
        if not resp.startswith("OK"):
            raise ConnectionError(f"STARTTLS geweigerd: {resp!r}")
        context = ssl.create_default_context()
        self._sock = context.wrap_socket(self._sock, server_hostname=self._host)
        # Nieuwe capabilities na TLS
        self._read_capabilities()

    def authenticate(self, username: str, password: str):
        creds = base64.b64encode(f"\x00{username}\x00{password}".encode()).decode()
        self._send(f'AUTHENTICATE "PLAIN" "{creds}"')
        resp = self._readline()
        if not resp.startswith("OK"):
            raise PermissionError(f"Authenticatie mislukt: {resp!r}")

    def put_script(self, name: str, content: str):
        encoded = content.encode("utf-8")
        self._send(f'PUTSCRIPT "{name}" {{{len(encoded)}+}}')
        self._sock.sendall(encoded)
        resp = self._readline()
        if not resp.startswith("OK"):
            raise RuntimeError(f"PUTSCRIPT mislukt: {resp!r}")

    def set_active(self, name: str):
        self._send(f'SETACTIVE "{name}"')
        resp = self._readline()
        if not resp.startswith("OK"):
            raise RuntimeError(f"SETACTIVE mislukt: {resp!r}")

    def list_scripts(self) -> list[str]:
        self._send("LISTSCRIPTS")
        scripts = []
        while True:
            line = self._readline()
            if line.startswith("OK"):
                break
            scripts.append(line)
        return scripts

    def logout(self):
        try:
            self._send("LOGOUT")
        except Exception:
            pass
        try:
            self._sock.close()
        except Exception:
            pass

    def _send(self, cmd: str):
        self._sock.sendall((cmd + "\r\n").encode())

    def _readline(self) -> str:
        buf = b""
        while not buf.endswith(b"\n"):
            chunk = self._sock.recv(1)
            if not chunk:
                break
            buf += chunk
        return buf.decode("utf-8", errors="replace").strip()

    def _read_capabilities(self):
        """Lees en negeer capability-regels tot een lege regel of einde."""
        # Capabilities eindigen met een lege regel of worden gevolgd door authenticatie
        # We lezen tot we iets tegenkomen dat niet op een capability lijkt
        # (dit is een vereenvoudiging; volledige RFC 5804 parsing is complexer)
        pass


def apply_prefix(script: str, prefix: str) -> str:
    """Voeg SIEVE_PREFIX toe aan alle fileinto-paden als dat nodig is."""
    if not prefix:
        return script
    import re
    def add_prefix(m):
        path = m.group(1)
        if path.startswith(prefix):
            return m.group(0)  # al aanwezig
        return f'fileinto "{prefix}{path}"'
    return re.sub(r'fileinto "([^"]+)"', add_prefix, script)


def run():
    if not SCRIPT_FILE.exists():
        print(f"Script niet gevonden: {SCRIPT_FILE}")
        sys.exit(1)

    script_content = SCRIPT_FILE.read_text(encoding="utf-8")
    script_content = apply_prefix(script_content, SIEVE_PREFIX)

    cfg = config.load()

    print(f"Script: {SCRIPT_FILE.name}  ({len(script_content)} bytes)")
    print(f"Server: {cfg.sieve_host}:{cfg.sieve_port}")
    print(f"Account:{cfg.username}")
    print(f"Naam:   {SCRIPT_NAME}\n")

    client = ManageSieveClient(cfg.sieve_host, cfg.sieve_port)

    print("Verbinden ...")
    try:
        client.connect()
        print("  STARTTLS ...")
        client.starttls()
        print("  Authenticeren ...")
        client.authenticate(cfg.username, cfg.password)
    except Exception as e:
        print(f"Verbinding mislukt: {e}")
        sys.exit(1)

    # Toon bestaande scripts
    scripts = client.list_scripts()
    if scripts:
        print(f"\nBestaande scripts op server:")
        for s in scripts:
            print(f"  {s}")
    else:
        print("\nGeen bestaande scripts.")

    # Bevestiging vragen
    print(f"\nScript '{SCRIPT_NAME}' wordt geüpload en geactiveerd.")
    bevestig = input("Typ JA om door te gaan: ").strip()
    if bevestig != "JA":
        print("Afgebroken.")
        client.logout()
        sys.exit(0)

    print("\nUploaden ...")
    try:
        client.put_script(SCRIPT_NAME, script_content)
        print(f"  ✓ {SCRIPT_NAME} geüpload")
        client.set_active(SCRIPT_NAME)
        print(f"  ✓ {SCRIPT_NAME} geactiveerd")
    except Exception as e:
        print(f"  ✗ Mislukt: {e}")
        client.logout()
        sys.exit(1)

    client.logout()
    print("\nKlaar. Nieuwe inkomende mail wordt nu automatisch gesorteerd.")
    print(f"\nAls mail niet aankomt in de verwachte mappen:")
    print(f"  1. Controleer of alle mappen bestaan (maak_mappen.py)")
    print(f"  2. Probeer SIEVE_PREFIX = \"INBOX.\" in dit script")
    print(f"  3. Controleer server-logs via webhosting-paneel")


if __name__ == "__main__":
    run()
