//
//  CustomItemService.swift
//  SportsTime
//
//  CloudKit service for custom itinerary items
//

import Foundation
import CloudKit

actor CustomItemService {
    static let shared = CustomItemService()

    private let container: CKContainer
    private let publicDatabase: CKDatabase

    private init() {
        self.container = CKContainer(identifier: "iCloud.com.sportstime.app")
        self.publicDatabase = container.publicCloudDatabase
    }

    // MARK: - CRUD Operations

    func createItem(_ item: CustomItineraryItem) async throws -> CustomItineraryItem {
        print("☁️ [CloudKit] Creating record for item: \(item.id)")
        let ckItem = CKCustomItineraryItem(item: item)

        do {
            let savedRecord = try await publicDatabase.save(ckItem.record)
            print("☁️ [CloudKit] Record saved: \(savedRecord.recordID.recordName)")
            return item
        } catch let error as CKError {
            print("☁️ [CloudKit] CKError on create: \(error.code.rawValue) - \(error.localizedDescription)")
            throw mapCloudKitError(error)
        } catch {
            print("☁️ [CloudKit] Unknown error on create: \(error)")
            throw CustomItemError.unknown(error)
        }
    }

    func updateItem(_ item: CustomItineraryItem) async throws -> CustomItineraryItem {
        // Fetch existing record to get changeTag
        let recordID = CKRecord.ID(recordName: item.id.uuidString)
        let existingRecord: CKRecord

        do {
            existingRecord = try await publicDatabase.record(for: recordID)
        } catch let error as CKError {
            throw mapCloudKitError(error)
        } catch {
            throw CustomItemError.unknown(error)
        }

        // Update fields
        let now = Date()
        existingRecord[CKCustomItineraryItem.categoryKey] = item.category.rawValue
        existingRecord[CKCustomItineraryItem.titleKey] = item.title
        existingRecord[CKCustomItineraryItem.anchorTypeKey] = item.anchorType.rawValue
        existingRecord[CKCustomItineraryItem.anchorIdKey] = item.anchorId
        existingRecord[CKCustomItineraryItem.anchorDayKey] = item.anchorDay
        existingRecord[CKCustomItineraryItem.sortOrderKey] = item.sortOrder
        existingRecord[CKCustomItineraryItem.modifiedAtKey] = now

        do {
            try await publicDatabase.save(existingRecord)
            var updatedItem = item
            updatedItem.modifiedAt = now
            return updatedItem
        } catch let error as CKError {
            throw mapCloudKitError(error)
        } catch {
            throw CustomItemError.unknown(error)
        }
    }

    /// Batch update sortOrder for multiple items (for reordering)
    func updateSortOrders(_ items: [CustomItineraryItem]) async throws {
        guard !items.isEmpty else { return }
        print("☁️ [CloudKit] Batch updating sortOrder for \(items.count) items")

        // Fetch all records
        let recordIDs = items.map { CKRecord.ID(recordName: $0.id.uuidString) }
        let fetchResults = try await publicDatabase.records(for: recordIDs)

        // Update each record's sortOrder
        var recordsToSave: [CKRecord] = []
        let now = Date()

        for item in items {
            let recordID = CKRecord.ID(recordName: item.id.uuidString)
            guard case .success(let record) = fetchResults[recordID] else { continue }

            record[CKCustomItineraryItem.sortOrderKey] = item.sortOrder
            record[CKCustomItineraryItem.modifiedAtKey] = now
            recordsToSave.append(record)
        }

        // Save all in one batch operation
        let modifyOp = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: nil)
        modifyOp.savePolicy = .changedKeys

        try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
            modifyOp.modifyRecordsResultBlock = { result in
                switch result {
                case .success:
                    print("☁️ [CloudKit] Batch sortOrder update complete")
                    continuation.resume()
                case .failure(let error):
                    print("☁️ [CloudKit] Batch update failed: \(error)")
                    continuation.resume(throwing: error)
                }
            }
            publicDatabase.add(modifyOp)
        }
    }

    func deleteItem(_ itemId: UUID) async throws {
        let recordID = CKRecord.ID(recordName: itemId.uuidString)

        do {
            try await publicDatabase.deleteRecord(withID: recordID)
        } catch let error as CKError {
            if error.code != .unknownItem {
                throw mapCloudKitError(error)
            }
            // Item already deleted - ignore
        } catch {
            throw CustomItemError.unknown(error)
        }
    }

    func fetchItems(forTripId tripId: UUID) async throws -> [CustomItineraryItem] {
        print("☁️ [CloudKit] Fetching items for tripId: \(tripId.uuidString)")
        let predicate = NSPredicate(
            format: "%K == %@",
            CKCustomItineraryItem.tripIdKey,
            tripId.uuidString
        )
        let query = CKQuery(recordType: CKRecordType.customItineraryItem, predicate: predicate)

        do {
            let (results, _) = try await publicDatabase.records(matching: query)
            print("☁️ [CloudKit] Query returned \(results.count) records")

            let items = results.compactMap { result -> CustomItineraryItem? in
                guard case .success(let record) = result.1 else {
                    print("☁️ [CloudKit] Record fetch failed for: \(result.0.recordName)")
                    return nil
                }
                let item = CKCustomItineraryItem(record: record).toItem()
                if item == nil {
                    print("☁️ [CloudKit] Failed to parse record: \(record.recordID.recordName)")
                }
                return item
            }.sorted { ($0.anchorDay, $0.sortOrder) < ($1.anchorDay, $1.sortOrder) }

            print("☁️ [CloudKit] Parsed \(items.count) valid items")
            return items
        } catch let error as CKError {
            print("☁️ [CloudKit] CKError on fetch: \(error.code.rawValue) - \(error.localizedDescription)")
            throw mapCloudKitError(error)
        } catch {
            print("☁️ [CloudKit] Unknown error on fetch: \(error)")
            throw CustomItemError.unknown(error)
        }
    }

    // MARK: - Error Mapping

    private func mapCloudKitError(_ error: CKError) -> CustomItemError {
        switch error.code {
        case .notAuthenticated:
            return .notSignedIn
        case .networkUnavailable, .networkFailure:
            return .networkUnavailable
        case .unknownItem:
            return .itemNotFound
        default:
            return .unknown(error)
        }
    }
}

// MARK: - Errors

enum CustomItemError: Error, LocalizedError {
    case notSignedIn
    case itemNotFound
    case networkUnavailable
    case unknown(Error)

    var errorDescription: String? {
        switch self {
        case .notSignedIn:
            return "Please sign in to iCloud to use custom items."
        case .itemNotFound:
            return "Item not found. It may have been deleted."
        case .networkUnavailable:
            return "Unable to connect. Please check your internet connection."
        case .unknown(let error):
            return "An error occurred: \(error.localizedDescription)"
        }
    }
}
