"""Unit tests for string escaping in StructureGenerator.

This module tests that field descriptions and default values containing special
characters (quotes, backslashes, newlines, etc.) are properly escaped in the
generated Python code.

Note: E501 (line too long) is disabled for this file because we test exact
string matching against generator output, which produces long lines for long
descriptions.
"""

from pipelex.core.concepts.concept_structure_blueprint import ConceptStructureBlueprint, ConceptStructureBlueprintFieldType
from pipelex.core.concepts.structure_generation.generator import StructureGenerator
from pipelex.core.stuffs.structured_content import StructuredContent


class TestStructureGeneratorEscaping:
    """Test string escaping in structure generation."""

    def test_escape_double_quotes_in_description(self):
        """Test that double quotes in field descriptions are properly escaped."""
        description = 'The species or type of animal (e.g., "fox", "penguin", "octopus")'

        blueprint = {
            "animal_type": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("Animal", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class Animal(StructuredContent):
    """Generated Animal class"""

    animal_type: str = Field(..., description="The species or type of animal (e.g., \\"fox\\", \\"penguin\\", \\"octopus\\")")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(animal_type="fox")  # pyright: ignore[reportCallIssue]
        assert instance.animal_type == "fox"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_single_quotes_in_description(self):
        """Test that single quotes in field descriptions are properly escaped."""
        description = "This is John's description with a single quote"

        blueprint = {
            "field_name": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("TestClass", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class TestClass(StructuredContent):
    """Generated TestClass class"""

    field_name: str = Field(..., description="This is John\'s description with a single quote")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(field_name="test")  # pyright: ignore[reportCallIssue]
        assert instance.field_name == "test"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_mixed_quotes_in_description(self):
        """Test that mixed single and double quotes are properly escaped."""
        description = """He said "it's working!" with excitement"""

        blueprint = {
            "message": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("Message", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class Message(StructuredContent):
    """Generated Message class"""

    message: str = Field(..., description="He said \\"it\'s working!\\" with excitement")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(message="Hello world")  # pyright: ignore[reportCallIssue]
        assert instance.message == "Hello world"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_backslashes_in_description(self):
        """Test that backslashes in field descriptions are properly escaped."""
        description = r"Path separator: C:\Users\Documents\file.txt"

        blueprint = {
            "path": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("FilePath", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class FilePath(StructuredContent):
    """Generated FilePath class"""

    path: str = Field(..., description="Path separator: C:\\\\Users\\\\Documents\\\\file.txt")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(path="/usr/local/bin")  # pyright: ignore[reportCallIssue]
        assert instance.path == "/usr/local/bin"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_newlines_in_description(self):
        """Test that newlines in field descriptions are properly escaped."""
        description = "Line 1\nLine 2\nLine 3"

        blueprint = {
            "multiline": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("MultiLine", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class MultiLine(StructuredContent):
    """Generated MultiLine class"""

    multiline: str = Field(..., description="Line 1\\nLine 2\\nLine 3")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(multiline="test")  # pyright: ignore[reportCallIssue]
        assert instance.multiline == "test"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_tabs_in_description(self):
        """Test that tabs in field descriptions are properly escaped."""
        description = "Column1\tColumn2\tColumn3"

        blueprint = {
            "columns": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("Columns", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class Columns(StructuredContent):
    """Generated Columns class"""

    columns: str = Field(..., description="Column1\\tColumn2\\tColumn3")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(columns="A,B,C")  # pyright: ignore[reportCallIssue]
        assert instance.columns == "A,B,C"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_multiple_special_characters_combined(self):
        """Test combination of multiple special characters."""
        description = r"""Complex: "quoted", 'single', backslash\, tab	, newline
and more!"""

        blueprint = {
            "complex_field": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("ComplexType", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class ComplexType(StructuredContent):
    """Generated ComplexType class"""

    complex_field: str = Field(..., description="Complex: \\"quoted\\", \'single\', backslash\\\\, tab\\t, newline\\nand more!")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(complex_field="value")  # pyright: ignore[reportCallIssue]
        assert instance.complex_field == "value"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_default_value_with_quotes(self):
        """Test that default values with quotes are properly escaped."""
        default_value = 'Example: "quoted text"'

        blueprint = {
            "field_with_default": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description="A field with quoted default",
                required=False,
                default_value=default_value,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("WithDefault", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class WithDefault(StructuredContent):
    """Generated WithDefault class"""

    field_with_default: Optional[str] = Field(default="Example: \\"quoted text\\"", description="A field with quoted default")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class()  # pyright: ignore[reportCallIssue]
        assert instance.field_with_default == default_value  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
        instance2 = generated_class(field_with_default="custom")  # pyright: ignore[reportCallIssue]
        assert instance2.field_with_default == "custom"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_escape_default_value_with_backslashes(self):
        """Test that default values with backslashes are properly escaped."""
        default_value = r"C:\Program Files\App"

        blueprint = {
            "path": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description="Default path",
                required=False,
                default_value=default_value,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("DefaultPath", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class DefaultPath(StructuredContent):
    """Generated DefaultPath class"""

    path: Optional[str] = Field(default="C:\\\\Program Files\\\\App", description="Default path")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class()  # pyright: ignore[reportCallIssue]
        assert instance.path == default_value  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_empty_string_description(self):
        """Test that empty string descriptions are handled correctly."""
        description = ""

        blueprint = {
            "empty_desc": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("EmptyDesc", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class EmptyDesc(StructuredContent):
    """Generated EmptyDesc class"""

    empty_desc: str = Field(..., description="")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(empty_desc="value")  # pyright: ignore[reportCallIssue]
        assert instance.empty_desc == "value"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_very_long_description_with_quotes(self):
        """Test that very long descriptions with quotes are handled correctly."""
        description = (
            "This is a very long description that contains many words and also has "
            'some "quoted sections" that need to be properly escaped. It goes on and on '
            "with more details about the field, including examples like "
            '"example1", "example2", and "example3". The description continues with '
            "even more information to test the robustness of the escaping mechanism."
        )

        blueprint = {
            "long_field": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("LongDesc", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class LongDesc(StructuredContent):
    """Generated LongDesc class"""

    long_field: str = Field(..., description=(
        "This is a very long description that contains many words and also has some \\"quoted sections\\" that need to be properly "
        "escaped. It goes on and on with more details about the field, including examples like \\"example1\\", \\"example2\\", and "
        "\\"example3\\". The description continues with even more information to test the robustness of the escaping mechanism."
    ))
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(long_field="test")  # pyright: ignore[reportCallIssue]
        assert instance.long_field == "test"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_unicode_characters_in_description(self):
        """Test that unicode characters in descriptions are handled correctly."""
        description = "Unicode characters: café, naïve, 日本語, emoji 🎉"

        blueprint = {
            "unicode_field": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("UnicodeTest", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class UnicodeTest(StructuredContent):
    """Generated UnicodeTest class"""

    unicode_field: str = Field(..., description="Unicode characters: café, naïve, 日本語, emoji 🎉")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(unicode_field="test")  # pyright: ignore[reportCallIssue]
        assert instance.unicode_field == "test"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_carriage_return_in_description(self):
        """Test that carriage returns in descriptions are handled correctly."""
        description = "Line 1\r\nLine 2\r\nLine 3"

        blueprint = {
            "crlf_field": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=description,
                required=True,
            )
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("CRLF", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class CRLF(StructuredContent):
    """Generated CRLF class"""

    crlf_field: str = Field(..., description="Line 1\\r\\nLine 2\\r\\nLine 3")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(crlf_field="test")  # pyright: ignore[reportCallIssue]
        assert instance.crlf_field == "test"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

    def test_multiple_fields_with_various_escaping_needs(self):
        """Test a realistic scenario with multiple fields needing different escaping."""
        blueprint = {
            "animal_type": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description='The species or type of animal (e.g., "fox", "penguin", "octopus")',
                required=True,
            ),
            "owner_name": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description="The owner's full name",
                required=False,
            ),
            "file_path": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description=r"Location on disk (e.g., C:\Animals\data.txt)",
                required=False,
                default_value=r"C:\default\path",
            ),
            "notes": ConceptStructureBlueprint(
                type=ConceptStructureBlueprintFieldType.TEXT,
                description='Notes with "quotes" and\nnewlines',
                required=False,
            ),
        }

        generator = StructureGenerator()
        generated_code, generated_class = generator.generate_from_structure_blueprint("AnimalRecord", blueprint)

        expected_code = '''\
"""
AUTOGENERATED CODE - DO NOT EDIT

If you want to customize this structure:
  1. Copy this file to your own module
  2. Remove the 'structure' or 'refines' declaration from the concept in the MTHDS file
     and declare it in inline mode (see https://docs.pipelex.com/latest/building-methods/concepts/define_your_concepts/#basic-concept-definition)
  3. Make sure your custom class is importable and registered

To regenerate: pipelex build structures <target_directory>
"""

from enum import Enum
from pipelex.core.stuffs.structured_content import StructuredContent
from pydantic import Field
from typing import Optional, List, Dict, Any, Literal


class AnimalRecord(StructuredContent):
    """Generated AnimalRecord class"""

    animal_type: str = Field(..., description="The species or type of animal (e.g., \\"fox\\", \\"penguin\\", \\"octopus\\")")
    owner_name: Optional[str] = Field(default=None, description="The owner\'s full name")
    file_path: Optional[str] = Field(default="C:\\\\default\\\\path", description="Location on disk (e.g., C:\\\\Animals\\\\data.txt)")
    notes: Optional[str] = Field(default=None, description="Notes with \\"quotes\\" and\\nnewlines")
'''

        assert generated_code == expected_code
        assert issubclass(generated_class, StructuredContent)
        instance = generated_class(animal_type="dog", owner_name="John Smith", file_path=r"C:\custom\location", notes="Some notes here")  # pyright: ignore[reportCallIssue]
        assert instance.animal_type == "dog"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
        assert instance.owner_name == "John Smith"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
        assert instance.file_path == r"C:\custom\location"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
        assert instance.notes == "Some notes here"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]

        # Verify instantiation with only required fields
        instance2 = generated_class(animal_type="cat")  # pyright: ignore[reportCallIssue]
        assert instance2.animal_type == "cat"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
        assert instance2.file_path == r"C:\default\path"  # type: ignore[attr-defined]  # pyright: ignore[reportAttributeAccessIssue]
