"""Tests for backup and restore scripts (scripts/backup.py, scripts/restore.py)."""

from __future__ import annotations

import json
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch

import pytest

# Add scripts dir to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))


def test_get_db_connection_params_from_asyncpg_url():
    """get_db_connection_params should convert asyncpg URL to standard postgresql URL."""
    from backup import get_db_connection_params

    with patch.dict(os.environ, {
        "DATABASE_URL": "postgresql+asyncpg://user:pass@localhost:5432/leocrm"
    }):
        params = get_db_connection_params()
    assert params["host"] == "localhost"
    assert params["port"] == "5432"
    assert params["database"] == "leocrm"
    assert params["username"] == "user"
    assert params["password"] == "pass"


def test_get_db_connection_params_missing_env():
    """get_db_connection_params should raise ValueError when DATABASE_URL is missing."""
    from backup import get_db_connection_params

    with patch.dict(os.environ, {}, clear=True):
        with pytest.raises(ValueError, match="DATABASE_URL"):
            get_db_connection_params()


def test_create_backup_manifest_structure():
    """create_backup_manifest should return a valid manifest dict."""
    from backup import create_backup_manifest

    manifest = create_backup_manifest(
        Path("/tmp/test_backup"),
        db_success=True,
        files_success=True,
        db_size=1024,
        files_count=5,
        files_size=2048,
    )
    assert manifest["backup_id"] == "test_backup"
    assert manifest["database"]["success"] is True
    assert manifest["database"]["size_bytes"] == 1024
    assert manifest["files"]["count"] == 5
    assert manifest["version"] == "1.0.0"


def test_backup_files_nonexistent_path():
    """backup_files should return success when storage path doesn't exist."""
    from backup import backup_files

    with tempfile.TemporaryDirectory() as tmpdir:
        success, count, size, error = backup_files(Path(tmpdir), "/nonexistent/path")
    assert success is True
    assert count == 0
    assert size == 0


def test_backup_files_copies_files():
    """backup_files should copy files from storage to backup directory."""
    from backup import backup_files

    with tempfile.TemporaryDirectory() as storage_dir:
        # Create test files
        (Path(storage_dir) / "file1.txt").write_text("hello")
        (Path(storage_dir) / "subdir").mkdir()
        (Path(storage_dir) / "subdir" / "file2.txt").write_text("world")

        with tempfile.TemporaryDirectory() as backup_dir:
            success, count, size, error = backup_files(Path(backup_dir), storage_dir)

        assert success is True
        assert count == 2
        assert size == 10  # "hello" (5) + "world" (5)


def test_apply_retention_policy_deletes_old_backups():
    """apply_retention_policy should delete backups older than retention_days."""
    import time
    from backup import apply_retention_policy

    with tempfile.TemporaryDirectory() as tmpdir:
        base = Path(tmpdir)
        # Create old backup (modify mtime to past)
        old_backup = base / "leocrm_backup_20200101_000000"
        old_backup.mkdir()
        (old_backup / "database.sql").write_text("old")
        old_time = time.time() - (30 * 86400)  # 30 days ago
        os.utime(old_backup, (old_time, old_time))

        # Create recent backup
        recent_backup = base / "leocrm_backup_20260723_220000"
        recent_backup.mkdir()
        (recent_backup / "database.sql").write_text("recent")

        deleted = apply_retention_policy(base, retention_days=7)

        assert len(deleted) == 1
        assert "leocrm_backup_20200101_000000" in deleted[0]
        assert recent_backup.exists()


def test_apply_retention_policy_zero_days_keeps_all():
    """apply_retention_policy with 0 days should keep all backups."""
    from backup import apply_retention_policy

    with tempfile.TemporaryDirectory() as tmpdir:
        base = Path(tmpdir)
        (base / "leocrm_backup_20200101_000000").mkdir()
        deleted = apply_retention_policy(base, retention_days=0)
        assert len(deleted) == 0


def test_run_backup_dry_run():
    """run_backup in dry-run mode should return success without executing."""
    from backup import run_backup

    with tempfile.TemporaryDirectory() as tmpdir:
        report = run_backup(
            destination="local",
            retention_days=7,
            storage_path="/tmp",
            backup_base_dir=tmpdir,
            dry_run=True,
        )
    assert report["dry_run"] is True
    assert report["overall_success"] is True
    assert "DRY-RUN" in report["database"]["error"]


def test_restore_files_copies_files():
    """restore_files should copy files from backup to target path."""
    from restore import restore_files

    with tempfile.TemporaryDirectory() as backup_dir:
        # Create backup files
        files_dir = Path(backup_dir) / "files"
        files_dir.mkdir()
        (files_dir / "file1.txt").write_text("hello")
        (files_dir / "subdir").mkdir()
        (files_dir / "subdir" / "file2.txt").write_text("world")

        with tempfile.TemporaryDirectory() as target_dir:
            success, count, size, error = restore_files(Path(backup_dir), target_dir)

        assert success is True
        assert count == 2
        assert size == 10


def test_restore_files_no_files_dir():
    """restore_files should return success when no files/ directory exists."""
    from restore import restore_files

    with tempfile.TemporaryDirectory() as backup_dir:
        with tempfile.TemporaryDirectory() as target_dir:
            success, count, size, error = restore_files(Path(backup_dir), target_dir)

        assert success is True
        assert count == 0


def test_validate_backup_dir_with_manifest():
    """validate_backup_dir should return manifest dict when manifest.json exists."""
    from restore import validate_backup_dir

    with tempfile.TemporaryDirectory() as tmpdir:
        manifest = {"backup_id": "test", "version": "1.0.0"}
        (Path(tmpdir) / "manifest.json").write_text(json.dumps(manifest))
        result = validate_backup_dir(Path(tmpdir))
        assert result is not None
        assert result["backup_id"] == "test"


def test_validate_backup_dir_without_manifest():
    """validate_backup_dir should return None when manifest.json doesn't exist."""
    from restore import validate_backup_dir

    with tempfile.TemporaryDirectory() as tmpdir:
        result = validate_backup_dir(Path(tmpdir))
        assert result is None


def test_run_restore_nonexistent_dir():
    """run_restore should report error when backup directory doesn't exist."""
    from restore import run_restore

    report = run_restore(
        backup_dir_path="/nonexistent/backup/dir",
        target_storage_path="/tmp",
        dry_run=False,
    )
    assert report["overall_success"] is False
    assert any("not found" in e for e in report["errors"])


def test_run_restore_dry_run():
    """run_restore in dry-run mode should return success without executing."""
    from restore import run_restore

    with tempfile.TemporaryDirectory() as tmpdir:
        report = run_restore(
            backup_dir_path=tmpdir,
            target_storage_path="/tmp",
            dry_run=True,
        )
    assert report["dry_run"] is True
    assert report["overall_success"] is True


def test_system_notif_has_backup_events():
    """system_notif plugin should register backup.completed and backup.failed events."""
    from app.plugins.builtins.system_notif.plugin import SystemNotifPlugin

    events = SystemNotifPlugin.manifest.events
    assert "backup.completed" in events
    assert "backup.failed" in events


def test_system_settings_has_backup_config():
    """SystemSettingsUpsert should have backup configuration fields."""
    from app.schemas.system_settings import SystemSettingsUpsert

    fields = SystemSettingsUpsert.model_fields
    assert "backup_interval" in fields
    assert "backup_retention_days" in fields
    assert "backup_destination" in fields
