from __future__ import annotations

import json
import os
import warnings
from pathlib import Path
import pytest


class TestGetConfigDir:
    """Tests for get_config_dir() lazy resolution."""

    def _get_fresh(self):
        """Import and clear cache for isolated test."""
        from aegro.cli._paths import get_config_dir

        get_config_dir.cache_clear()
        return get_config_dir

    def test_returns_path_named_aegro(self, monkeypatch):
        monkeypatch.delenv("AEGRO_CONFIG_DIR", raising=False)
        fn = self._get_fresh()
        result = fn()
        assert isinstance(result, Path)
        assert result.name == "aegro"

    def test_env_override_takes_priority(self, tmp_path, monkeypatch):
        custom = tmp_path / "custom"
        monkeypatch.setenv("AEGRO_CONFIG_DIR", str(custom))
        fn = self._get_fresh()
        assert fn() == custom

    def test_cache_returns_same_object(self, monkeypatch):
        monkeypatch.delenv("AEGRO_CONFIG_DIR", raising=False)
        fn = self._get_fresh()
        a = fn()
        b = fn()
        assert a is b

    def test_cache_clear_allows_new_resolution(self, tmp_path, monkeypatch):
        monkeypatch.setenv("AEGRO_CONFIG_DIR", str(tmp_path / "first"))
        fn = self._get_fresh()
        first = fn()
        fn.cache_clear()
        monkeypatch.setenv("AEGRO_CONFIG_DIR", str(tmp_path / "second"))
        second = fn()
        assert first != second


class TestMigrateLegacyFile:
    """Tests for legacy config file migration."""

    def test_copies_valid_json(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"farms": {}}', encoding="utf-8")

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        native_file = native_dir / "credentials.json"
        assert native_file.exists()
        assert json.loads(native_file.read_text(encoding="utf-8")) == {"farms": {}}

    def test_renames_legacy_to_migrated(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"farms": {}}', encoding="utf-8")

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        assert not (legacy_dir / "credentials.json").exists()
        assert (legacy_dir / "credentials.json.migrated").exists()

    def test_emits_user_warning(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"farms": {}}', encoding="utf-8")

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            _migrate_legacy_file(native_dir, "credentials.json")

        assert len(w) == 1
        assert issubclass(w[0].category, UserWarning)
        assert "migrated" in str(w[0].message).lower()

    def test_skips_if_native_exists(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        native_dir.mkdir()
        (legacy_dir / "credentials.json").write_text('{"farms": {"old": "key"}}', encoding="utf-8")
        (native_dir / "credentials.json").write_text('{"farms": {"new": "key"}}', encoding="utf-8")

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        # Native file should NOT be overwritten
        data = json.loads((native_dir / "credentials.json").read_text(encoding="utf-8"))
        assert data["farms"] == {"new": "key"}

    def test_skips_if_no_legacy(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()  # exists but no credentials.json

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        assert not (native_dir / "credentials.json").exists()

    def test_skips_corrupted_json(self, tmp_path, monkeypatch):
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        (legacy_dir / "credentials.json").write_text("NOT VALID JSON {{{", encoding="utf-8")

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        assert not (native_dir / "credentials.json").exists()

    def test_migrates_cp1252_legacy_file(self, tmp_path, monkeypatch):
        """CP1252-encoded legacy files are migrated (re-encoded as UTF-8)."""
        legacy_dir = tmp_path / "legacy"
        native_dir = tmp_path / "native"
        legacy_dir.mkdir()
        data = '{"farms": {"Fazenda S\u00e3o Jo\u00e3o": "key-1"}}'
        (legacy_dir / "credentials.json").write_bytes(data.encode("cp1252"))

        monkeypatch.setattr("aegro.cli._paths._LEGACY_DIR", legacy_dir)
        from aegro.cli._paths import _migrate_legacy_file

        _migrate_legacy_file(native_dir, "credentials.json")

        native_file = native_dir / "credentials.json"
        assert native_file.exists()
        # Verify it was re-encoded as UTF-8
        content = native_file.read_text(encoding="utf-8")
        parsed = json.loads(content)
        assert parsed["farms"]["Fazenda São João"] == "key-1"


class TestAtomicReplace:
    """Tests for _atomic_replace retry logic."""

    def test_replaces_file(self, tmp_path):
        src = tmp_path / "src.txt"
        dst = tmp_path / "dst.txt"
        src.write_text("new content", encoding="utf-8")

        from aegro.cli._paths import _atomic_replace

        _atomic_replace(str(src), str(dst))

        assert dst.read_text(encoding="utf-8") == "new content"
        assert not src.exists()

    def test_retries_on_permission_error_windows(self, tmp_path, monkeypatch):
        monkeypatch.setattr("aegro.cli._paths.sys.platform", "win32")

        src = tmp_path / "src.txt"
        dst = tmp_path / "dst.txt"
        src.write_text("content", encoding="utf-8")

        call_count = 0
        original_replace = os.replace

        def flaky_replace(s, d):
            nonlocal call_count
            call_count += 1
            if call_count < 3:
                raise PermissionError("file locked")
            original_replace(s, d)

        monkeypatch.setattr("aegro.cli._paths.os.replace", flaky_replace)
        monkeypatch.setattr("aegro.cli._paths.time.sleep", lambda _: None)

        from aegro.cli._paths import _atomic_replace

        _atomic_replace(str(src), str(dst))

        assert call_count == 3
        assert dst.read_text(encoding="utf-8") == "content"

    def test_raises_after_max_retries(self, tmp_path, monkeypatch):
        monkeypatch.setattr("aegro.cli._paths.sys.platform", "win32")

        src = tmp_path / "src.txt"
        dst = tmp_path / "dst.txt"
        src.write_text("content", encoding="utf-8")

        def always_raise(s, d):
            raise PermissionError("locked")

        monkeypatch.setattr("aegro.cli._paths.os.replace", always_raise)
        monkeypatch.setattr("aegro.cli._paths.time.sleep", lambda _: None)

        from aegro.cli._paths import _atomic_replace

        with pytest.raises(PermissionError, match="locked"):
            _atomic_replace(str(src), str(dst))

    def test_no_retry_on_non_windows(self, tmp_path, monkeypatch):
        monkeypatch.setattr("aegro.cli._paths.sys.platform", "linux")

        src = tmp_path / "src.txt"
        dst = tmp_path / "dst.txt"
        src.write_text("content", encoding="utf-8")

        def always_raise(s, d):
            raise PermissionError("locked")

        monkeypatch.setattr("aegro.cli._paths.os.replace", always_raise)

        from aegro.cli._paths import _atomic_replace

        with pytest.raises(PermissionError):
            _atomic_replace(str(src), str(dst))
