# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Manage email aliases stored in sqlite database.
"""

import contextlib
import pwd
import sqlite3
from dataclasses import dataclass

from plinth import actions


@dataclass
class Alias:
    value: str
    name: str


@contextlib.contextmanager
def _get_cursor():
    """Return a DB cursor as context manager."""
    # Turn ON autocommit mode
    db_path = '/var/lib/postfix/freedombox-aliases/aliases.sqlite3'
    connection = sqlite3.connect(db_path, isolation_level=None)
    connection.row_factory = sqlite3.Row
    try:
        cursor = connection.cursor()
        yield cursor
    finally:
        connection.close()


def get(username):
    """Get all aliases of a user."""
    query = 'SELECT name, value FROM alias WHERE value=?'
    with _get_cursor() as cursor:
        rows = cursor.execute(query, (username, ))
        return [Alias(**row) for row in rows]


def exists(name):
    """Return whether alias is already taken."""
    try:
        pwd.getpwnam(name)
        return True
    except KeyError:
        pass

    with _get_cursor() as cursor:
        query = 'SELECT COUNT(*) FROM alias WHERE name=?'
        cursor.execute(query, (name, ))
        return cursor.fetchone()[0] != 0


def put(username, name):
    """Insert if not exists a new alias."""
    query = 'INSERT INTO alias (name, value) VALUES (?, ?)'
    with _get_cursor() as cursor:
        try:
            cursor.execute(query, (name, username))
        except sqlite3.IntegrityError:
            pass  # Alias exists, rare since we are already checking


def delete(username, aliases):
    """Delete a set of aliases."""
    query = 'DELETE FROM alias WHERE value=? AND name=?'
    parameter_seq = ((username, name) for name in aliases)
    with _get_cursor() as cursor:
        cursor.execute('BEGIN')
        cursor.executemany(query, parameter_seq)
        cursor.execute('COMMIT')


def first_setup():
    """Create the database file and schema inside it."""
    actions.superuser_run('email', ['aliases', 'setup'])

    # Create schema if not exists
    query = '''
BEGIN;
CREATE TABLE IF NOT EXISTS alias (
    name TEXT NOT NULL,
    value TEXT NOT NULL,
    PRIMARY KEY (name)
);
COMMIT;
'''
    with _get_cursor() as cursor:
        cursor.executescript(query)


def setup_common_aliases(username):
    """Create aliases for common mailboxes described in RFC2142.

    See: https://datatracker.ietf.org/doc/html/rfc2142
    """
    aliases = [
        'mailer-daemon', 'postmaster', 'nobody', 'webmaster', 'www',
        'hostmaster', 'info', 'support', 'abuse', 'noc', 'security'
        'usenet', 'news', 'ftp'
    ]
    for alias in aliases:
        if not exists(alias):
            put(username, alias)
