"""Tests for input parsers."""

import pytest
from agentreflect.parsers.pytest_parser import PytestParser
from agentreflect.parsers.git_parser import GitLogParser
from agentreflect.parsers.notes_parser import NotesParser


# ─── PytestParser ───────────────────────────────────────────────────────────

class TestPytestParser:
    def setup_method(self):
        self.parser = PytestParser()

    def test_parses_failed_line_with_attributeerror(self):
        text = "FAILED tests/test_api.py::test_foo - AttributeError: 'NoneType' object has no attribute 'x'"
        records = self.parser.parse(text)
        assert len(records) >= 1
        types = [r.type for r in records]
        assert any("AttributeError" in t or "TestFailure" in t for t in types)

    def test_parses_failed_line_with_nameerror(self):
        text = "FAILED tests/test_bar.py::test_baz - NameError: name 'foo' is not defined"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_parses_error_line(self):
        text = "ERROR tests/conftest.py - ImportError: cannot import 'x'"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_parses_short_traceback_format(self):
        text = """
________ test_foo ________

tests/test_foo.py:10: in test_foo
    obj.do_thing()
E   AttributeError: 'NoneType' object has no attribute 'do_thing'
"""
        records = self.parser.parse(text)
        assert len(records) >= 1
        assert records[0].type == "AttributeError"

    def test_parses_multiple_failures(self):
        with open("tests/fixtures/sample-failures.txt") as f:
            content = f.read()
        records = self.parser.parse(content)
        assert len(records) >= 3

    def test_extracts_source_file_from_section(self):
        text = """
________ test_foo ________

tests/test_foo.py:42: in test_foo
    raise ValueError("bad")
E   ValueError: bad
"""
        records = self.parser.parse(text)
        assert any(r.source_file != "" for r in records) or len(records) >= 1

    def test_parses_timeout_error(self):
        text = "FAILED tests/test_slow.py::test_slow - TimeoutError: timed out"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_parses_keyerror(self):
        text = "FAILED tests/test_dict.py::test_dict - KeyError: 'missing_key'"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_empty_input_returns_empty_list(self):
        records = self.parser.parse("")
        assert records == []

    def test_parses_e_prefixed_lines(self):
        text = """
E   AttributeError: object has no attribute 'foo'
"""
        records = self.parser.parse(text)
        assert len(records) >= 1
        assert any(r.type == "AttributeError" for r in records)

    def test_parses_import_error_in_failed_line(self):
        text = "FAILED tests/test_x.py::test_x - ImportError: cannot import 'y'"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_section_header_extraction(self):
        text = """
________ test_none_check ________

src/api.py:15: in process
    result = obj.value
E   AttributeError: 'NoneType' object has no attribute 'value'

FAILED tests/test_api.py::test_none_check - AttributeError: 'NoneType' object has no attribute 'value'
"""
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_parses_assertion_error(self):
        text = "FAILED tests/test_assert.py::test_eq - AssertionError: assert 1 == 2"
        records = self.parser.parse(text)
        assert len(records) >= 1

    def test_context_populated(self):
        text = """
________ test_ctx ________

tests/test_ctx.py:5: in test_ctx
    do_thing()
E   RuntimeError: context failure
"""
        records = self.parser.parse(text)
        if records:
            assert records[0].context != "" or records[0].raw != ""


# ─── GitLogParser ────────────────────────────────────────────────────────────

class TestGitLogParser:
    def setup_method(self):
        self.parser = GitLogParser()

    def test_parses_fix_prefix(self):
        text = "abc1234 fix: remove None check bypass"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert records[0].type == "git-fix"

    def test_parses_bug_prefix(self):
        text = "def5678 bug: wrong variable name in handler"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert records[0].type == "git-bug"

    def test_parses_revert_prefix(self):
        text = "999aaaa revert: undo broken import change"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert records[0].type == "git-revert"

    def test_parses_agent_error_commit(self):
        text = "aabbcc1 claude broke the build with wrong imports"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert records[0].type == "git-agent-error"

    def test_skips_normal_commits(self):
        text = "1234567 add new user endpoint"
        records = self.parser.parse(text)
        assert len(records) == 0

    def test_empty_log_returns_empty(self):
        records = self.parser.parse("")
        assert records == []

    def test_parses_multiple_commits(self):
        text = """abc1234 fix: null pointer in user handler
def5678 add new feature
999aaaa bug: wrong calculation in totals
bbbbbbb codex made incorrect changes"""
        records = self.parser.parse(text)
        assert len(records) == 3

    def test_fix_with_scope(self):
        text = "abc1234 fix(api): correct response parsing"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert records[0].type == "git-fix"

    def test_message_extracted(self):
        text = "abc1234 fix: stop agent from overwriting files"
        records = self.parser.parse(text)
        assert len(records) == 1
        assert "stop agent from overwriting files" in records[0].message

    def test_from_repo_on_nonexistent_path(self):
        # Should not raise, just return empty
        records = GitLogParser.from_repo("/nonexistent/path/abc")
        assert records == []


# ─── NotesParser ─────────────────────────────────────────────────────────────

class TestNotesParser:
    def setup_method(self):
        self.parser = NotesParser()

    def test_parses_attributeerror_mention(self):
        records = self.parser.parse("The agent got an AttributeError when obj was None")
        assert len(records) >= 1
        assert any(r.type == "AttributeError" for r in records)

    def test_parses_none_mention(self):
        records = self.parser.parse("agent forgot to check for None before accessing the attribute")
        assert len(records) >= 1

    def test_parses_import_mention(self):
        records = self.parser.parse("forgot to add the import for the new module")
        assert len(records) >= 1

    def test_empty_string_returns_empty(self):
        records = self.parser.parse("")
        assert records == []

    def test_generic_note_returns_something(self):
        records = self.parser.parse("something went wrong with the build")
        assert len(records) >= 1

    def test_raw_populated(self):
        text = "agent used wrong variable name for the config"
        records = self.parser.parse(text)
        if records:
            assert records[0].raw == text

    def test_parses_timeout_mention(self):
        records = self.parser.parse("the API call timed out waiting for the server")
        assert len(records) >= 1

    def test_multiple_error_types_in_notes(self):
        text = "got AttributeError and also ImportError in the same run"
        records = self.parser.parse(text)
        types = [r.type for r in records]
        assert "AttributeError" in types
        assert "ImportError" in types
