"""Tests for glreview.errors module."""

from __future__ import annotations

import pytest

from glreview.errors import (
    AlreadyInStateError,
    ConfigurationError,
    FileChangedDuringReviewError,
    GitError,
    GitLabError,
    GitLabNotAvailableError,
    InvalidStateTransitionError,
    IssueNotClosedError,
    ModuleNotFoundError,
    ReviewError,
)


class TestReviewError:
    """Tests for base ReviewError."""

    def test_is_exception(self):
        """ReviewError is an Exception."""
        assert issubclass(ReviewError, Exception)

    def test_can_raise(self):
        """Can raise ReviewError."""
        with pytest.raises(ReviewError):
            raise ReviewError("test error")


class TestModuleNotFoundError:
    """Tests for ModuleNotFoundError."""

    def test_inherits_from_review_error(self):
        """ModuleNotFoundError inherits from ReviewError."""
        assert issubclass(ModuleNotFoundError, ReviewError)

    def test_stores_path(self):
        """Stores the path attribute."""
        err = ModuleNotFoundError("src/module.py")
        assert err.path == "src/module.py"

    def test_message_format(self):
        """Has correct message format."""
        err = ModuleNotFoundError("src/module.py")
        assert str(err) == "Module not found in registry: src/module.py"


class TestInvalidStateTransitionError:
    """Tests for InvalidStateTransitionError."""

    def test_inherits_from_review_error(self):
        """InvalidStateTransitionError inherits from ReviewError."""
        assert issubclass(InvalidStateTransitionError, ReviewError)

    def test_stores_attributes(self):
        """Stores current, target, and valid_targets."""
        err = InvalidStateTransitionError(
            "needs_review", "reviewed", ["in_progress"]
        )
        assert err.current == "needs_review"
        assert err.target == "reviewed"
        assert err.valid_targets == ["in_progress"]

    def test_message_without_valid_targets(self):
        """Message without valid targets."""
        err = InvalidStateTransitionError("needs_review", "reviewed")
        assert str(err) == "Cannot transition from 'needs_review' to 'reviewed'"
        assert err.valid_targets == []

    def test_message_with_valid_targets(self):
        """Message with valid targets."""
        err = InvalidStateTransitionError(
            "needs_review", "reviewed", ["in_progress"]
        )
        assert "Valid transitions: ['in_progress']" in str(err)


class TestAlreadyInStateError:
    """Tests for AlreadyInStateError."""

    def test_inherits_from_review_error(self):
        """AlreadyInStateError inherits from ReviewError."""
        assert issubclass(AlreadyInStateError, ReviewError)

    def test_stores_attributes(self):
        """Stores path and status."""
        err = AlreadyInStateError("src/module.py", "reviewed")
        assert err.path == "src/module.py"
        assert err.status == "reviewed"

    def test_message_format(self):
        """Has correct message format."""
        err = AlreadyInStateError("src/module.py", "reviewed")
        assert str(err) == "src/module.py is already in 'reviewed' status"


class TestGitError:
    """Tests for GitError."""

    def test_inherits_from_review_error(self):
        """GitError inherits from ReviewError."""
        assert issubclass(GitError, ReviewError)

    def test_stores_operation(self):
        """Stores the operation."""
        err = GitError("commit", "nothing to commit")
        assert err.operation == "commit"

    def test_message_format(self):
        """Has correct message format."""
        err = GitError("commit", "nothing to commit")
        assert str(err) == "Git commit failed: nothing to commit"


class TestGitLabError:
    """Tests for GitLabError."""

    def test_inherits_from_review_error(self):
        """GitLabError inherits from ReviewError."""
        assert issubclass(GitLabError, ReviewError)

    def test_stores_operation(self):
        """Stores the operation."""
        err = GitLabError("create_issue", "permission denied")
        assert err.operation == "create_issue"

    def test_message_format(self):
        """Has correct message format."""
        err = GitLabError("create_issue", "permission denied")
        assert str(err) == "GitLab create_issue failed: permission denied"


class TestGitLabNotAvailableError:
    """Tests for GitLabNotAvailableError."""

    def test_inherits_from_gitlab_error(self):
        """GitLabNotAvailableError inherits from GitLabError."""
        assert issubclass(GitLabNotAvailableError, GitLabError)

    def test_default_message(self):
        """Has correct default message."""
        err = GitLabNotAvailableError()
        assert err.operation == "authentication"
        assert "No GitLab token configured" in str(err)


class TestIssueNotClosedError:
    """Tests for IssueNotClosedError."""

    def test_inherits_from_review_error(self):
        """IssueNotClosedError inherits from ReviewError."""
        assert issubclass(IssueNotClosedError, ReviewError)

    def test_stores_attributes(self):
        """Stores issue_number and state."""
        err = IssueNotClosedError("123", "open")
        assert err.issue_number == "123"
        assert err.state == "open"

    def test_message_format(self):
        """Has correct message format."""
        err = IssueNotClosedError("123", "open")
        assert str(err) == "Issue #123 is not closed (state: open)"


class TestFileChangedDuringReviewError:
    """Tests for FileChangedDuringReviewError."""

    def test_inherits_from_review_error(self):
        """FileChangedDuringReviewError inherits from ReviewError."""
        assert issubclass(FileChangedDuringReviewError, ReviewError)

    def test_stores_attributes(self):
        """Stores path, start_commit, and current_commit."""
        err = FileChangedDuringReviewError("src/module.py", "abc123", "def456")
        assert err.path == "src/module.py"
        assert err.start_commit == "abc123"
        assert err.current_commit == "def456"

    def test_message_format(self):
        """Has correct message format."""
        err = FileChangedDuringReviewError("src/module.py", "abc123", "def456")
        assert "src/module.py changed during review" in str(err)
        assert "started at abc123" in str(err)
        assert "now at def456" in str(err)


class TestConfigurationError:
    """Tests for ConfigurationError."""

    def test_inherits_from_review_error(self):
        """ConfigurationError inherits from ReviewError."""
        assert issubclass(ConfigurationError, ReviewError)

    def test_can_raise_with_message(self):
        """Can raise with custom message."""
        with pytest.raises(ConfigurationError, match="missing config"):
            raise ConfigurationError("missing config")
