//
//  TripDetailView.swift
//  SportsTime
//

import SwiftUI
import SwiftData
import MapKit
import Combine
import UniformTypeIdentifiers

struct TripDetailView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.colorScheme) private var colorScheme

    let trip: Trip
    private let providedGames: [String: RichGame]?

    /// When true, shows Add buttons and custom items (only for saved trips)
    private let allowCustomItems: Bool

    @Query private var savedTrips: [SavedTrip]
    @State private var showProPaywall = false
    @State private var selectedDay: ItineraryDay?
    @State private var showExportSheet = false
    @State private var exportURL: URL?
    @State private var isExporting = false
    @State private var exportProgress: PDFAssetPrefetcher.PrefetchProgress?
    @State private var mapCameraPosition: MapCameraPosition = .automatic
    @State private var isSaved = false
    @State private var routePolylines: [MKPolyline] = []
    @State private var isLoadingRoutes = false
    @State private var loadedGames: [String: RichGame] = [:]
    @State private var isLoadingGames = false

    // Custom items state
    @State private var customItems: [CustomItineraryItem] = []
    @State private var addItemAnchor: AddItemAnchor?
    @State private var editingItem: CustomItineraryItem?
    @State private var subscriptionCancellable: AnyCancellable?
    @State private var draggedItem: CustomItineraryItem?
    @State private var dropTargetId: String?  // Track which drop zone is being hovered

    private let exportService = ExportService()
    private let dataProvider = AppDataProvider.shared

    /// Games dictionary - uses provided games if available, otherwise uses loaded games
    private var games: [String: RichGame] {
        providedGames ?? loadedGames
    }

    /// Initialize with trip and games dictionary (existing callers - no custom items)
    init(trip: Trip, games: [String: RichGame]) {
        self.trip = trip
        self.providedGames = games
        self.allowCustomItems = false
    }

    /// Initialize with just trip - games will be loaded from AppDataProvider (no custom items)
    init(trip: Trip) {
        self.trip = trip
        self.providedGames = nil
        self.allowCustomItems = false
    }

    /// Initialize for saved trip context - enables custom items
    init(trip: Trip, games: [String: RichGame], allowCustomItems: Bool) {
        self.trip = trip
        self.providedGames = games
        self.allowCustomItems = allowCustomItems
    }

    /// Initialize for saved trip context without provided games - loads from AppDataProvider
    init(trip: Trip, allowCustomItems: Bool) {
        self.trip = trip
        self.providedGames = nil
        self.allowCustomItems = allowCustomItems
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 0) {
                // Hero Map
                heroMapSection
                    .frame(height: 280)

                // Content
                VStack(spacing: Theme.Spacing.lg) {
                    // Header
                    tripHeader
                        .padding(.top, Theme.Spacing.lg)

                    // Stats Row
                    statsRow

                    // Score Card
                    if let score = trip.score {
                        scoreCard(score)
                    }

                    // Day-by-Day Itinerary
                    itinerarySection
                }
                .padding(.horizontal, Theme.Spacing.lg)
                .padding(.bottom, Theme.Spacing.xxl)
            }
        }
        .background(Theme.backgroundGradient(colorScheme))
        .toolbarBackground(Theme.cardBackground(colorScheme), for: .navigationBar)
        .toolbar {
            ToolbarItemGroup(placement: .primaryAction) {
                ShareButton(trip: trip, style: .icon)
                    .foregroundStyle(Theme.warmOrange)

                Button {
                    if StoreManager.shared.isPro {
                        Task {
                            await exportPDF()
                        }
                    } else {
                        showProPaywall = true
                    }
                } label: {
                    HStack(spacing: 2) {
                        Image(systemName: "doc.fill")
                        if !StoreManager.shared.isPro {
                            ProBadge()
                        }
                    }
                    .foregroundStyle(Theme.warmOrange)
                }
            }
        }
        .sheet(isPresented: $showExportSheet) {
            if let url = exportURL {
                ShareSheet(items: [url])
            }
        }
        .sheet(isPresented: $showProPaywall) {
            PaywallView()
        }
        .sheet(item: $addItemAnchor) { anchor in
            AddItemSheet(
                tripId: trip.id,
                anchorDay: anchor.day,
                anchorType: anchor.type,
                anchorId: anchor.anchorId,
                existingItem: nil
            ) { item in
                Task { await saveCustomItem(item) }
            }
        }
        .sheet(item: $editingItem) { item in
            AddItemSheet(
                tripId: trip.id,
                anchorDay: item.anchorDay,
                anchorType: item.anchorType,
                anchorId: item.anchorId,
                existingItem: item
            ) { updatedItem in
                Task { await saveCustomItem(updatedItem) }
            }
        }
        .onAppear {
            checkIfSaved()
        }
        .task {
            await loadGamesIfNeeded()
            if allowCustomItems {
                await loadCustomItems()
                await setupSubscription()
            }
        }
        .onDisappear {
            subscriptionCancellable?.cancel()
        }
        .onChange(of: customItems) { _, _ in
            // Clear drag state after items update (move completed)
            draggedItem = nil
            dropTargetId = nil
        }
        .overlay {
            if isExporting {
                exportProgressOverlay
            }
        }
    }

    // MARK: - Export Progress Overlay

    private var exportProgressOverlay: some View {
        ZStack {
            // Background dimmer
            Color.black.opacity(0.6)
                .ignoresSafeArea()

            // Progress card
            VStack(spacing: Theme.Spacing.lg) {
                // Progress ring
                ZStack {
                    Circle()
                        .stroke(Theme.cardBackgroundElevated(colorScheme), lineWidth: 8)
                        .frame(width: 80, height: 80)

                    Circle()
                        .trim(from: 0, to: exportProgress?.percentComplete ?? 0)
                        .stroke(Theme.warmOrange, style: StrokeStyle(lineWidth: 8, lineCap: .round))
                        .frame(width: 80, height: 80)
                        .rotationEffect(.degrees(-90))
                        .animation(.easeInOut(duration: 0.3), value: exportProgress?.percentComplete)

                    Image(systemName: "doc.fill")
                        .font(.title2)
                        .foregroundStyle(Theme.warmOrange)
                }

                VStack(spacing: Theme.Spacing.xs) {
                    Text("Creating PDF")
                        .font(.headline)
                        .foregroundStyle(Theme.textPrimary(colorScheme))

                    Text(exportProgress?.currentStep ?? "Preparing...")
                        .font(.subheadline)
                        .foregroundStyle(Theme.textSecondary(colorScheme))
                        .multilineTextAlignment(.center)

                    if let progress = exportProgress {
                        Text("\(Int(progress.percentComplete * 100))%")
                            .font(.caption.monospaced())
                            .foregroundStyle(Theme.textMuted(colorScheme))
                    }
                }
            }
            .padding(Theme.Spacing.xl)
            .background(Theme.cardBackground(colorScheme))
            .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large))
            .shadow(color: .black.opacity(0.3), radius: 20, y: 10)
        }
        .transition(.opacity)
    }

    // MARK: - Hero Map Section

    private var heroMapSection: some View {
        ZStack(alignment: .bottom) {
            Map(position: $mapCameraPosition, interactionModes: []) {
                ForEach(stopCoordinates.indices, id: \.self) { index in
                    let stop = stopCoordinates[index]
                    Annotation(stop.name, coordinate: stop.coordinate) {
                        PulsingDot(color: index == 0 ? Theme.warmOrange : Theme.routeGold, size: 10)
                    }
                }

                ForEach(routePolylines.indices, id: \.self) { index in
                    MapPolyline(routePolylines[index])
                        .stroke(Theme.routeGold, lineWidth: 4)
                }
            }
            .mapStyle(colorScheme == .dark ? .standard(elevation: .flat, emphasis: .muted) : .standard)
            .overlay(alignment: .topTrailing) {
                // Save/Unsave heart button
                Button {
                    toggleSaved()
                } label: {
                    Image(systemName: isSaved ? "heart.fill" : "heart")
                        .font(.title3)
                        .foregroundStyle(isSaved ? .red : .white)
                        .padding(12)
                        .background(.ultraThinMaterial)
                        .clipShape(Circle())
                        .shadow(color: .black.opacity(0.2), radius: 4, y: 2)
                }
                .padding(.top, 12)
                .padding(.trailing, 12)
            }

            // Gradient overlay at bottom
            LinearGradient(
                colors: [.clear, Theme.cardBackground(colorScheme).opacity(0.8), Theme.cardBackground(colorScheme)],
                startPoint: .top,
                endPoint: .bottom
            )
            .frame(height: 80)

            // Loading indicator
            if isLoadingRoutes {
                LoadingSpinner(size: .medium)
                    .padding(.bottom, 40)
            }
        }
        .task {
            updateMapRegion()
            await fetchDrivingRoutes()
        }
    }

    // MARK: - Header

    private var tripHeader: some View {
        VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
            // Date range
            Text(trip.formattedDateRange)
                .font(.subheadline)
                .foregroundStyle(Theme.textSecondary(colorScheme))

            // Route preview
            RoutePreviewStrip(cities: trip.stops.map { $0.city })
                .padding(.vertical, Theme.Spacing.xs)

            // Sport badges
            HStack(spacing: Theme.Spacing.xs) {
                ForEach(Array(trip.uniqueSports), id: \.self) { sport in
                    HStack(spacing: 4) {
                        Image(systemName: sport.iconName)
                            .font(.caption2)
                        Text(sport.rawValue)
                            .font(.caption2)
                    }
                    .padding(.horizontal, 10)
                    .padding(.vertical, 5)
                    .background(sport.themeColor.opacity(0.2))
                    .foregroundStyle(sport.themeColor)
                    .clipShape(Capsule())
                }
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    // MARK: - Stats Row

    private var statsRow: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: Theme.Spacing.sm) {
                StatPill(icon: "calendar", value: "\(trip.tripDuration) days")
                StatPill(icon: "mappin.circle", value: "\(trip.stops.count) cities")
                StatPill(icon: "sportscourt", value: "\(trip.totalGames) games")
                StatPill(icon: "road.lanes", value: trip.formattedTotalDistance)
                StatPill(icon: "car", value: trip.formattedTotalDriving)
            }
        }
    }

    // MARK: - Score Card

    private func scoreCard(_ score: TripScore) -> some View {
        VStack(spacing: Theme.Spacing.md) {
            HStack {
                Text("Trip Score")
                    .font(.headline)
                    .foregroundStyle(Theme.textPrimary(colorScheme))
                Spacer()
                Text(score.scoreGrade)
                    .font(.largeTitle)
                    .foregroundStyle(Theme.warmOrange)
                    .glowEffect(color: Theme.warmOrange, radius: 8)
            }

            HStack(spacing: Theme.Spacing.lg) {
                scoreItem(label: "Games", value: score.gameQualityScore, color: Theme.mlbRed)
                scoreItem(label: "Route", value: score.routeEfficiencyScore, color: Theme.routeGold)
                scoreItem(label: "Balance", value: score.leisureBalanceScore, color: Theme.mlsGreen)
                scoreItem(label: "Prefs", value: score.preferenceAlignmentScore, color: Theme.nbaOrange)
            }
        }
        .cardStyle()
    }

    private func scoreItem(label: String, value: Double, color: Color) -> some View {
        VStack(spacing: 4) {
            Text(String(format: "%.0f", value))
                .font(.headline)
                .foregroundStyle(color)
            Text(label)
                .font(.caption)
                .foregroundStyle(Theme.textMuted(colorScheme))
        }
    }

    // MARK: - Itinerary

    private var itinerarySection: some View {
        VStack(alignment: .leading, spacing: Theme.Spacing.md) {
            Text("Itinerary")
                .font(.title2)
                .foregroundStyle(Theme.textPrimary(colorScheme))

            if isLoadingGames {
                HStack {
                    Spacer()
                    ProgressView("Loading games...")
                        .padding(.vertical, Theme.Spacing.xl)
                    Spacer()
                }
            } else {
                // ZStack with continuous vertical line behind all content
                ZStack(alignment: .top) {
                    // Continuous vertical line down the center
                    Rectangle()
                        .fill(Theme.routeGold.opacity(0.4))
                        .frame(width: 2)
                        .frame(maxHeight: .infinity)

                    // Itinerary content
                    VStack(spacing: Theme.Spacing.md) {
                        ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in
                            itineraryRow(for: section, at: index)
                        }
                    }
                }
            }
        }
    }

    @ViewBuilder
    private func itineraryRow(for section: ItinerarySection, at index: Int) -> some View {
        let sectionId = sectionIdentifier(for: section, at: index)
        let isDropTarget = dropTargetId == sectionId && draggedItem != nil

        switch section {
        case .day(let dayNumber, let date, let gamesOnDay):
            DaySection(
                dayNumber: dayNumber,
                date: date,
                games: gamesOnDay
            )
            .staggeredAnimation(index: index)
            .overlay(alignment: .bottom) {
                if isDropTarget {
                    DropTargetIndicator()
                }
            }
            .dropDestination(for: String.self) { items, _ in
                guard let itemIdString = items.first,
                      let itemId = UUID(uuidString: itemIdString),
                      let item = customItems.first(where: { $0.id == itemId }),
                      let lastGame = gamesOnDay.last else { return false }
                Task {
                    // Insert at beginning (sortOrder 0) when dropping on day card
                    await moveItemToBeginning(item, toDay: dayNumber, anchorType: .afterGame, anchorId: lastGame.game.id)
                }
                return true
            } isTargeted: { targeted in
                withAnimation(.easeInOut(duration: 0.2)) {
                    dropTargetId = targeted ? sectionId : (dropTargetId == sectionId ? nil : dropTargetId)
                }
            }

        case .travel(let segment):
            TravelSection(segment: segment)
                .staggeredAnimation(index: index)
                .overlay(alignment: .bottom) {
                    if isDropTarget {
                        DropTargetIndicator()
                    }
                }
                .dropDestination(for: String.self) { items, _ in
                    guard let itemIdString = items.first,
                          let itemId = UUID(uuidString: itemIdString),
                          let item = customItems.first(where: { $0.id == itemId }) else { return false }
                    // Find the day for this travel segment
                    let day = findDayForTravelSegment(segment)
                    // Use stable identifier instead of UUID (UUIDs change on reload)
                    let stableAnchorId = stableTravelAnchorId(segment)
                    Task {
                        // Insert at beginning when dropping on travel section
                        await moveItemToBeginning(item, toDay: day, anchorType: .afterTravel, anchorId: stableAnchorId)
                    }
                    return true
                } isTargeted: { targeted in
                    withAnimation(.easeInOut(duration: 0.2)) {
                        dropTargetId = targeted ? sectionId : (dropTargetId == sectionId ? nil : dropTargetId)
                    }
                }

        case .customItem(let item):
            let isDragging = draggedItem?.id == item.id
            CustomItemRow(
                item: item,
                onTap: { editingItem = item },
                onDelete: { Task { await deleteCustomItem(item) } }
            )
            .opacity(isDragging ? 0.4 : 1.0)
            .staggeredAnimation(index: index)
            .overlay(alignment: .top) {
                if isDropTarget && !isDragging {
                    DropTargetIndicator()
                }
            }
            .draggable(item.id.uuidString) {
                // Drag preview
                CustomItemRow(
                    item: item,
                    onTap: {},
                    onDelete: {}
                )
                .frame(width: 300)
                .opacity(0.9)
                .onAppear { draggedItem = item }
            }
            .dropDestination(for: String.self) { items, _ in
                guard let itemIdString = items.first,
                      let itemId = UUID(uuidString: itemIdString),
                      let droppedItem = customItems.first(where: { $0.id == itemId }),
                      droppedItem.id != item.id else { return false }
                Task {
                    await moveItem(droppedItem, toDay: item.anchorDay, anchorType: item.anchorType, anchorId: item.anchorId, beforeItem: item)
                }
                draggedItem = nil
                dropTargetId = nil
                return true
            } isTargeted: { targeted in
                withAnimation(.easeInOut(duration: 0.2)) {
                    dropTargetId = targeted ? sectionId : (dropTargetId == sectionId ? nil : dropTargetId)
                }
            }

        case .addButton(let day, let anchorType, let anchorId):
            VStack(spacing: 0) {
                if isDropTarget {
                    DropTargetIndicator()
                }
                InlineAddButton {
                    addItemAnchor = AddItemAnchor(day: day, type: anchorType, anchorId: anchorId)
                }
            }
            .dropDestination(for: String.self) { items, _ in
                guard let itemIdString = items.first,
                      let itemId = UUID(uuidString: itemIdString),
                      let item = customItems.first(where: { $0.id == itemId }) else { return false }
                Task {
                    // Insert at beginning when dropping on add button area
                    await moveItemToBeginning(item, toDay: day, anchorType: anchorType, anchorId: anchorId)
                }
                draggedItem = nil
                dropTargetId = nil
                return true
            } isTargeted: { targeted in
                withAnimation(.easeInOut(duration: 0.2)) {
                    dropTargetId = targeted ? sectionId : (dropTargetId == sectionId ? nil : dropTargetId)
                }
            }
        }
    }

    /// Create a stable identifier for an itinerary section (for drop target tracking)
    private func sectionIdentifier(for section: ItinerarySection, at index: Int) -> String {
        switch section {
        case .day(let dayNumber, _, _):
            return "day-\(dayNumber)"
        case .travel(let segment):
            return "travel-\(segment.fromLocation.name)-\(segment.toLocation.name)"
        case .customItem(let item):
            return "item-\(item.id.uuidString)"
        case .addButton(let day, let anchorType, _):
            return "add-\(day)-\(anchorType.rawValue)"
        }
    }

    private func findDayForTravelSegment(_ segment: TravelSegment) -> Int {
        // Find which day this travel segment belongs to by looking at sections
        // Travel appears BEFORE the arrival day, so look FORWARD to find arrival day
        for (index, section) in itinerarySections.enumerated() {
            if case .travel(let s) = section, s.id == segment.id {
                // Look forward to find the arrival day
                for i in (index + 1)..<itinerarySections.count {
                    if case .day(let dayNumber, _, _) = itinerarySections[i] {
                        return dayNumber
                    }
                }
            }
        }
        return 1
    }

    /// Create a stable anchor ID for a travel segment (UUIDs regenerate on reload)
    private func stableTravelAnchorId(_ segment: TravelSegment) -> String {
        let from = segment.fromLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
        let to = segment.toLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
        return "travel:\(from)->\(to)"
    }

    private func moveItem(_ item: CustomItineraryItem, toDay day: Int, anchorType: CustomItineraryItem.AnchorType, anchorId: String?, beforeItem: CustomItineraryItem? = nil) async {
        var updated = item
        updated.anchorDay = day
        updated.anchorType = anchorType
        updated.anchorId = anchorId
        updated.modifiedAt = Date()

        // Calculate sortOrder
        let itemsAtSameAnchor = customItems.filter {
            $0.anchorDay == day &&
            $0.anchorType == anchorType &&
            $0.anchorId == anchorId &&
            $0.id != item.id
        }.sorted { $0.sortOrder < $1.sortOrder }

        var itemsToSync: [CustomItineraryItem] = []

        print("📍 [Move] itemsAtSameAnchor: \(itemsAtSameAnchor.map { "\($0.title) (id: \($0.id.uuidString.prefix(8)))" })")
        if let beforeItem = beforeItem {
            print("📍 [Move] beforeItem: \(beforeItem.title) (id: \(beforeItem.id.uuidString.prefix(8)))")
        }

        if let beforeItem = beforeItem,
           let beforeIndex = itemsAtSameAnchor.firstIndex(where: { $0.id == beforeItem.id }) {
            updated.sortOrder = beforeIndex
            print("📍 [Move] Setting \(item.title) sortOrder to \(beforeIndex) (before \(beforeItem.title))")

            // Shift other items and track them for syncing
            for i in beforeIndex..<itemsAtSameAnchor.count {
                if var shiftItem = customItems.first(where: { $0.id == itemsAtSameAnchor[i].id }) {
                    shiftItem.sortOrder = i + 1
                    shiftItem.modifiedAt = Date()
                    print("📍 [Move] Shifting \(shiftItem.title) sortOrder to \(i + 1)")
                    if let idx = customItems.firstIndex(where: { $0.id == shiftItem.id }) {
                        customItems[idx] = shiftItem
                    }
                    itemsToSync.append(shiftItem)
                }
            }
        } else {
            updated.sortOrder = itemsAtSameAnchor.count
            print("📍 [Move] Setting \(item.title) sortOrder to \(itemsAtSameAnchor.count) (end of list)")
        }

        // Update local state
        if let idx = customItems.firstIndex(where: { $0.id == item.id }) {
            customItems[idx] = updated
        }

        // Sync moved item and all shifted items to CloudKit
        do {
            _ = try await CustomItemService.shared.updateItem(updated)
            print("✅ [Move] Synced \(updated.title) to day \(day), anchor: \(anchorType.rawValue), sortOrder: \(updated.sortOrder)")

            // Also sync shifted items
            for shiftItem in itemsToSync {
                _ = try await CustomItemService.shared.updateItem(shiftItem)
                print("✅ [Move] Synced shifted item \(shiftItem.title) with sortOrder: \(shiftItem.sortOrder)")
            }
        } catch {
            print("❌ [Move] Failed to sync: \(error)")
        }
    }

    /// Move item to the beginning of an anchor position (sortOrder 0), shifting existing items down
    private func moveItemToBeginning(_ item: CustomItineraryItem, toDay day: Int, anchorType: CustomItineraryItem.AnchorType, anchorId: String?) async {
        var updated = item
        updated.anchorDay = day
        updated.anchorType = anchorType
        updated.anchorId = anchorId
        updated.sortOrder = 0  // Insert at beginning
        updated.modifiedAt = Date()

        // Get existing items at this anchor position (excluding the moved item)
        let existingItems = customItems.filter {
            $0.anchorDay == day &&
            $0.anchorType == anchorType &&
            $0.anchorId == anchorId &&
            $0.id != item.id
        }.sorted { $0.sortOrder < $1.sortOrder }

        print("📍 [MoveToBeginning] Moving \(item.title) to beginning of day \(day), anchor: \(anchorType.rawValue)")
        print("📍 [MoveToBeginning] Existing items to shift: \(existingItems.map { "\($0.title) (order: \($0.sortOrder))" })")

        // Shift all existing items down by 1
        var itemsToSync: [CustomItineraryItem] = []
        for (index, existingItem) in existingItems.enumerated() {
            if var shiftItem = customItems.first(where: { $0.id == existingItem.id }) {
                shiftItem.sortOrder = index + 1  // Shift down
                shiftItem.modifiedAt = Date()
                print("📍 [MoveToBeginning] Shifting \(shiftItem.title) sortOrder to \(index + 1)")
                if let idx = customItems.firstIndex(where: { $0.id == shiftItem.id }) {
                    customItems[idx] = shiftItem
                }
                itemsToSync.append(shiftItem)
            }
        }

        // Update local state for the moved item
        if let idx = customItems.firstIndex(where: { $0.id == item.id }) {
            customItems[idx] = updated
        }

        // Sync to CloudKit
        do {
            _ = try await CustomItemService.shared.updateItem(updated)
            print("✅ [MoveToBeginning] Synced \(updated.title) with sortOrder: 0")

            for shiftItem in itemsToSync {
                _ = try await CustomItemService.shared.updateItem(shiftItem)
                print("✅ [MoveToBeginning] Synced shifted item \(shiftItem.title) with sortOrder: \(shiftItem.sortOrder)")
            }
        } catch {
            print("❌ [MoveToBeginning] Failed to sync: \(error)")
        }
    }

    /// Build itinerary sections: group by day AND city, with travel, custom items, and add buttons
    private var itinerarySections: [ItinerarySection] {
        var sections: [ItinerarySection] = []

        // Build day+city sections for days with games
        var dayCitySections: [(dayNumber: Int, date: Date, city: String, games: [RichGame])] = []
        let days = tripDays

        for (index, dayDate) in days.enumerated() {
            let dayNum = index + 1
            let gamesOnDay = gamesOn(date: dayDate)

            guard !gamesOnDay.isEmpty else { continue }

            // Group games by city, maintaining chronological order
            var gamesByCity: [(city: String, games: [RichGame])] = []
            for game in gamesOnDay {
                let city = game.stadium.city
                if let lastIndex = gamesByCity.indices.last, gamesByCity[lastIndex].city == city {
                    // Same city as previous game - add to existing group
                    gamesByCity[lastIndex].games.append(game)
                } else {
                    // Different city - start new group
                    gamesByCity.append((city, [game]))
                }
            }

            // Add each city group as a separate section
            for cityGroup in gamesByCity {
                dayCitySections.append((dayNum, dayDate, cityGroup.city, cityGroup.games))
            }
        }

        // Build sections: insert travel, custom items (only if allowed), and add buttons
        for (index, section) in dayCitySections.enumerated() {

            // Check if we need travel BEFORE this section (coming from different city)
            if index > 0 {
                let prevSection = dayCitySections[index - 1]
                let prevCity = prevSection.city
                let currentCity = section.city

                // If cities differ, find travel segment from prev -> current
                if !prevCity.isEmpty && !currentCity.isEmpty && prevCity != currentCity {
                    if let travelSegment = findTravelSegment(from: prevCity, to: currentCity) {
                        sections.append(.travel(travelSegment))

                        if allowCustomItems {
                            // Use stable anchor ID for travel segments
                            let stableId = stableTravelAnchorId(travelSegment)

                            // Add button after travel
                            sections.append(.addButton(day: section.dayNumber, anchorType: .afterTravel, anchorId: stableId))

                            // Custom items after this travel (sorted by sortOrder)
                            let itemsAfterTravel = customItems.filter {
                                $0.anchorDay == section.dayNumber &&
                                $0.anchorType == .afterTravel &&
                                $0.anchorId == stableId
                            }.sorted { $0.sortOrder < $1.sortOrder }
                            for item in itemsAfterTravel {
                                sections.append(.customItem(item))
                            }
                        }
                    }
                }
            }

            if allowCustomItems {
                // Custom items at start of day (sorted by sortOrder)
                let itemsAtStart = customItems.filter {
                    $0.anchorDay == section.dayNumber && $0.anchorType == .startOfDay
                }.sorted { $0.sortOrder < $1.sortOrder }
                for item in itemsAtStart {
                    sections.append(.customItem(item))
                }
            }

            // Add the day section
            sections.append(.day(dayNumber: section.dayNumber, date: section.date, games: section.games))

            if allowCustomItems {
                // Add button after day's games
                if let lastGame = section.games.last {
                    sections.append(.addButton(day: section.dayNumber, anchorType: .afterGame, anchorId: lastGame.game.id))

                    // Custom items after this game (sorted by sortOrder)
                    let itemsAfterGame = customItems.filter {
                        $0.anchorDay == section.dayNumber &&
                        $0.anchorType == .afterGame &&
                        $0.anchorId == lastGame.game.id
                    }.sorted { $0.sortOrder < $1.sortOrder }
                    for item in itemsAfterGame {
                        sections.append(.customItem(item))
                    }
                }
            }
        }

        return sections
    }

    private var tripDays: [Date] {
        let calendar = Calendar.current
        guard let startDate = trip.stops.first?.arrivalDate,
              let endDate = trip.stops.last?.departureDate else { return [] }

        var days: [Date] = []
        var current = calendar.startOfDay(for: startDate)
        let end = calendar.startOfDay(for: endDate)

        while current <= end {
            days.append(current)
            current = calendar.date(byAdding: .day, value: 1, to: current)!
        }
        return days
    }

    private func gamesOn(date: Date) -> [RichGame] {
        let calendar = Calendar.current
        let dayStart = calendar.startOfDay(for: date)
        let allGameIds = trip.stops.flatMap { $0.games }

        let foundGames = allGameIds.compactMap { games[$0] }

        return foundGames.filter { richGame in
            calendar.startOfDay(for: richGame.game.dateTime) == dayStart
        }.sorted { $0.game.dateTime < $1.game.dateTime }
    }

    /// Get the city for a given date (from the stop that covers that date)
    private func cityOn(date: Date) -> String? {
        let calendar = Calendar.current
        let dayStart = calendar.startOfDay(for: date)

        return trip.stops.first { stop in
            let arrivalDay = calendar.startOfDay(for: stop.arrivalDate)
            let departureDay = calendar.startOfDay(for: stop.departureDate)
            return dayStart >= arrivalDay && dayStart <= departureDay
        }?.city
    }

    /// Find travel segment that goes from one city to another
    private func findTravelSegment(from fromCity: String, to toCity: String) -> TravelSegment? {
        let fromLower = fromCity.lowercased().trimmingCharacters(in: .whitespaces)
        let toLower = toCity.lowercased().trimmingCharacters(in: .whitespaces)

        return trip.travelSegments.first { segment in
            let segmentFrom = segment.fromLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
            let segmentTo = segment.toLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
            return segmentFrom == fromLower && segmentTo == toLower
        }
    }

    // MARK: - Map Helpers

    private func fetchDrivingRoutes() async {
        let stops = stopCoordinates
        guard stops.count >= 2 else { return }

        isLoadingRoutes = true
        var polylines: [MKPolyline] = []

        for i in 0..<(stops.count - 1) {
            let source = stops[i]
            let destination = stops[i + 1]

            let request = MKDirections.Request()
            let sourceLocation = CLLocation(latitude: source.coordinate.latitude, longitude: source.coordinate.longitude)
            let destLocation = CLLocation(latitude: destination.coordinate.latitude, longitude: destination.coordinate.longitude)
            request.source = MKMapItem(location: sourceLocation, address: nil)
            request.destination = MKMapItem(location: destLocation, address: nil)
            request.transportType = .automobile

            let directions = MKDirections(request: request)

            do {
                let response = try await directions.calculate()
                if let route = response.routes.first {
                    polylines.append(route.polyline)
                }
            } catch {
                let straightLine = MKPolyline(coordinates: [source.coordinate, destination.coordinate], count: 2)
                polylines.append(straightLine)
            }
        }

        routePolylines = polylines
        isLoadingRoutes = false
    }

    private var stopCoordinates: [(name: String, coordinate: CLLocationCoordinate2D)] {
        trip.stops.compactMap { stop -> (String, CLLocationCoordinate2D)? in
            if let coord = stop.coordinate {
                return (stop.city, coord)
            }
            if let stadiumId = stop.stadium,
               let stadium = dataProvider.stadium(for: stadiumId) {
                return (stadium.name, stadium.coordinate)
            }
            return nil
        }
    }

    private func updateMapRegion() {
        guard !stopCoordinates.isEmpty else { return }

        let coordinates = stopCoordinates.map(\.coordinate)
        let lats = coordinates.map(\.latitude)
        let lons = coordinates.map(\.longitude)

        guard let minLat = lats.min(),
              let maxLat = lats.max(),
              let minLon = lons.min(),
              let maxLon = lons.max() else { return }

        let center = CLLocationCoordinate2D(
            latitude: (minLat + maxLat) / 2,
            longitude: (minLon + maxLon) / 2
        )

        let latSpan = (maxLat - minLat) * 1.3 + 0.5
        let lonSpan = (maxLon - minLon) * 1.3 + 0.5

        mapCameraPosition = .region(MKCoordinateRegion(
            center: center,
            span: MKCoordinateSpan(latitudeDelta: max(latSpan, 1), longitudeDelta: max(lonSpan, 1))
        ))
    }

    // MARK: - Actions

    /// Load games from AppDataProvider if not provided
    private func loadGamesIfNeeded() async {
        // Skip if games were provided
        guard providedGames == nil else { return }

        // Collect all game IDs from the trip
        let gameIds = trip.stops.flatMap { $0.games }
        guard !gameIds.isEmpty else { return }

        isLoadingGames = true

        // Load RichGame data from AppDataProvider
        var loaded: [String: RichGame] = [:]
        for gameId in gameIds {
            do {
                if let game = try await dataProvider.fetchGame(by: gameId),
                   let richGame = dataProvider.richGame(from: game) {
                    loaded[gameId] = richGame
                }
            } catch {
                // Skip games that fail to load
            }
        }

        loadedGames = loaded
        isLoadingGames = false
    }

    private func exportPDF() async {
        isExporting = true
        exportProgress = nil

        do {
            let url = try await exportService.exportToPDF(trip: trip, games: games) { progress in
                await MainActor.run {
                    self.exportProgress = progress
                }
            }
            exportURL = url
            showExportSheet = true
        } catch {
            // PDF export failed silently
        }

        isExporting = false
    }

    private func toggleSaved() {
        if isSaved {
            unsaveTrip()
        } else {
            saveTrip()
        }
    }

    private func saveTrip() {
        // Check trip limit for free users
        if !StoreManager.shared.isPro && savedTrips.count >= StoreManager.freeTripLimit {
            showProPaywall = true
            return
        }

        guard let savedTrip = SavedTrip.from(trip, games: games, status: .planned) else {
            return
        }

        modelContext.insert(savedTrip)

        do {
            try modelContext.save()
            withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
                isSaved = true
            }
        } catch {
            // Save failed silently
        }
    }

    private func unsaveTrip() {
        let tripId = trip.id
        let descriptor = FetchDescriptor<SavedTrip>(
            predicate: #Predicate { $0.id == tripId }
        )

        do {
            let savedTrips = try modelContext.fetch(descriptor)
            for savedTrip in savedTrips {
                modelContext.delete(savedTrip)
            }
            try modelContext.save()
            withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
                isSaved = false
            }
        } catch {
            // Unsave failed silently
        }
    }

    private func checkIfSaved() {
        let tripId = trip.id
        let descriptor = FetchDescriptor<SavedTrip>(
            predicate: #Predicate { $0.id == tripId }
        )

        if let count = try? modelContext.fetchCount(descriptor), count > 0 {
            isSaved = true
        }
    }

    // MARK: - Custom Items (CloudKit persistence)

    private func setupSubscription() async {
        // Subscribe to real-time updates for this trip
        do {
            try await CustomItemSubscriptionService.shared.subscribeToTrip(trip.id)

            // Listen for changes and reload
            subscriptionCancellable = await CustomItemSubscriptionService.shared.changePublisher
                .filter { $0 == self.trip.id }
                .receive(on: DispatchQueue.main)
                .sink { [self] _ in
                    print("📡 [Subscription] Received update, reloading custom items...")
                    Task {
                        await loadCustomItems()
                    }
                }
        } catch {
            print("📡 [Subscription] Failed to subscribe: \(error)")
        }
    }

    private func loadCustomItems() async {
        print("🔍 [CustomItems] Loading items for trip: \(trip.id)")
        do {
            let items = try await CustomItemService.shared.fetchItems(forTripId: trip.id)
            print("✅ [CustomItems] Loaded \(items.count) items from CloudKit")
            for item in items {
                print("   - \(item.title) (day \(item.anchorDay), anchor: \(item.anchorType.rawValue), sortOrder: \(item.sortOrder))")
            }
            customItems = items
        } catch {
            print("❌ [CustomItems] Failed to load: \(error)")
        }
    }

    private func saveCustomItem(_ item: CustomItineraryItem) async {
        // Check if this is an update or create
        let isUpdate = customItems.contains(where: { $0.id == item.id })
        print("💾 [CustomItems] Saving item: '\(item.title)' (isUpdate: \(isUpdate))")
        print("   - tripId: \(item.tripId)")
        print("   - anchorDay: \(item.anchorDay), anchorType: \(item.anchorType.rawValue)")

        // Update local state immediately for responsive UI
        if isUpdate {
            if let index = customItems.firstIndex(where: { $0.id == item.id }) {
                customItems[index] = item
            }
        } else {
            customItems.append(item)
        }

        // Persist to CloudKit
        do {
            if isUpdate {
                let updated = try await CustomItemService.shared.updateItem(item)
                print("✅ [CustomItems] Updated in CloudKit: \(updated.title)")
            } else {
                let created = try await CustomItemService.shared.createItem(item)
                print("✅ [CustomItems] Created in CloudKit: \(created.title)")
            }
        } catch {
            print("❌ [CustomItems] CloudKit save failed: \(error)")
        }
    }

    private func deleteCustomItem(_ item: CustomItineraryItem) async {
        print("🗑️ [CustomItems] Deleting item: '\(item.title)'")

        // Remove from local state immediately
        customItems.removeAll { $0.id == item.id }

        // Delete from CloudKit
        do {
            try await CustomItemService.shared.deleteItem(item.id)
            print("✅ [CustomItems] Deleted from CloudKit")
        } catch {
            print("❌ [CustomItems] CloudKit delete failed: \(error)")
        }
    }
}

// MARK: - Itinerary Section

enum ItinerarySection {
    case day(dayNumber: Int, date: Date, games: [RichGame])
    case travel(TravelSegment)
    case customItem(CustomItineraryItem)
    case addButton(day: Int, anchorType: CustomItineraryItem.AnchorType, anchorId: String?)

    var isCustomItem: Bool {
        if case .customItem = self { return true }
        return false
    }
}

// MARK: - Add Item Anchor (for sheet)

struct AddItemAnchor: Identifiable {
    let id = UUID()
    let day: Int
    let type: CustomItineraryItem.AnchorType
    let anchorId: String?
}

// MARK: - Inline Add Button

private struct InlineAddButton: View {
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            HStack {
                Image(systemName: "plus.circle.fill")
                    .foregroundStyle(Theme.warmOrange.opacity(0.6))
                Text("Add")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
            .padding(.vertical, 4)
            .padding(.horizontal, 8)
        }
        .buttonStyle(.plain)
        .frame(maxWidth: .infinity, alignment: .leading)
    }
}

// MARK: - Drop Target Indicator

private struct DropTargetIndicator: View {
    var body: some View {
        HStack(spacing: 6) {
            Circle()
                .fill(Theme.warmOrange)
                .frame(width: 8, height: 8)
            Rectangle()
                .fill(Theme.warmOrange)
                .frame(height: 2)
            Circle()
                .fill(Theme.warmOrange)
                .frame(width: 8, height: 8)
        }
        .padding(.horizontal, Theme.Spacing.md)
        .padding(.vertical, 4)
        .transition(.opacity.combined(with: .scale(scale: 0.8)))
    }
}

// MARK: - Day Section

struct DaySection: View {
    let dayNumber: Int
    let date: Date
    let games: [RichGame]
    @Environment(\.colorScheme) private var colorScheme

    private var formattedDate: String {
        date.formatted(.dateTime.weekday(.wide).month().day())
    }

    private var gameCity: String? {
        games.first?.stadium.city
    }

    private var isRestDay: Bool {
        games.isEmpty
    }

    var body: some View {
        VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
            // Day header
            HStack {
                VStack(alignment: .leading, spacing: 2) {
                    Text("Day \(dayNumber)")
                        .font(.title2)
                        .foregroundStyle(Theme.textPrimary(colorScheme))

                    Text(formattedDate)
                        .font(.subheadline)
                        .foregroundStyle(Theme.textSecondary(colorScheme))
                }

                Spacer()

                if isRestDay {
                    Text("Rest Day")
                        .badgeStyle(color: Theme.mlsGreen, filled: false)
                } else if !games.isEmpty {
                    Text("\(games.count) game\(games.count > 1 ? "s" : "")")
                        .badgeStyle(color: Theme.warmOrange, filled: false)
                }
            }

            // City label
            if let city = gameCity {
                Label(city, systemImage: "mappin")
                    .font(.subheadline)
                    .foregroundStyle(Theme.textSecondary(colorScheme))
            }

            // Games
            ForEach(games, id: \.game.id) { richGame in
                GameRow(game: richGame)
            }
        }
        .cardStyle()
    }
}

// MARK: - Game Row

struct GameRow: View {
    let game: RichGame
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        HStack(spacing: Theme.Spacing.md) {
            // Sport color bar
            SportColorBar(sport: game.game.sport)

            VStack(alignment: .leading, spacing: 4) {
                // Sport badge + Matchup
                HStack(spacing: 6) {
                    // Sport icon and name
                    HStack(spacing: 3) {
                        Image(systemName: game.game.sport.iconName)
                            .font(.caption2)
                        Text(game.game.sport.rawValue)
                            .font(.caption2)
                    }
                    .foregroundStyle(game.game.sport.themeColor)

                    // Matchup
                    HStack(spacing: 4) {
                        Text(game.awayTeam.abbreviation)
                            .font(.body)
                        Text("@")
                            .foregroundStyle(Theme.textMuted(colorScheme))
                        Text(game.homeTeam.abbreviation)
                            .font(.body)
                    }
                    .foregroundStyle(Theme.textPrimary(colorScheme))
                }

                // Stadium
                HStack(spacing: 4) {
                    Image(systemName: "building.2")
                        .font(.caption2)
                    Text(game.stadium.name)
                        .font(.subheadline)
                }
                .foregroundStyle(Theme.textSecondary(colorScheme))
            }

            Spacer()

            // Time
            Text(game.localGameTimeShort)
                .font(.subheadline)
                .foregroundStyle(Theme.warmOrange)
        }
        .padding(Theme.Spacing.sm)
        .background(Theme.cardBackgroundElevated(colorScheme))
        .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.small))
    }
}

// MARK: - Travel Section

struct TravelSection: View {
    let segment: TravelSegment
    @Environment(\.colorScheme) private var colorScheme
    @State private var showEVChargers = false

    private var hasEVChargers: Bool {
        !segment.evChargingStops.isEmpty
    }

    var body: some View {
        // Travel card
        VStack(spacing: 0) {
                // Main travel info
                HStack(spacing: Theme.Spacing.md) {
                    // Icon
                    ZStack {
                        Circle()
                            .fill(Theme.cardBackgroundElevated(colorScheme))
                            .frame(width: 44, height: 44)

                        Image(systemName: "car.fill")
                            .foregroundStyle(Theme.routeGold)
                    }

                    VStack(alignment: .leading, spacing: 2) {
                        Text("Travel")
                            .font(.caption)
                            .foregroundStyle(Theme.textMuted(colorScheme))

                        Text("\(segment.fromLocation.name) → \(segment.toLocation.name)")
                            .font(.body)
                            .foregroundStyle(Theme.textPrimary(colorScheme))
                    }

                    Spacer()

                    VStack(alignment: .trailing, spacing: 2) {
                        Text(segment.formattedDistance)
                            .font(.subheadline)
                            .foregroundStyle(Theme.textPrimary(colorScheme))
                        Text(segment.formattedDuration)
                            .font(.caption)
                            .foregroundStyle(Theme.textSecondary(colorScheme))
                    }
                }
                .padding(Theme.Spacing.md)

                // EV Chargers section (if available)
                if hasEVChargers {
                    Divider()
                        .background(Theme.routeGold.opacity(0.2))

                    Button {
                        withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                            showEVChargers.toggle()
                        }
                    } label: {
                        HStack(spacing: Theme.Spacing.sm) {
                            Image(systemName: "bolt.fill")
                                .foregroundStyle(.green)
                                .font(.caption)

                            Text("\(segment.evChargingStops.count) EV Charger\(segment.evChargingStops.count > 1 ? "s" : "") Along Route")
                                .font(.subheadline)
                                .foregroundStyle(Theme.textPrimary(colorScheme))

                            Spacer()

                            Image(systemName: showEVChargers ? "chevron.up" : "chevron.down")
                                .font(.caption)
                                .foregroundStyle(Theme.textMuted(colorScheme))
                        }
                        .padding(.horizontal, Theme.Spacing.md)
                        .padding(.vertical, Theme.Spacing.sm)
                        .contentShape(Rectangle())
                    }
                    .buttonStyle(.plain)

                    if showEVChargers {
                        VStack(spacing: 0) {
                            ForEach(segment.evChargingStops) { charger in
                                EVChargerRow(charger: charger)
                            }
                        }
                        .padding(.horizontal, Theme.Spacing.md)
                        .padding(.bottom, Theme.Spacing.sm)
                        .transition(.opacity.combined(with: .move(edge: .top)))
                    }
                }
            }
            .background {
                // Opaque base + semi-transparent accent (prevents line showing through)
                RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)
                    .fill(Theme.cardBackground(colorScheme))
                RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)
                    .fill(Theme.routeGold.opacity(0.05))
            }
            .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium))
            .overlay {
                RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)
                    .strokeBorder(Theme.routeGold.opacity(0.3), lineWidth: 1)
            }
    }
}

// MARK: - EV Charger Row

struct EVChargerRow: View {
    let charger: EVChargingStop
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        HStack(spacing: Theme.Spacing.sm) {
            // Connector line indicator
            VStack(spacing: 0) {
                Rectangle()
                    .fill(.green.opacity(0.3))
                    .frame(width: 1, height: 8)
                Circle()
                    .fill(.green)
                    .frame(width: 6, height: 6)
                Rectangle()
                    .fill(.green.opacity(0.3))
                    .frame(width: 1, height: 8)
            }

            VStack(alignment: .leading, spacing: 2) {
                HStack(spacing: Theme.Spacing.xs) {
                    Text(charger.name)
                        .font(.subheadline)
                        .foregroundStyle(Theme.textPrimary(colorScheme))
                        .lineLimit(1)

                    chargerTypeBadge
                }

                HStack(spacing: Theme.Spacing.xs) {
                    if let address = charger.location.address {
                        Text(address)
                            .font(.caption)
                            .foregroundStyle(Theme.textSecondary(colorScheme))
                    }

                    Text("•")
                        .foregroundStyle(Theme.textMuted(colorScheme))

                    Text("~\(charger.formattedChargeTime) charge")
                        .font(.caption)
                        .foregroundStyle(Theme.textSecondary(colorScheme))
                }
            }

            Spacer()
        }
        .padding(.vertical, 6)
    }

    @ViewBuilder
    private var chargerTypeBadge: some View {
        let (text, color) = chargerTypeInfo
        Text(text)
            .font(.caption2)
            .foregroundStyle(color)
            .padding(.horizontal, 6)
            .padding(.vertical, 2)
            .background(color.opacity(0.15))
            .clipShape(Capsule())
    }

    private var chargerTypeInfo: (String, Color) {
        switch charger.chargerType {
        case .supercharger:
            return ("Supercharger", .red)
        case .dcFast:
            return ("DC Fast", .blue)
        case .level2:
            return ("Level 2", .green)
        }
    }
}

// MARK: - Share Sheet

struct ShareSheet: UIViewControllerRepresentable {
    let items: [Any]

    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: items, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

#Preview {
    NavigationStack {
        TripDetailView(
            trip: Trip(
                name: "MLB Road Trip",
                preferences: TripPreferences(
                    startLocation: LocationInput(name: "New York"),
                    endLocation: LocationInput(name: "Chicago")
                )
            )
        )
    }
}
