"""Tests for the PassphraseProvider .env and env-var resolution."""

import os
import stat
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest

# Mock the dependencies before importing the provider
sys.modules.setdefault("id_wispera.auth.keychain_provider", MagicMock())
sys.modules.setdefault("id_wispera.auth.session_token", MagicMock())

from id_wispera.auth.passphrase_provider import (
    PassphraseProvider,
    PassphraseResult,
    PASSPHRASE_ENV_VAR,
)


# ──────────────────────────────────────────────────────────────────────────────
# _read_passphrase_from_dotenv
# ──────────────────────────────────────────────────────────────────────────────


class TestReadPassphraseFromDotenv:
    def test_reads_passphrase(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=my-secret\n")
        assert PassphraseProvider._read_passphrase_from_dotenv(env_file) == "my-secret"

    def test_strips_double_quotes(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text('IDW_PASSPHRASE="quoted-pass"\n')
        assert PassphraseProvider._read_passphrase_from_dotenv(env_file) == "quoted-pass"

    def test_strips_single_quotes(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE='single-quoted'\n")
        assert (
            PassphraseProvider._read_passphrase_from_dotenv(env_file) == "single-quoted"
        )

    def test_ignores_comments_and_blanks(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text(
            "# comment\n\n  \n# another\nIDW_PASSPHRASE=after-comments\n"
        )
        assert (
            PassphraseProvider._read_passphrase_from_dotenv(env_file)
            == "after-comments"
        )

    def test_ignores_other_keys(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text("OTHER=nope\nIDW_PASSPHRASE=correct\nANOTHER=no\n")
        assert PassphraseProvider._read_passphrase_from_dotenv(env_file) == "correct"

    def test_returns_none_for_missing_file(self):
        assert (
            PassphraseProvider._read_passphrase_from_dotenv(
                Path("/nonexistent/.env")
            )
            is None
        )

    def test_returns_none_when_key_not_present(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text("OTHER=value\n")
        assert PassphraseProvider._read_passphrase_from_dotenv(env_file) is None

    def test_handles_value_with_equals(self, tmp_path: Path):
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=pass=with=equals\n")
        assert (
            PassphraseProvider._read_passphrase_from_dotenv(env_file)
            == "pass=with=equals"
        )


# ──────────────────────────────────────────────────────────────────────────────
# _resolve_from_env_or_dotenv
# ──────────────────────────────────────────────────────────────────────────────


class TestResolveFromEnvOrDotenv:
    def test_prefers_process_env(self, tmp_path: Path, monkeypatch):
        monkeypatch.setenv("IDW_PASSPHRASE", "env-wins")
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=dotenv-loses\n")
        monkeypatch.chdir(tmp_path)

        result = PassphraseProvider._resolve_from_env_or_dotenv()
        assert result is not None
        assert result.source == "env-var"
        assert result.passphrase == "env-wins"

    def test_reads_from_cwd_dotenv(self, tmp_path: Path, monkeypatch):
        monkeypatch.delenv("IDW_PASSPHRASE", raising=False)
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=cwd-pass\n")
        monkeypatch.chdir(tmp_path)

        result = PassphraseProvider._resolve_from_env_or_dotenv()
        assert result is not None
        assert result.source == "dotenv-file"
        assert result.passphrase == "cwd-pass"

    def test_returns_none_when_nothing_available(self, tmp_path: Path, monkeypatch):
        monkeypatch.delenv("IDW_PASSPHRASE", raising=False)
        monkeypatch.chdir(tmp_path)

        result = PassphraseProvider._resolve_from_env_or_dotenv()
        assert result is None


# ──────────────────────────────────────────────────────────────────────────────
# _warn_if_world_readable
# ──────────────────────────────────────────────────────────────────────────────


class TestWarnIfWorldReadable:
    def test_warns_on_world_readable(self, tmp_path: Path, capsys):
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=test\n")
        env_file.chmod(0o644)

        PassphraseProvider._warn_if_world_readable(env_file)

        captured = capsys.readouterr()
        assert "[WARNING]" in captured.err
        assert "world-readable" in captured.err
        assert "chmod 600" in captured.err

    def test_no_warning_on_restricted_file(self, tmp_path: Path, capsys):
        env_file = tmp_path / ".env"
        env_file.write_text("IDW_PASSPHRASE=test\n")
        env_file.chmod(0o600)

        PassphraseProvider._warn_if_world_readable(env_file)

        captured = capsys.readouterr()
        assert "[WARNING]" not in captured.err

    def test_no_crash_on_missing_file(self):
        # Should not raise
        PassphraseProvider._warn_if_world_readable(Path("/nonexistent/.env"))


# ──────────────────────────────────────────────────────────────────────────────
# Source literal values
# ──────────────────────────────────────────────────────────────────────────────


class TestPassphraseSourceValues:
    def test_source_values_are_strings(self):
        """Ensure source values match the expected Literal types."""
        result_env = PassphraseResult(passphrase="x", source="env-var")
        assert result_env.source == "env-var"

        result_dotenv = PassphraseResult(passphrase="x", source="dotenv-file")
        assert result_dotenv.source == "dotenv-file"

        result_session = PassphraseResult(passphrase="x", source="session-token")
        assert result_session.source == "session-token"

        result_kc = PassphraseResult(passphrase="x", source="keychain")
        assert result_kc.source == "keychain"

        result_interactive = PassphraseResult(passphrase="x", source="interactive")
        assert result_interactive.source == "interactive"
