from __future__ import annotations

import json
import sys
from pathlib import Path
import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from chekan.onec_lock import (
    OnecLockError,
    check_active_sessions,
    check_testbase_marker,
    create_testbase_backup,
    load_onec_config,
    parse_ibconnection,
    verify_and_lock_onec,
)


def test_lock_fails_when_no_config(tmp_path: Path) -> None:
    """Нет chekan-1c.json -> отказ 'назовите базу в chekan-1c.json'."""
    with pytest.raises(OnecLockError, match="назовите базу в chekan-1c.json"):
        verify_and_lock_onec(tmp_path)


def test_lock_fails_when_empty_config(tmp_path: Path) -> None:
    """Пустой chekan-1c.json -> отказ 'назовите базу в chekan-1c.json'."""
    (tmp_path / "chekan-1c.json").write_text("{}", encoding="utf-8")
    with pytest.raises(OnecLockError, match="назовите базу в chekan-1c.json"):
        verify_and_lock_onec(tmp_path)


def test_lock_fails_on_server_base(tmp_path: Path) -> None:
    """Строка соединения /S (клиент-сервер) -> отказ 'серверная база считается боевой'."""
    cfg = {"ibconnection": "/Sserver\\base"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")
    with pytest.raises(OnecLockError, match="серверная база считается боевой"):
        verify_and_lock_onec(tmp_path)


def test_lock_fails_when_no_marker(tmp_path: Path) -> None:
    """Нет метки .chekan-testbase -> отказ."""
    db_dir = tmp_path / "my_test_ib"
    db_dir.mkdir()
    cfg = {"ibconnection": f"/F{db_dir}"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    with pytest.raises(OnecLockError, match="отсутствует метка тестовой базы"):
        verify_and_lock_onec(tmp_path)

    # Проверка, что инструмент НЕ создал метку сам
    assert not (db_dir / ".chekan-testbase").exists()
    assert not (tmp_path / ".chekan-testbase").exists()


def test_tool_never_creates_marker_itself(tmp_path: Path) -> None:
    """Инструмент ни при каких условиях не создаёт .chekan-testbase автоматически."""
    db_dir = tmp_path / "db_unmarked"
    db_dir.mkdir()
    cfg = {"ibconnection": str(db_dir)}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    try:
        verify_and_lock_onec(tmp_path)
    except OnecLockError:
        pass

    assert not (db_dir / ".chekan-testbase").exists()
    assert not (tmp_path / ".chekan-testbase").exists()


def test_lock_fails_on_blacklisted_path_even_with_marker(tmp_path: Path) -> None:
    """Чёрный список перевешивает метку (~/Services/onec-build-agent/ib, ib_stand, ib_u*)."""
    fake_build_agent = Path.home() / "Services" / "onec-build-agent" / "ib_stand"
    cfg = {"ibconnection": f"/F{fake_build_agent}"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    with pytest.raises(OnecLockError, match="в чёрном списке боевых баз"):
        verify_and_lock_onec(tmp_path)


def test_lock_fails_when_base_is_busy(tmp_path: Path) -> None:
    """Активный сеанс / файл блокировки 1Cv8.cfl -> отказ."""
    db_dir = tmp_path / "my_locked_ib"
    db_dir.mkdir()
    (db_dir / ".chekan-testbase").touch()
    (db_dir / "1Cv8.cfl").touch()  # файл блокировки сеанса

    cfg = {"ibconnection": f"/F{db_dir}"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    with pytest.raises(OnecLockError, match="база .* занята"):
        verify_and_lock_onec(tmp_path)


def test_lock_passes_when_valid_testbase(tmp_path: Path) -> None:
    """Корректная тестовая файловая база с меткой и без блокировок проходит проверку."""
    db_dir = tmp_path / "clean_test_ib"
    db_dir.mkdir()
    (db_dir / ".chekan-testbase").touch()

    cfg = {"ibconnection": f"/F{db_dir}", "v8version": "8.3.27.1859"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    res = verify_and_lock_onec(tmp_path, backup=False)
    assert res["db_dir"] == db_dir.resolve()
    assert res["ibconnection"] == f"/F{db_dir.resolve()}"
    assert res["v8version"] == "8.3.27.1859"


def test_lock_creates_backup(tmp_path: Path) -> None:
    """Копия перед первой записью создаётся, путь возвращается."""
    db_dir = tmp_path / "backup_test_ib"
    db_dir.mkdir()
    (db_dir / ".chekan-testbase").touch()
    (db_dir / "1Cv8.1CD").write_text("1C Database Content", encoding="utf-8")

    cfg = {"ibconnection": f"/F{db_dir}"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    res = verify_and_lock_onec(tmp_path, backup=True)
    backup_path = res["backup_path"]
    assert backup_path is not None
    assert backup_path.is_dir()
    assert (backup_path / "1Cv8.1CD").is_file()
    assert (backup_path / "1Cv8.1CD").read_text(encoding="utf-8") == "1C Database Content"


def test_lock_corruption_lever(tmp_path: Path) -> None:
    """Рычаг порчи: снятие метки .chekan-testbase обязано приводить к отказу."""
    db_dir = tmp_path / "lever_test_ib"
    db_dir.mkdir()
    marker = db_dir / ".chekan-testbase"
    marker.touch()

    cfg = {"ibconnection": f"/F{db_dir}"}
    (tmp_path / "chekan-1c.json").write_text(json.dumps(cfg), encoding="utf-8")

    # С меткой — проходит
    res = verify_and_lock_onec(tmp_path)
    assert res["db_dir"] == db_dir.resolve()

    # Снимаем метку — обязано отказать
    marker.unlink()
    with pytest.raises(OnecLockError):
        verify_and_lock_onec(tmp_path)
