import os
import stat

from cpanel_mailgen.cli import main
from cpanel_mailgen.csvfile import parse

CSV = "E-mail;Password\nalice@example.com;\nbob@example.com;\ncarol@example.com;\n"


def _csv(tmp_path, content=CSV, name="comptes.csv"):
    path = tmp_path / name
    path.write_text(content)
    return path


def test_fills_every_empty_password(tmp_path):
    path = _csv(tmp_path)
    assert main([str(path)]) == 0
    data = parse(path.read_text())
    assert all(row[data.password_index] for row in data.rows)


def test_emails_are_never_modified(tmp_path):
    """La contrainte centrale : ne pas toucher a la colonne E-mail."""
    path = _csv(tmp_path)
    before = parse(path.read_text()).emails
    main([str(path)])
    assert parse(path.read_text()).emails == before


def test_email_order_is_preserved(tmp_path):
    path = _csv(tmp_path)
    main([str(path)])
    assert parse(path.read_text()).emails == [
        "alice@example.com",
        "bob@example.com",
        "carol@example.com",
    ]


def test_existing_passwords_are_kept_by_default(tmp_path):
    path = _csv(tmp_path, "E-mail;Password\nalice@x.fr;DEJA_DEFINI\nbob@x.fr;\n")
    main([str(path)])
    data = parse(path.read_text())
    assert data.rows[0][1] == "DEJA_DEFINI"
    assert data.rows[1][1] and data.rows[1][1] != "DEJA_DEFINI"


def test_force_regenerates_existing_passwords(tmp_path):
    path = _csv(tmp_path, "E-mail;Password\nalice@x.fr;DEJA_DEFINI\n")
    main([str(path), "--force"])
    assert parse(path.read_text()).rows[0][1] != "DEJA_DEFINI"


def test_passwords_are_all_different(tmp_path):
    path = _csv(tmp_path)
    main([str(path)])
    data = parse(path.read_text())
    passwords = [row[data.password_index] for row in data.rows]
    assert len(set(passwords)) == len(passwords)


def test_no_generated_password_contains_a_semicolon(tmp_path):
    path = _csv(tmp_path)
    main([str(path)])
    data = parse(path.read_text())
    # Le fichier reste relisible : c'est la preuve qu'aucun ; ne s'est glisse
    # dans un mot de passe avec un delimiteur ';'.
    assert len(data.rows) == 3
    assert all(";" not in row[data.password_index] for row in data.rows)


def test_length_option(tmp_path):
    path = _csv(tmp_path)
    main([str(path), "--length", "32"])
    data = parse(path.read_text())
    assert all(len(row[data.password_index]) == 32 for row in data.rows)


def test_too_short_length_exits_2(tmp_path):
    assert main([str(_csv(tmp_path)), "--length", "4"]) == 2


def test_backup_is_written_when_overwriting(tmp_path):
    path = _csv(tmp_path)
    original = path.read_text()
    main([str(path)])
    backup = path.with_suffix(path.suffix + ".bak")
    assert backup.exists()
    assert backup.read_text() == original


def test_no_backup_option(tmp_path):
    path = _csv(tmp_path)
    main([str(path), "--no-backup"])
    assert not path.with_suffix(path.suffix + ".bak").exists()


def test_output_to_another_file_leaves_the_input_untouched(tmp_path):
    path = _csv(tmp_path)
    original = path.read_text()
    out = tmp_path / "rempli.csv"
    main([str(path), "-o", str(out)])
    assert path.read_text() == original
    assert all(row[1] for row in parse(out.read_text()).rows)


def test_output_is_0600(tmp_path):
    path = _csv(tmp_path)
    out = tmp_path / "rempli.csv"
    main([str(path), "-o", str(out)])
    assert stat.S_IMODE(os.stat(out).st_mode) == 0o600


def test_delimiter_is_preserved(tmp_path):
    path = _csv(tmp_path, "E-mail,Password\nalice@x.fr,\n")
    main([str(path)])
    assert path.read_text().splitlines()[0] == "E-mail,Password"


def test_extra_columns_survive(tmp_path):
    path = _csv(tmp_path, "Nom;E-mail;Password;Note\nAlice;alice@x.fr;;bla\n")
    main([str(path)])
    row = parse(path.read_text()).rows[0]
    assert row[0] == "Alice"
    assert row[3] == "bla"


def test_cpanel_output_has_unlimited_quota(tmp_path):
    path = _csv(tmp_path)
    cpanel = tmp_path / "import.csv"
    main([str(path), "--cpanel-out", str(cpanel)])
    lines = cpanel.read_text().strip().splitlines()
    assert len(lines) == 3
    for line, email in zip(lines, ["alice", "bob", "carol"], strict=True):
        parts = line.split(",")
        assert parts[0].startswith(email)
        assert parts[1]  # mot de passe present
        assert parts[2] == ""  # quota vide = illimite


def test_cpanel_output_with_explicit_quota(tmp_path):
    path = _csv(tmp_path)
    cpanel = tmp_path / "import.csv"
    main([str(path), "--cpanel-out", str(cpanel), "--quota", "2048"])
    assert all(
        line.split(",")[2] == "2048" for line in cpanel.read_text().strip().splitlines()
    )


def test_cpanel_output_is_0600(tmp_path):
    path = _csv(tmp_path)
    cpanel = tmp_path / "import.csv"
    main([str(path), "--cpanel-out", str(cpanel)])
    assert stat.S_IMODE(os.stat(cpanel).st_mode) == 0o600


def test_cpanel_passwords_match_the_main_file(tmp_path):
    path = _csv(tmp_path)
    cpanel = tmp_path / "import.csv"
    main([str(path), "--cpanel-out", str(cpanel)])
    data = parse(path.read_text())
    expected = {
        (row[data.email_index], row[data.password_index]) for row in data.rows
    }
    produced = {
        (line.split(",")[0], line.split(",")[1])
        for line in cpanel.read_text().strip().splitlines()
    }
    assert produced == expected


def test_invalid_csv_exits_2(tmp_path):
    assert main([str(_csv(tmp_path, "Nom;Quota\nAlice;5G\n"))]) == 2


def test_output_is_usable_by_imapsync_gen(tmp_path):
    """Le CSV rempli doit rester consommable par l'outil de migration."""
    path = _csv(tmp_path)
    main([str(path)])
    data = parse(path.read_text())
    for row in data.rows:
        assert ";" not in row[data.password_index]
        assert ";" not in row[data.email_index]


def test_backup_is_also_0600(tmp_path):
    """La sauvegarde peut contenir des mots de passe deja en service."""
    path = _csv(tmp_path, "E-mail;Password\nalice@x.fr;DEJA_EN_SERVICE\n")
    path.chmod(0o644)
    main([str(path), "--force"])
    backup = path.with_suffix(path.suffix + ".bak")
    assert stat.S_IMODE(os.stat(backup).st_mode) == 0o600
