from xproject_creator.objects import (
    PBXBuildFile,
    PBXFileReference,
    PBXGroup,
    PBXProject,
    XCBuildConfiguration,
    XCConfigurationList,
)
from xproject_creator.serializer import (
    comment_for,
    needs_quoting,
    quote,
    serialize_object,
    serialize_project,
)


def test_quoting_simple_identifiers():
    assert not needs_quoting("Debug")
    assert not needs_quoting("YES")
    assert not needs_quoting("sourcecode.swift")
    assert not needs_quoting("0")


def test_quoting_special_chars():
    assert needs_quoting("<group>")
    assert needs_quoting("$(inherited)")
    assert needs_quoting("com.apple.product-type.application")
    assert needs_quoting("dwarf-with-dsym")
    assert needs_quoting("")
    assert needs_quoting("hello world")


def test_quote_wraps():
    assert quote("Debug") == "Debug"
    assert quote("<group>") == '"<group>"'
    assert quote("") == '""'


def test_serialize_minimal_project():
    """Test that a minimal project serializes to valid ASCII plist."""
    objects = {}

    file_ref = PBXFileReference(
        "AAAAAAAAAAAAAAAAAAAAAAAA", "test.swift", last_known_file_type="sourcecode.swift"
    )
    objects[file_ref.object_key] = file_ref

    group = PBXGroup("BBBBBBBBBBBBBBBBBBBBBBBB", children=[file_ref.object_key], path="src")
    objects[group.object_key] = group

    config = XCBuildConfiguration(
        "CCCCCCCCCCCCCCCCCCCCCCCC", name="Debug", build_settings={"SWIFT_VERSION": "5.0"}
    )
    objects[config.object_key] = config

    config_list = XCConfigurationList(
        "DDDDDDDDDDDDDDDDDDDDDDDD", build_configurations=[config.object_key]
    )
    objects[config_list.object_key] = config_list

    project = PBXProject(
        "EEEEEEEEEEEEEEEEEEEEEEEE",
        build_configuration_list=config_list.object_key,
        main_group=group.object_key,
    )
    objects[project.object_key] = project

    output = serialize_project(project.object_key, objects)

    # Check structure
    assert output.startswith("// !$*UTF8*$!\n{")
    assert "/* Begin PBXFileReference section */" in output
    assert "/* End PBXFileReference section */" in output
    assert "/* Begin PBXGroup section */" in output
    assert "/* Begin PBXProject section */" in output
    assert "/* Begin XCBuildConfiguration section */" in output
    assert "/* Begin XCConfigurationList section */" in output
    assert "rootObject = EEEEEEEEEEEEEEEEEEEEEEEE /* Project object */;" in output
    assert "SWIFT_VERSION = 5.0;" in output


def test_quote_escapes():
    assert quote('say "hello"') == '"say \\"hello\\""'
    assert quote("line\nnewline") == '"line\\nnewline"'
    assert quote("back\\slash") == '"back\\\\slash"'


def test_comment_for_known_object():
    fr = PBXFileReference("A" * 24, "test.swift")
    objects = {"A" * 24: fr}
    assert comment_for("A" * 24, objects) == " /* test.swift */"


def test_comment_for_unknown_object():
    assert comment_for("UNKNOWN" + "0" * 17, {}) == ""


def test_serialize_single_line_build_file():
    """PBXBuildFile should serialize on a single line."""
    fr = PBXFileReference("A" * 24, "test.swift", last_known_file_type="sourcecode.swift")
    bf = PBXBuildFile("B" * 24, file_ref="A" * 24, file_ref_comment="test.swift")
    objects = {"A" * 24: fr, "B" * 24: bf}

    output = serialize_object("B" * 24, bf, objects)
    # Single line: no internal newlines within the object definition
    lines = output.strip().split("\n")
    assert len(lines) == 1
    assert "test.swift" in output
    assert "fileRef" in output


def test_serialize_single_line_file_reference():
    """PBXFileReference should serialize on a single line."""
    fr = PBXFileReference("A" * 24, "test.swift", last_known_file_type="sourcecode.swift")
    objects = {"A" * 24: fr}

    output = serialize_object("A" * 24, fr, objects)
    lines = output.strip().split("\n")
    assert len(lines) == 1


def test_serialize_multiline_group():
    """PBXGroup should serialize multi-line."""
    fr = PBXFileReference("A" * 24, "test.swift")
    g = PBXGroup("B" * 24, children=["A" * 24], path="src")
    objects = {"A" * 24: fr, "B" * 24: g}

    output = serialize_object("B" * 24, g, objects)
    lines = output.strip().split("\n")
    assert len(lines) > 1
    assert "children" in output
    assert "test.swift" in output


def test_serialize_empty_dict_value():
    """Empty dicts in build settings should serialize correctly."""
    cfg = XCBuildConfiguration("A" * 24, name="Debug")
    objects = {"A" * 24: cfg}
    output = serialize_object("A" * 24, cfg, objects)
    assert "buildSettings = {\n" in output


def test_serialize_list_with_references():
    """Lists containing object references should get comments."""
    fr = PBXFileReference("A" * 24, "test.swift")
    g = PBXGroup("B" * 24, children=["A" * 24])
    objects = {"A" * 24: fr, "B" * 24: g}

    output = serialize_object("B" * 24, g, objects)
    assert "/* test.swift */" in output


def test_serialize_build_file_with_settings():
    """PBXBuildFile with settings dict should include settings inline."""
    bf = PBXBuildFile(
        "A" * 24,
        file_ref="B" * 24,
        settings={"ATTRIBUTES": ["RemoveHeadersOnCopy"]},
    )
    objects = {"A" * 24: bf}
    output = serialize_object("A" * 24, bf, objects)
    assert "ATTRIBUTES" in output
    assert "RemoveHeadersOnCopy" in output


def test_serialize_object_version():
    """Custom objectVersion should be reflected in output."""
    cfg = XCBuildConfiguration("A" * 24, name="Debug")
    proj = PBXProject("B" * 24, build_configuration_list="A" * 24, main_group="A" * 24)
    cl = XCConfigurationList("C" * 24, build_configurations=["A" * 24])
    objects = {"A" * 24: cfg, "B" * 24: proj, "C" * 24: cl}

    output = serialize_project("B" * 24, objects, object_version="77")
    assert "objectVersion = 77;" in output


def test_serialize_single_line_empty_list():
    """PBXBuildFile with empty settings containing an empty list."""
    bf = PBXBuildFile(
        "A" * 24,
        file_ref="B" * 24,
        settings={"ATTRIBUTES": []},
    )
    objects = {"A" * 24: bf}
    output = serialize_object("A" * 24, bf, objects)
    assert "( )" in output


def test_serialize_single_line_list_with_ref():
    """Single-line object with list containing object references."""
    fr = PBXFileReference("A" * 24, "test.swift")
    bf = PBXBuildFile(
        "B" * 24,
        file_ref="A" * 24,
        settings={"REFS": ["A" * 24]},
    )
    objects = {"A" * 24: fr, "B" * 24: bf}
    output = serialize_object("B" * 24, bf, objects)
    assert "test.swift" in output


def test_isa_first_single_line():
    """isa should be the first key in single-line serialization."""
    fr = PBXFileReference("A" * 24, "test.swift", last_known_file_type="sourcecode.swift")
    objects = {"A" * 24: fr}
    output = serialize_object("A" * 24, fr, objects)
    # Extract the part after "= {"
    inner = output.split("= {")[1].split("}")[0].strip()
    first_key = inner.split("=")[0].strip()
    assert first_key == "isa"


def test_isa_first_multiline():
    """isa should be the first key in multi-line serialization."""
    g = PBXGroup("A" * 24, children=[], path="src")
    objects = {"A" * 24: g}
    output = serialize_object("A" * 24, g, objects)
    lines = output.strip().split("\n")
    # First line is the opening, second should be isa
    second_line = lines[1].strip()
    assert second_line.startswith("isa =")
