//
//  ItineraryRowFlatteningTests.swift
//  SportsTimeTests
//
//  Tests for row flattening order and ItineraryRowItem model.
//

import XCTest
@testable import SportsTime

private typealias H = ItineraryTestHelpers

final class ItineraryRowFlatteningTests: XCTestCase {

    private let testDate = H.testDate

    // MARK: - Row Flattening Order Tests

    /// Verifies that rows are flattened in correct order under SEMANTIC TRAVEL MODEL:
    /// 1. Day header
    /// 2. Items with sortOrder < 0 (before games, including travel)
    /// 3. Games
    /// 4. Items with sortOrder >= 0 (after games, including travel)
    ///
    /// NOTE: travelBefore is IGNORED - travel must be in items with sortOrder to appear.
    func test_rowFlattening_correctOrder_semanticTravel() {
        // Given: A day with travel in items (before games), games, and custom item (after games)
        let travel = H.makeTravelSegment(from: "Chicago", to: "Detroit")
        let travelItem = H.makeTravelItem(from: "Chicago", to: "Detroit", day: 1, sortOrder: -1.0)
        let games = [H.makeRichGame(city: "Detroit", hour: 19)]
        let customItem = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "Dinner")

        let dayData = ItineraryDayData(
            id: 1,
            dayNumber: 1,
            date: testDate,
            games: games,
            items: [.travel(travel, dayNumber: 1), .customItem(customItem)],
            travelBefore: nil  // travelBefore is IGNORED under semantic model
        )

        // When: Controller reloads with travel having negative sortOrder (before games)
        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: [dayData], travelValidRanges: [:], itineraryItems: [travelItem, customItem])

        // Then: Order should be: header, travel (before games), games, custom (after games)
        let rowCount = controller.tableView(controller.tableView, numberOfRowsInSection: 0)
        XCTAssertEqual(rowCount, 4, "Expected 4 rows: header, travel, games, custom item")

        // Verify order by reorderability
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 0, section: 0)), "Row 0 = Header (NOT reorderable)")
        XCTAssertTrue(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 1, section: 0)), "Row 1 = Travel (reorderable)")
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 2, section: 0)), "Row 2 = Games (NOT reorderable)")
        XCTAssertTrue(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 3, section: 0)), "Row 3 = Custom item (reorderable)")
    }

    func test_rowFlattening_itemsBeforeGames_negativeSortOrder() {
        // Given: Custom items with negative sortOrder should appear BEFORE games
        let games = [H.makeRichGame(city: "Detroit", hour: 19)]
        let beforeItem = H.makeCustomItem(day: 1, sortOrder: -1.0, title: "Morning coffee")
        let afterItem = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "Dinner")

        let dayData = ItineraryDayData(
            id: 1,
            dayNumber: 1,
            date: testDate,
            games: games,
            items: [.customItem(beforeItem), .customItem(afterItem)],
            travelBefore: nil
        )

        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: [dayData], travelValidRanges: [:], itineraryItems: [beforeItem, afterItem])

        // Then: Order should be header, beforeItem, games, afterItem
        let rowCount = controller.tableView(controller.tableView, numberOfRowsInSection: 0)
        XCTAssertEqual(rowCount, 4, "Expected 4 rows: header, before-item, games, after-item")

        // Verify the before-games item appears at row 1 (after header at row 0)
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 0, section: 0)), "Row 0 should be header (not reorderable)")
        XCTAssertTrue(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 1, section: 0)), "Row 1 should be before-item (reorderable)")
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 2, section: 0)), "Row 2 should be games (not reorderable)")
        XCTAssertTrue(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 3, section: 0)), "Row 3 should be after-item (reorderable)")
    }

    func test_rowFlattening_multipleItemsSortedBySortOrder() {
        // Given: Multiple custom items should be sorted by sortOrder
        let item1 = H.makeCustomItem(day: 1, sortOrder: 3.0, title: "Third")
        let item2 = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "First")
        let item3 = H.makeCustomItem(day: 1, sortOrder: 2.0, title: "Second")

        let dayData = ItineraryDayData(
            id: 1,
            dayNumber: 1,
            date: testDate,
            games: [],
            items: [.customItem(item1), .customItem(item2), .customItem(item3)],
            travelBefore: nil
        )

        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: [dayData], travelValidRanges: [:], itineraryItems: [item1, item2, item3])

        // Then: Items should appear in sortOrder: First (1.0), Second (2.0), Third (3.0)
        let rowCount = controller.tableView(controller.tableView, numberOfRowsInSection: 0)
        XCTAssertEqual(rowCount, 4, "Expected 4 rows: header + 3 items")
    }

    // MARK: - Day Number Calculation Tests

    func test_dayNumber_firstDayHeader_returnsDay1() {
        // Given: A simple 3-day trip
        let days = H.makeDays(count: 3)
        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: days, travelValidRanges: [:])

        // The first row should be Day 1 header
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 0, section: 0)), "First row should be header")
    }

    func test_dayNumber_rowAfterHeader_belongsToSameDay() {
        // Given: A day with games
        let games = [H.makeRichGame(city: "Detroit", hour: 19)]
        let dayData = ItineraryDayData(id: 1, dayNumber: 1, date: testDate, games: games, items: [], travelBefore: nil)

        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: [dayData], travelValidRanges: [:])

        // Row 0 = header (Day 1), Row 1 = games (belongs to Day 1)
        XCTAssertEqual(controller.tableView(controller.tableView, numberOfRowsInSection: 0), 2)
    }

    func test_dayNumber_travelRow_belongsToItsDay() {
        // Given: Travel in Day 2's items (semantic model - travelBefore is ignored)
        let travel = H.makeTravelSegment(from: "Chicago", to: "Detroit")
        let travelItem = H.makeTravelItem(from: "Chicago", to: "Detroit", day: 2, sortOrder: 1.0)
        let day1 = ItineraryDayData(id: 1, dayNumber: 1, date: testDate, games: [], items: [], travelBefore: nil)
        let day2 = ItineraryDayData(
            id: 2,
            dayNumber: 2,
            date: H.dayAfter(testDate),
            games: [],
            items: [.travel(travel, dayNumber: 2)],
            travelBefore: nil  // travelBefore is IGNORED under semantic model
        )

        let controller = ItineraryTableViewController(style: .plain)
        controller.reloadData(days: [day1, day2], travelValidRanges: [:], itineraryItems: [travelItem])

        // Row order: Day1 header, Day2 header, travel (in day 2's after-games region)
        let rowCount = controller.tableView(controller.tableView, numberOfRowsInSection: 0)
        XCTAssertEqual(rowCount, 3, "Expected: Day1 header, Day2 header, travel")

        // Travel is reorderable and belongs to Day 2 (positioned after Day 2 header)
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 0, section: 0)), "Day 1 header")
        XCTAssertFalse(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 1, section: 0)), "Day 2 header")
        XCTAssertTrue(controller.tableView(controller.tableView, canMoveRowAt: IndexPath(row: 2, section: 0)), "Travel is reorderable")
    }

    // MARK: - ItineraryRowItem Tests

    func test_itineraryRowItem_dayHeader_hasCorrectId() {
        let item = ItineraryRowItem.dayHeader(dayNumber: 3, date: testDate)
        XCTAssertEqual(item.id, "day:3")
    }

    func test_itineraryRowItem_games_hasCorrectId() {
        let games = [H.makeRichGame(city: "Detroit", hour: 19)]
        let item = ItineraryRowItem.games(games, dayNumber: 2)
        XCTAssertEqual(item.id, "games:2")
    }

    func test_itineraryRowItem_travel_hasStableId() {
        let segment = H.makeTravelSegment(from: "Chicago", to: "Detroit")
        let item = ItineraryRowItem.travel(segment, dayNumber: 1)
        XCTAssertTrue(item.id.hasPrefix("travel:"), "Travel ID should start with 'travel:'")
        XCTAssertTrue(item.id.contains(segment.id.uuidString), "Travel ID should contain segment UUID")
    }

    func test_itineraryRowItem_customItem_hasUuidId() {
        let customItem = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "Test")
        let item = ItineraryRowItem.customItem(customItem)
        XCTAssertTrue(item.id.hasPrefix("item:"), "Custom item ID should start with 'item:'")
    }

    func test_itineraryRowItem_reorderability() {
        XCTAssertFalse(ItineraryRowItem.dayHeader(dayNumber: 1, date: testDate).isReorderable)
        XCTAssertFalse(ItineraryRowItem.games([], dayNumber: 1).isReorderable)
        XCTAssertTrue(ItineraryRowItem.travel(H.makeTravelSegment(from: "A", to: "B"), dayNumber: 1).isReorderable)
        XCTAssertTrue(ItineraryRowItem.customItem(H.makeCustomItem(day: 1, sortOrder: 1.0, title: "X")).isReorderable)
    }
}
