import XCTest
@testable import CollapseLogic

final class LevelLoaderTests: XCTestCase {

    // Inline JSON helper to avoid needing the bundle in unit tests
    private func loadFromJSON(_ json: String) throws -> GameState {
        let data = Data(json.utf8)
        let def = try JSONDecoder().decode(LevelDefinition.self, from: data)
        try LevelLoader.validate(def)
        return LevelLoader.makeGameState(from: def)
    }

    private let validJSON = """
    {
      "id": "w1_test",
      "world": 1, "level": 1, "title": "Test",
      "grid": { "width": 5, "height": 5 },
      "blocks": [
        { "x": 0, "y": 0, "color": "red" },
        { "x": 4, "y": 4, "color": "blue" }
      ],
      "walls": [{ "x": 2, "y": 2 }],
      "special_tiles": [],
      "objective": { "type": "clear_all" },
      "par": 3,
      "hints": []
    }
    """

    func testValidLevelParses() throws {
        let state = try loadFromJSON(validJSON)
        XCTAssertEqual(state.blocks.count, 2)
        XCTAssertEqual(state.walls.count, 1)
        XCTAssertEqual(state.par, 3)
        XCTAssertEqual(state.grid.width, 5)
        XCTAssertEqual(state.grid.height, 5)
        XCTAssertEqual(state.objective, .clearAll)
    }

    func testBlockPositionsCorrect() throws {
        let state = try loadFromJSON(validJSON)
        let redBlock = state.blocks.first(where: { $0.color == .red })
        XCTAssertNotNil(redBlock)
        XCTAssertEqual(redBlock?.position, GridPosition(x: 0, y: 0))
    }

    func testWallPositionsCorrect() throws {
        let state = try loadFromJSON(validJSON)
        XCTAssertTrue(state.walls.contains(GridPosition(x: 2, y: 2)))
    }

    func testGridTooSmallFails() {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 2, "height": 5 },
          "blocks": [{ "x": 0, "y": 0, "color": "red" }, { "x": 1, "y": 1, "color": "blue" }],
          "objective": { "type": "clear_all" }, "par": 1
        }
        """
        XCTAssertThrowsError(try loadFromJSON(json))
    }

    func testFewerThan2BlocksFails() {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 4, "height": 4 },
          "blocks": [{ "x": 0, "y": 0, "color": "red" }],
          "objective": { "type": "clear_all" }, "par": 1
        }
        """
        XCTAssertThrowsError(try loadFromJSON(json))
    }

    func testBlockOutOfBoundsFails() {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 4, "height": 4 },
          "blocks": [
            { "x": 0, "y": 0, "color": "red" },
            { "x": 9, "y": 0, "color": "blue" }
          ],
          "objective": { "type": "clear_all" }, "par": 1
        }
        """
        XCTAssertThrowsError(try loadFromJSON(json))
    }

    func testOverlappingPositionsFails() {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 4, "height": 4 },
          "blocks": [
            { "x": 1, "y": 1, "color": "red" },
            { "x": 1, "y": 1, "color": "blue" }
          ],
          "objective": { "type": "clear_all" }, "par": 1
        }
        """
        XCTAssertThrowsError(try loadFromJSON(json))
    }

    func testZeroPar() {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 4, "height": 4 },
          "blocks": [
            { "x": 0, "y": 0, "color": "red" },
            { "x": 3, "y": 0, "color": "red" }
          ],
          "objective": { "type": "clear_all" }, "par": 0
        }
        """
        XCTAssertThrowsError(try loadFromJSON(json))
    }

    func testClearColorObjectiveParsed() throws {
        let json = """
        {
          "id": "x", "world": 1, "level": 1,
          "grid": { "width": 4, "height": 4 },
          "blocks": [
            { "x": 0, "y": 0, "color": "red" },
            { "x": 3, "y": 0, "color": "red" }
          ],
          "objective": { "type": "clear_color", "color": "red" }, "par": 1
        }
        """
        let state = try loadFromJSON(json)
        XCTAssertEqual(state.objective, .clearColor(.red))
    }

    func testInitialMoveCountIsZero() throws {
        let state = try loadFromJSON(validJSON)
        XCTAssertEqual(state.moveCount, 0)
    }

    func testInitialHistoryIsEmpty() throws {
        let state = try loadFromJSON(validJSON)
        XCTAssertTrue(state.history.isEmpty)
    }

    // MARK: - World 2 Level Loading Tests

    /// All 12 World 2 level JSONs parse and validate without errors.
    func testAllWorld2LevelsLoad() throws {
        let levelJSONs = world2LevelJSONs()
        XCTAssertEqual(levelJSONs.count, 12, "Expected 12 World 2 level JSONs")
        for (idx, json) in levelJSONs.enumerated() {
            let levelNum = idx + 1
            XCTAssertNoThrow(
                try loadFromJSON(json),
                "World 2 level \(levelNum) failed to parse"
            )
        }
    }

    /// Pre-placed secondary color blocks (e.g. purple in w2_01) load with the correct color.
    func testPreplacedSecondaryColorsLoad() throws {
        // w2_07 "Inheritance": has pre-placed purple blocks at (4,1) and (4,3)
        let json = """
        {
          "id": "w2_07", "world": 2, "level": 7, "title": "Inheritance",
          "grid": { "width": 5, "height": 5 },
          "blocks": [
            { "x": 0, "y": 0, "color": "red" },
            { "x": 0, "y": 4, "color": "blue" },
            { "x": 4, "y": 1, "color": "purple" },
            { "x": 4, "y": 3, "color": "purple" }
          ],
          "walls": [{ "x": 2, "y": 0 }, { "x": 2, "y": 4 }],
          "special_tiles": [],
          "objective": { "type": "clear_all" },
          "par": 4, "hints": []
        }
        """
        let state = try loadFromJSON(json)
        let purpleBlocks = state.blocks.filter { $0.color == .purple }
        XCTAssertEqual(purpleBlocks.count, 2)
        XCTAssertTrue(purpleBlocks.contains(where: { $0.position == GridPosition(x: 4, y: 1) }))
        XCTAssertTrue(purpleBlocks.contains(where: { $0.position == GridPosition(x: 4, y: 3) }))
    }

    /// Metadata file contains two worlds, World 2 has 12 levels.
    func testWorld2MetadataLoads() throws {
        let json = """
        {
          "version": "1.0",
          "worlds": [
            {
              "id": 1, "name": "Primary",
              "description": "Learn the basics.",
              "new_mechanic": null,
              "levels": [
                { "id": "w1_01", "title": "First Push", "file": "world1/level_01.json", "is_challenge": false }
              ]
            },
            {
              "id": 2, "name": "Fusion",
              "description": "Discover what happens when two colors collide.",
              "new_mechanic": "merging",
              "unlock_stars": 15,
              "levels": [
                { "id": "w2_01", "title": "First Contact",   "file": "world2/level_01.json", "is_challenge": false },
                { "id": "w2_02", "title": "Bound Together",  "file": "world2/level_02.json", "is_challenge": false },
                { "id": "w2_03", "title": "The Third Color", "file": "world2/level_03.json", "is_challenge": false },
                { "id": "w2_04", "title": "Sun Stone",       "file": "world2/level_04.json", "is_challenge": false },
                { "id": "w2_05", "title": "Color Theory",    "file": "world2/level_05.json", "is_challenge": false },
                { "id": "w2_06", "title": "Triad",           "file": "world2/level_06.json", "is_challenge": false },
                { "id": "w2_07", "title": "Inheritance",     "file": "world2/level_07.json", "is_challenge": false },
                { "id": "w2_08", "title": "Reflection",      "file": "world2/level_08.json", "is_challenge": false },
                { "id": "w2_09", "title": "Cascade",         "file": "world2/level_09.json", "is_challenge": false },
                { "id": "w2_10", "title": "Chain Reaction",  "file": "world2/level_10.json", "is_challenge": false },
                { "id": "w2_11", "title": "The Long Way",    "file": "world2/level_11.json", "is_challenge": false },
                { "id": "w2_12", "title": "Prism",           "file": "world2/level_12.json", "is_challenge": true, "stars_required": 15 }
              ]
            }
          ]
        }
        """
        let data = Data(json.utf8)
        let metadata = try JSONDecoder().decode(MetadataFile.self, from: data)
        XCTAssertEqual(metadata.worlds.count, 2)
        let world2 = metadata.worlds.first(where: { $0.id == 2 })
        XCTAssertNotNil(world2)
        XCTAssertEqual(world2?.levels.count, 12)
        XCTAssertEqual(world2?.name, "Fusion")
        XCTAssertEqual(world2?.unlockStars, 15)
        XCTAssertEqual(world2?.newMechanic, "merging")
    }

    // MARK: - World 2 level JSON fixtures

    /// Returns inline JSON strings for all 12 World 2 levels.
    private func world2LevelJSONs() -> [String] {
        return [
            // Level 1 — First Contact
            """
            { "id": "w2_01", "world": 2, "level": 1, "title": "First Contact",
              "grid": { "width": 4, "height": 4 },
              "blocks": [
                { "x": 0, "y": 1, "color": "red" },
                { "x": 3, "y": 1, "color": "blue" },
                { "x": 1, "y": 3, "color": "purple" }
              ],
              "walls": [], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 2, "hints": [] }
            """,
            // Level 2 — Bound Together
            """
            { "id": "w2_02", "world": 2, "level": 2, "title": "Bound Together",
              "grid": { "width": 5, "height": 4 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 4, "y": 0, "color": "blue" },
                { "x": 2, "y": 3, "color": "purple" }
              ],
              "walls": [{ "x": 2, "y": 0 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 3, "hints": [] }
            """,
            // Level 3 — The Third Color
            """
            { "id": "w2_03", "world": 2, "level": 3, "title": "The Third Color",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 4, "y": 0, "color": "blue" },
                { "x": 0, "y": 4, "color": "red" },
                { "x": 4, "y": 4, "color": "blue" },
                { "x": 2, "y": 2, "color": "purple" },
                { "x": 2, "y": 3, "color": "purple" }
              ],
              "walls": [], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 4, "hints": [] }
            """,
            // Level 4 — Sun Stone
            """
            { "id": "w2_04", "world": 2, "level": 4, "title": "Sun Stone",
              "grid": { "width": 5, "height": 4 },
              "blocks": [
                { "x": 0, "y": 1, "color": "red" },
                { "x": 4, "y": 1, "color": "yellow" },
                { "x": 2, "y": 3, "color": "orange" }
              ],
              "walls": [], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 2, "hints": [] }
            """,
            // Level 5 — Color Theory
            """
            { "id": "w2_05", "world": 2, "level": 5, "title": "Color Theory",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "blue" },
                { "x": 4, "y": 0, "color": "yellow" },
                { "x": 2, "y": 4, "color": "green" }
              ],
              "walls": [{ "x": 2, "y": 2 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 3, "hints": [] }
            """,
            // Level 6 — Triad
            """
            { "id": "w2_06", "world": 2, "level": 6, "title": "Triad",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 4, "y": 0, "color": "blue" },
                { "x": 0, "y": 4, "color": "yellow" },
                { "x": 4, "y": 4, "color": "red" },
                { "x": 2, "y": 2, "color": "purple" },
                { "x": 3, "y": 3, "color": "orange" }
              ],
              "walls": [], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 5, "hints": [] }
            """,
            // Level 7 — Inheritance
            """
            { "id": "w2_07", "world": 2, "level": 7, "title": "Inheritance",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 0, "y": 4, "color": "blue" },
                { "x": 4, "y": 1, "color": "purple" },
                { "x": 4, "y": 3, "color": "purple" }
              ],
              "walls": [{ "x": 2, "y": 0 }, { "x": 2, "y": 4 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 4, "hints": [] }
            """,
            // Level 8 — Reflection
            """
            { "id": "w2_08", "world": 2, "level": 8, "title": "Reflection",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 2, "color": "red" },
                { "x": 4, "y": 2, "color": "yellow" },
                { "x": 2, "y": 0, "color": "orange" },
                { "x": 2, "y": 4, "color": "orange" }
              ],
              "walls": [{ "x": 2, "y": 2 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 4, "hints": [] }
            """,
            // Level 9 — Cascade
            """
            { "id": "w2_09", "world": 2, "level": 9, "title": "Cascade",
              "grid": { "width": 5, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "blue" },
                { "x": 4, "y": 0, "color": "yellow" },
                { "x": 0, "y": 4, "color": "red" },
                { "x": 4, "y": 4, "color": "blue" },
                { "x": 2, "y": 1, "color": "green" },
                { "x": 2, "y": 3, "color": "purple" }
              ],
              "walls": [{ "x": 2, "y": 2 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 5, "hints": [] }
            """,
            // Level 10 — Chain Reaction
            """
            { "id": "w2_10", "world": 2, "level": 10, "title": "Chain Reaction",
              "grid": { "width": 6, "height": 5 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 5, "y": 0, "color": "blue" },
                { "x": 0, "y": 4, "color": "yellow" },
                { "x": 5, "y": 4, "color": "red" },
                { "x": 3, "y": 2, "color": "purple" },
                { "x": 1, "y": 2, "color": "orange" }
              ],
              "walls": [{ "x": 2, "y": 0 }, { "x": 3, "y": 4 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 6, "hints": [] }
            """,
            // Level 11 — The Long Way
            """
            { "id": "w2_11", "world": 2, "level": 11, "title": "The Long Way",
              "grid": { "width": 6, "height": 6 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 5, "y": 0, "color": "yellow" },
                { "x": 0, "y": 5, "color": "blue" },
                { "x": 5, "y": 5, "color": "red" },
                { "x": 3, "y": 2, "color": "orange" },
                { "x": 2, "y": 3, "color": "purple" },
                { "x": 3, "y": 4, "color": "green" }
              ],
              "walls": [{ "x": 2, "y": 0 }, { "x": 3, "y": 5 }], "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 8, "hints": [] }
            """,
            // Level 12 — Prism (boss)
            """
            { "id": "w2_12", "world": 2, "level": 12, "title": "Prism",
              "grid": { "width": 6, "height": 6 },
              "blocks": [
                { "x": 0, "y": 0, "color": "red" },
                { "x": 5, "y": 0, "color": "blue" },
                { "x": 0, "y": 5, "color": "yellow" },
                { "x": 5, "y": 2, "color": "red" },
                { "x": 0, "y": 2, "color": "blue" },
                { "x": 5, "y": 5, "color": "yellow" },
                { "x": 2, "y": 1, "color": "purple" },
                { "x": 3, "y": 4, "color": "orange" },
                { "x": 1, "y": 4, "color": "green" }
              ],
              "walls": [{ "x": 2, "y": 0 }, { "x": 4, "y": 1 }, { "x": 1, "y": 3 },
                        { "x": 4, "y": 3 }, { "x": 2, "y": 5 }],
              "special_tiles": [],
              "objective": { "type": "clear_all" }, "par": 9, "hints": [] }
            """
        ]
    }
}
