"""Tests for acceptance criteria parsing and validation."""

from __future__ import annotations

from dpe.models.acceptance_criteria import AcceptanceCriteriaSection, AcceptanceCriterion
from dpe.validation import (
    parse_acceptance_criteria,
    validate_prd_has_acceptance_criteria,
)


class TestAcceptanceCriterionModel:
    """Test the AcceptanceCriterion dataclass."""

    def test_basic_creation(self) -> None:
        ac = AcceptanceCriterion(
            ac_id="AC-001",
            title="User can log in",
            given="A registered user with valid credentials",
            when="The user submits the login form",
            then_clause="The user is redirected to the dashboard",
            and_clauses=[],
        )
        assert ac.ac_id == "AC-001"
        assert ac.title == "User can log in"
        assert len(ac.and_clauses) == 0

    def test_with_and_clauses(self) -> None:
        ac = AcceptanceCriterion(
            ac_id="AC-002",
            title="Password reset",
            given="A registered user who forgot their password",
            when="The user requests a password reset",
            then_clause="An email is sent with a reset link",
            and_clauses=[
                "The reset link expires in 24 hours",
                "The old password is invalidated",
            ],
        )
        assert len(ac.and_clauses) == 2
        assert "expires in 24 hours" in ac.and_clauses[0]

    def test_to_markdown(self) -> None:
        ac = AcceptanceCriterion(
            ac_id="AC-003",
            title="Simple test",
            given="precondition",
            when="action",
            then_clause="result",
            and_clauses=["extra1", "extra2"],
        )
        md = ac.to_markdown()
        assert "AC-003: Simple test" in md
        assert "Given: precondition" in md
        assert "When: action" in md
        assert "Then: result" in md
        assert "And: extra1" in md
        assert "And: extra2" in md


class TestAcceptanceCriteriaSectionModel:
    """Test the AcceptanceCriteriaSection dataclass."""

    def test_empty_section(self) -> None:
        section = AcceptanceCriteriaSection(criteria=[])
        assert not section.validate_not_empty()

    def test_non_empty_section(self) -> None:
        ac = AcceptanceCriterion(
            ac_id="AC-001",
            title="Test",
            given="g",
            when="w",
            then_clause="t",
        )
        section = AcceptanceCriteriaSection(criteria=[ac])
        assert section.validate_not_empty()

    def test_to_markdown(self) -> None:
        ac1 = AcceptanceCriterion(
            ac_id="AC-001",
            title="First",
            given="g1",
            when="w1",
            then_clause="t1",
        )
        ac2 = AcceptanceCriterion(
            ac_id="AC-002",
            title="Second",
            given="g2",
            when="w2",
            then_clause="t2",
        )
        section = AcceptanceCriteriaSection(criteria=[ac1, ac2])
        md = section.to_markdown()
        assert "## Acceptance Criteria" in md
        assert "AC-001" in md
        assert "AC-002" in md


class TestParseAcceptanceCriteria:
    """Test the parse_acceptance_criteria function."""

    def test_parse_single_criterion(self) -> None:
        prd = """
# Product Requirements Document

## Acceptance Criteria

AC-001: User can log in
Given: A registered user with valid credentials
When: The user submits the login form
Then: The user is redirected to the dashboard

## Other Section
Some content here.
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 1
        ac = section.criteria[0]
        assert ac.ac_id == "AC-001"
        assert ac.title == "User can log in"
        assert "registered user" in ac.given
        assert "submits the login form" in ac.when
        assert "redirected to the dashboard" in ac.then_clause
        assert len(ac.and_clauses) == 0

    def test_parse_multiple_criteria(self) -> None:
        prd = """
## Acceptance Criteria

AC-001: First feature
Given: Precondition 1
When: Action 1
Then: Result 1

AC-002: Second feature
Given: Precondition 2
When: Action 2
Then: Result 2
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 2
        assert section.criteria[0].ac_id == "AC-001"
        assert section.criteria[1].ac_id == "AC-002"

    def test_parse_with_and_clauses(self) -> None:
        prd = """
## Acceptance Criteria

AC-001: Complex test
Given: Initial state
When: User performs action
Then: Primary result occurs
And: Secondary result also occurs
And: Third result is observed

AC-002: Another test
Given: Different state
When: Different action
Then: Different result
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 2

        # First criterion should have 2 And clauses
        ac1 = section.criteria[0]
        assert ac1.ac_id == "AC-001"
        assert len(ac1.and_clauses) == 2
        assert "Secondary result" in ac1.and_clauses[0]
        assert "Third result" in ac1.and_clauses[1]

        # Second criterion should have no And clauses
        ac2 = section.criteria[1]
        assert ac2.ac_id == "AC-002"
        assert len(ac2.and_clauses) == 0

    def test_parse_missing_section(self) -> None:
        prd = """
# PRD without acceptance criteria

## Goals
Some goals here.

## Technical Requirements
Some requirements.
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 0
        assert not section.validate_not_empty()

    def test_parse_empty_section(self) -> None:
        prd = """
## Acceptance Criteria

## Next Section
Content here.
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 0

    def test_parse_case_insensitive(self) -> None:
        prd = """
## acceptance criteria

AC-001: Case test
given: lowercase given
when: lowercase when
then: lowercase then
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 1
        assert section.criteria[0].ac_id == "AC-001"

    def test_parse_with_extra_whitespace(self) -> None:
        prd = """
##   Acceptance Criteria

AC-001:   User logs in
Given:   Valid credentials
When:   Form submitted
Then:   Dashboard shown
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 1
        ac = section.criteria[0]
        assert ac.title == "User logs in"
        assert "Valid credentials" in ac.given


class TestValidatePrdHasAcceptanceCriteria:
    """Test the validate_prd_has_acceptance_criteria function."""

    def test_valid_prd_with_criteria(self) -> None:
        prd = """
# Product Requirements Document

## Acceptance Criteria

AC-001: Feature works
Given: System is ready
When: User performs action
Then: Expected result occurs
"""
        is_valid, error = validate_prd_has_acceptance_criteria(prd)
        assert is_valid
        assert error == ""

    def test_invalid_prd_without_section(self) -> None:
        prd = """
# Product Requirements Document

## Goals
Some goals.

## Requirements
Some requirements.
"""
        is_valid, error = validate_prd_has_acceptance_criteria(prd)
        assert not is_valid
        assert "missing or empty" in error
        assert "Acceptance Criteria" in error

    def test_invalid_prd_with_empty_section(self) -> None:
        prd = """
## Acceptance Criteria

## Next Section
"""
        is_valid, error = validate_prd_has_acceptance_criteria(prd)
        assert not is_valid
        assert "missing or empty" in error

    def test_valid_prd_with_multiple_criteria(self) -> None:
        prd = """
## Acceptance Criteria

AC-001: First
Given: State 1
When: Action 1
Then: Result 1

AC-002: Second
Given: State 2
When: Action 2
Then: Result 2
And: Extra validation

AC-003: Third
Given: State 3
When: Action 3
Then: Result 3
"""
        is_valid, error = validate_prd_has_acceptance_criteria(prd)
        assert is_valid
        assert error == ""


class TestAcceptanceCriteriaIntegration:
    """Integration tests for acceptance criteria workflow."""

    def test_full_workflow_parse_and_regenerate(self) -> None:
        """Test that we can parse criteria and regenerate markdown."""
        original_prd = """
## Acceptance Criteria

AC-001: User authentication
Given: A user with valid credentials
When: The user logs in
Then: Access is granted
And: Session token is created
"""
        section = parse_acceptance_criteria(original_prd)
        regenerated = section.to_markdown()

        assert "AC-001" in regenerated
        assert "User authentication" in regenerated
        assert "Given:" in regenerated
        assert "When:" in regenerated
        assert "Then:" in regenerated
        assert "And:" in regenerated

    def test_criteria_numbering_sequence(self) -> None:
        """Test that criteria can be numbered sequentially."""
        prd = """
## Acceptance Criteria

AC-001: First
Given: G1
When: W1
Then: T1

AC-002: Second
Given: G2
When: W2
Then: T2

AC-003: Third
Given: G3
When: W3
Then: T3
"""
        section = parse_acceptance_criteria(prd)
        assert len(section.criteria) == 3
        ids = [c.ac_id for c in section.criteria]
        assert ids == ["AC-001", "AC-002", "AC-003"]
