import Foundation
import CloudKit

/// Service for persisting and syncing ItineraryItems to CloudKit
actor ItineraryItemService {
    static let shared = ItineraryItemService()

    private let container = CloudKitContainerConfig.makeContainer()
    private var database: CKDatabase { container.privateCloudDatabase }

    private let recordType = "ItineraryItem"

    // Debounce tracking
    private var pendingUpdates: [UUID: ItineraryItem] = [:]
    private var retryCount: [UUID: Int] = [:]
    private var debounceTask: Task<Void, Never>?

    private let maxRetries = 3
    private let debounceInterval: Duration = .seconds(1.5)

    private init() {}

    /// Cancel any pending debounce task (for cleanup)
    func cancelPendingSync() {
        debounceTask?.cancel()
        debounceTask = nil
    }

    // MARK: - CRUD Operations

    /// Fetch all items for a trip
    func fetchItems(forTripId tripId: UUID) async throws -> [ItineraryItem] {
        let predicate = NSPredicate(format: "tripId == %@", tripId.uuidString)
        let query = CKQuery(recordType: recordType, predicate: predicate)

        let (results, _) = try await database.records(matching: query)

        return results.compactMap { _, result in
            guard case .success(let record) = result else { return nil }
            return ItineraryItem(from: record)
        }
    }

    /// Create a new item
    func createItem(_ item: ItineraryItem) async throws -> ItineraryItem {
        let record = item.toCKRecord()
        let savedRecord = try await database.save(record)
        return ItineraryItem(from: savedRecord) ?? item
    }

    /// Update an existing item (debounced)
    func updateItem(_ item: ItineraryItem) async {
        pendingUpdates[item.id] = item

        // Cancel existing debounce task
        debounceTask?.cancel()

        // Start new debounce task with proper cancellation handling
        debounceTask = Task { [weak self] in
            // Check cancellation before sleeping
            guard !Task.isCancelled else { return }

            do {
                try await Task.sleep(for: self?.debounceInterval ?? .seconds(1.5))
            } catch {
                // Task was cancelled during sleep
                return
            }

            // Check cancellation after sleeping (belt and suspenders)
            guard !Task.isCancelled else { return }

            await self?.flushPendingUpdates()
        }
    }

    /// Force immediate sync of pending updates
    func flushPendingUpdates() async {
        let updates = pendingUpdates
        pendingUpdates.removeAll()

        guard !updates.isEmpty else { return }

        let records = updates.values.map { $0.toCKRecord() }

        do {
            // Use .changedKeys save policy - CloudKit handles conflicts automatically
            _ = try await database.modifyRecords(saving: records, deleting: [], savePolicy: .changedKeys)

            // Success - clear retry counts for saved items
            for item in updates.values {
                retryCount[item.id] = nil
            }
        } catch {
            // Add back to pending for retry, respecting max retry limit
            for item in updates.values {
                let currentRetries = retryCount[item.id] ?? 0
                if currentRetries < maxRetries {
                    pendingUpdates[item.id] = item
                    retryCount[item.id] = currentRetries + 1
                }
                // If max retries exceeded, silently drop the update to prevent infinite loop
            }
        }
    }

    /// Delete an item
    func deleteItem(_ itemId: UUID) async throws {
        let recordId = CKRecord.ID(recordName: itemId.uuidString)
        try await database.deleteRecord(withID: recordId)
    }

    /// Delete all items for a trip
    func deleteItems(forTripId tripId: UUID) async throws {
        let items = try await fetchItems(forTripId: tripId)

        for item in items {
            let recordId = CKRecord.ID(recordName: item.id.uuidString)
            try? await database.deleteRecord(withID: recordId)
        }
    }
}

// MARK: - CloudKit Conversion

extension ItineraryItem {
    init?(from record: CKRecord) {
        guard let idString = record["itemId"] as? String,
              let id = UUID(uuidString: idString),
              let tripIdString = record["tripId"] as? String,
              let tripId = UUID(uuidString: tripIdString),
              let day = record["day"] as? Int,
              let sortOrder = record["sortOrder"] as? Double,
              let kindString = record["kind"] as? String,
              let modifiedAt = record["modifiedAt"] as? Date else {
            return nil
        }

        self.id = id
        self.tripId = tripId
        self.day = day
        self.sortOrder = sortOrder
        self.modifiedAt = modifiedAt

        // Parse kind
        switch kindString {
        case "game":
            guard let gameId = record["gameId"] as? String,
                  let gameCity = record["gameCity"] as? String else { return nil }
            self.kind = .game(gameId: gameId, city: gameCity)

        case "travel":
            guard let fromCity = record["travelFromCity"] as? String,
                  let toCity = record["travelToCity"] as? String else { return nil }
            let info = TravelInfo(
                fromCity: fromCity,
                toCity: toCity,
                segmentIndex: record["travelSegmentIndex"] as? Int,
                distanceMeters: record["travelDistanceMeters"] as? Double,
                durationSeconds: record["travelDurationSeconds"] as? Double
            )
            self.kind = .travel(info)

        case "custom":
            guard let title = record["customTitle"] as? String,
                  let icon = record["customIcon"] as? String else { return nil }
            let info = CustomInfo(
                title: title,
                icon: icon,
                time: record["customTime"] as? Date,
                latitude: record["latitude"] as? Double,
                longitude: record["longitude"] as? Double,
                address: record["address"] as? String
            )
            self.kind = .custom(info)

        default:
            return nil
        }
    }

    func toCKRecord() -> CKRecord {
        let recordId = CKRecord.ID(recordName: id.uuidString)
        let record = CKRecord(recordType: "ItineraryItem", recordID: recordId)

        record["itemId"] = id.uuidString
        record["tripId"] = tripId.uuidString
        record["day"] = day
        record["sortOrder"] = sortOrder
        record["modifiedAt"] = modifiedAt

        switch kind {
        case .game(let gameId, let city):
            record["kind"] = "game"
            record["gameId"] = gameId
            record["gameCity"] = city

        case .travel(let info):
            record["kind"] = "travel"
            record["travelFromCity"] = info.fromCity
            record["travelToCity"] = info.toCity
            record["travelSegmentIndex"] = info.segmentIndex
            record["travelDistanceMeters"] = info.distanceMeters
            record["travelDurationSeconds"] = info.durationSeconds

        case .custom(let info):
            record["kind"] = "custom"
            record["customTitle"] = info.title
            record["customIcon"] = info.icon
            record["customTime"] = info.time
            record["latitude"] = info.latitude
            record["longitude"] = info.longitude
            record["address"] = info.address
        }

        return record
    }
}
