//
//  ItineraryCustomItemTests.swift
//  SportsTimeTests
//
//  Tests for custom item movement and constraints.
//

import XCTest
@testable import SportsTime

private typealias H = ItineraryTestHelpers

final class ItineraryCustomItemTests: XCTestCase {

    private let testTripId = H.testTripId
    private let testDate = H.testDate

    // MARK: - Custom Item Movement Tests

    func test_customItem_canMoveToAnyDay() {
        // Given: A 5-day trip
        let constraints = ItineraryConstraints(tripDayCount: 5, items: [])
        let itineraryItem = ItineraryItem(tripId: testTripId, day: 1, sortOrder: 1.0, kind: .custom(CustomInfo(title: "Dinner", icon: "🍽️")))

        // Custom items can go on any day
        for day in 1...5 {
            XCTAssertTrue(constraints.isValidPosition(for: itineraryItem, day: day, sortOrder: 50), "Custom item should be valid on Day \(day)")
        }
    }

    func test_customItem_canMoveBeforeOrAfterGames() {
        // Given: A day with a game at sortOrder 100
        let game = H.makeGameItem(city: "Detroit", day: 2, sortOrder: 100)
        let constraints = ItineraryConstraints(tripDayCount: 3, items: [game])

        let customItem = ItineraryItem(tripId: testTripId, day: 2, sortOrder: 50, kind: .custom(CustomInfo(title: "Breakfast", icon: "🍳")))

        // Before game (sortOrder 50) - VALID
        XCTAssertTrue(constraints.isValidPosition(for: customItem, day: 2, sortOrder: 50), "Custom item before game should be valid")

        // After game (sortOrder 150) - VALID
        XCTAssertTrue(constraints.isValidPosition(for: customItem, day: 2, sortOrder: 150), "Custom item after game should be valid")
    }

    func test_customItem_cannotBeMovedOutsideTripRange() {
        // Given: A 3-day trip
        let constraints = ItineraryConstraints(tripDayCount: 3, items: [])
        let customItem = ItineraryItem(tripId: testTripId, day: 1, sortOrder: 1.0, kind: .custom(CustomInfo(title: "Test", icon: "⭐")))

        // Day 0 (before trip) - INVALID
        XCTAssertFalse(constraints.isValidPosition(for: customItem, day: 0, sortOrder: 50), "Day 0 should be invalid")

        // Day 4 (after trip) - INVALID
        XCTAssertFalse(constraints.isValidPosition(for: customItem, day: 4, sortOrder: 50), "Day 4 should be invalid (trip is 3 days)")
    }

    // MARK: - Move Validation Tests

    func test_moveValidation_customItem_blockedFromRow0() {
        // Row 0 should always be blocked for drops

        let customItem = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "Moving")
        let dayData = ItineraryDayData(id: 1, dayNumber: 1, date: testDate, games: [], items: [.customItem(customItem)], travelBefore: nil)

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

        // Attempt to move item to row 0
        let source = IndexPath(row: 1, section: 0)
        let proposed = IndexPath(row: 0, section: 0)

        let result = controller.tableView(controller.tableView, targetIndexPathForMoveFromRowAt: source, toProposedIndexPath: proposed)

        // Should NOT allow row 0
        XCTAssertNotEqual(result.row, 0, "Row 0 should be blocked for drops")
    }

    // MARK: - Cross-Day Movement Tests

    func test_moveItemBetweenDays_updatesDay() {
        // Given: Item on Day 1, move to Day 2
        let item = H.makeCustomItem(day: 1, sortOrder: 1.0, title: "Moving")

        let day1 = ItineraryDayData(id: 1, dayNumber: 1, date: testDate, games: [], items: [.customItem(item)], travelBefore: nil)
        let day2 = ItineraryDayData(id: 2, dayNumber: 2, date: H.dayAfter(testDate), games: [], items: [], travelBefore: nil)

        var capturedDay: Int = 0
        let controller = ItineraryTableViewController(style: .plain)
        controller.onCustomItemMoved = { _, day, _ in
            capturedDay = day
        }
        controller.reloadData(days: [day1, day2], travelValidRanges: [:], itineraryItems: [item])

        // Rows: 0=Day1 header, 1=item, 2=Day2 header
        // Move item (row 1) to after Day2 header (row 2 becomes row 2 after move)
        controller.tableView(controller.tableView, moveRowAt: IndexPath(row: 1, section: 0), to: IndexPath(row: 2, section: 0))

        XCTAssertEqual(capturedDay, 2, "Item should now belong to Day 2")
    }

    func test_moveItem_fromLastDayToFirstDay() {
        // Given: 3-day trip with item on Day 3
        // When: Moving to Day 1
        // Then: Day should be 1

        let item = H.makeCustomItem(day: 3, sortOrder: 1.0, title: "Moving")

        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: [], travelBefore: nil)
        let day3 = ItineraryDayData(id: 3, dayNumber: 3, date: H.dayAfter(H.dayAfter(testDate)), games: [], items: [.customItem(item)], travelBefore: nil)

        var capturedDay: Int = 0
        var capturedSortOrder: Double = 0
        let controller = ItineraryTableViewController(style: .plain)
        controller.onCustomItemMoved = { _, day, sortOrder in
            capturedDay = day
            capturedSortOrder = sortOrder
        }
        controller.reloadData(days: [day1, day2, day3], travelValidRanges: [:], itineraryItems: [item])

        // Rows: 0=Day1 header, 1=Day2 header, 2=Day3 header, 3=item
        // Move item to after Day1 header
        controller.tableView(controller.tableView, moveRowAt: IndexPath(row: 3, section: 0), to: IndexPath(row: 1, section: 0))

        XCTAssertEqual(capturedDay, 1, "Item should now be on Day 1")
        XCTAssertEqual(capturedSortOrder, 1.0, accuracy: 0.01, "First item on empty day should get sortOrder 1.0")
    }

    func test_moveItem_acrossMultipleDays_withGames() {
        // Given: Item on Day 3, games on Day 1
        // When: Moving to Day 1 (after games)

        let game1 = H.makeRichGame(city: "Detroit", hour: 14)
        let item = H.makeCustomItem(day: 3, sortOrder: 1.0, title: "Moving")

        let day1 = ItineraryDayData(id: 1, dayNumber: 1, date: testDate, games: [game1], items: [], travelBefore: nil)
        let day2 = ItineraryDayData(id: 2, dayNumber: 2, date: H.dayAfter(testDate), games: [], items: [], travelBefore: nil)
        let day3 = ItineraryDayData(id: 3, dayNumber: 3, date: H.dayAfter(H.dayAfter(testDate)), games: [], items: [.customItem(item)], travelBefore: nil)

        var capturedDay: Int = 0
        let controller = ItineraryTableViewController(style: .plain)
        controller.onCustomItemMoved = { _, day, _ in
            capturedDay = day
        }
        controller.reloadData(days: [day1, day2, day3], travelValidRanges: [:], itineraryItems: [item])

        // Rows: 0=Day1 header, 1=games, 2=Day2 header, 3=Day3 header, 4=item
        // Move item to row 2 (after Day1 games, before Day2 header)
        controller.tableView(controller.tableView, moveRowAt: IndexPath(row: 4, section: 0), to: IndexPath(row: 2, section: 0))

        XCTAssertEqual(capturedDay, 1, "Item moved after Day 1 games should be on Day 1")
    }
}
