//
//  VisitPhotoService.swift
//  SportsTime
//
//  Manages visit photos with CloudKit sync for backup.
//  Thumbnails stored locally in SwiftData for fast loading.
//  Full images stored in CloudKit private database.
//

import Foundation
import CloudKit
import SwiftData
import UIKit

// MARK: - Photo Service Errors

enum PhotoServiceError: Error, LocalizedError {
    case notSignedIn
    case uploadFailed(String)
    case downloadFailed(String)
    case thumbnailGenerationFailed
    case invalidImage
    case assetNotFound
    case quotaExceeded

    var errorDescription: String? {
        switch self {
        case .notSignedIn:
            return "Please sign in to iCloud to sync photos"
        case .uploadFailed(let message):
            return "Upload failed: \(message)"
        case .downloadFailed(let message):
            return "Download failed: \(message)"
        case .thumbnailGenerationFailed:
            return "Could not generate thumbnail"
        case .invalidImage:
            return "Invalid image data"
        case .assetNotFound:
            return "Photo not found in cloud storage"
        case .quotaExceeded:
            return "iCloud storage quota exceeded"
        }
    }
}

// MARK: - Visit Photo Service

@MainActor
final class VisitPhotoService {

    // MARK: - Properties

    private let modelContext: ModelContext
    private let container: CKContainer
    private let privateDatabase: CKDatabase

    // Configuration
    private static let thumbnailSize = CGSize(width: 200, height: 200)
    private static let compressionQuality: CGFloat = 0.7
    private static let recordType = "VisitPhoto"

    // MARK: - Initialization

    init(modelContext: ModelContext) {
        self.modelContext = modelContext
        self.container = CKContainer(identifier: "iCloud.com.sportstime.app")
        self.privateDatabase = container.privateCloudDatabase
    }

    // MARK: - Public API

    /// Add a photo to a visit
    /// - Parameters:
    ///   - visit: The visit to add the photo to
    ///   - image: The UIImage to add
    ///   - caption: Optional caption for the photo
    /// - Returns: The created photo metadata
    func addPhoto(to visit: StadiumVisit, image: UIImage, caption: String? = nil) async throws -> VisitPhotoMetadata {
        // Generate thumbnail
        guard let thumbnail = generateThumbnail(from: image) else {
            throw PhotoServiceError.thumbnailGenerationFailed
        }

        guard let thumbnailData = thumbnail.jpegData(compressionQuality: Self.compressionQuality) else {
            throw PhotoServiceError.thumbnailGenerationFailed
        }

        // Get current photo count for order index
        let orderIndex = visit.photoMetadata?.count ?? 0

        // Create metadata record
        let metadata = VisitPhotoMetadata(
            visitId: visit.id,
            cloudKitAssetId: nil,
            thumbnailData: thumbnailData,
            caption: caption,
            orderIndex: orderIndex,
            uploadStatus: .pending
        )

        // Add to visit
        if visit.photoMetadata == nil {
            visit.photoMetadata = []
        }
        visit.photoMetadata?.append(metadata)

        modelContext.insert(metadata)
        try modelContext.save()

        // Queue background upload
        Task.detached { [weak self] in
            await self?.uploadPhoto(metadata: metadata, image: image)
        }

        return metadata
    }

    /// Fetch full-resolution image for a photo
    /// - Parameter metadata: The photo metadata
    /// - Returns: The full-resolution UIImage
    func fetchFullImage(for metadata: VisitPhotoMetadata) async throws -> UIImage {
        guard let assetId = metadata.cloudKitAssetId else {
            throw PhotoServiceError.assetNotFound
        }

        let recordID = CKRecord.ID(recordName: assetId)

        do {
            let record = try await privateDatabase.record(for: recordID)

            guard let asset = record["imageAsset"] as? CKAsset,
                  let fileURL = asset.fileURL,
                  let data = try? Data(contentsOf: fileURL),
                  let image = UIImage(data: data) else {
                throw PhotoServiceError.downloadFailed("Could not read image data")
            }

            return image
        } catch let error as CKError {
            throw mapCloudKitError(error)
        }
    }

    /// Delete a photo from visit and CloudKit
    /// - Parameter metadata: The photo metadata to delete
    func deletePhoto(_ metadata: VisitPhotoMetadata) async throws {
        // Delete from CloudKit if uploaded
        if let assetId = metadata.cloudKitAssetId {
            let recordID = CKRecord.ID(recordName: assetId)
            do {
                try await privateDatabase.deleteRecord(withID: recordID)
            } catch {
                // Continue with local deletion even if CloudKit fails
            }
        }

        // Delete from SwiftData
        modelContext.delete(metadata)
        try modelContext.save()
    }

    /// Retry uploading failed photos
    func retryFailedUploads() async {
        let descriptor = FetchDescriptor<VisitPhotoMetadata>(
            predicate: #Predicate { $0.uploadStatusRaw == "failed" || $0.uploadStatusRaw == "pending" }
        )

        do {
            let pendingPhotos = try modelContext.fetch(descriptor)

            for metadata in pendingPhotos {
                // We can't upload without the original image
                // Mark as failed permanently if no thumbnail
                if metadata.thumbnailData == nil {
                    metadata.uploadStatus = .failed
                }
            }

            try modelContext.save()
        } catch {
            // Silently fail - will retry on next launch
        }
    }

    /// Get upload status summary
    func getUploadStatus() -> (pending: Int, uploaded: Int, failed: Int) {
        let descriptor = FetchDescriptor<VisitPhotoMetadata>()

        do {
            let all = try modelContext.fetch(descriptor)

            let pending = all.filter { $0.uploadStatus == .pending }.count
            let uploaded = all.filter { $0.uploadStatus == .uploaded }.count
            let failed = all.filter { $0.uploadStatus == .failed }.count

            return (pending, uploaded, failed)
        } catch {
            return (0, 0, 0)
        }
    }

    /// Check if CloudKit is available for photo sync
    func isCloudKitAvailable() async -> Bool {
        do {
            let status = try await container.accountStatus()
            return status == .available
        } catch {
            return false
        }
    }

    // MARK: - Private Methods

    private func uploadPhoto(metadata: VisitPhotoMetadata, image: UIImage) async {
        guard let imageData = image.jpegData(compressionQuality: Self.compressionQuality) else {
            await MainActor.run {
                metadata.uploadStatus = .failed
                try? modelContext.save()
            }
            return
        }

        // Check CloudKit availability
        do {
            let status = try await container.accountStatus()
            guard status == .available else {
                await MainActor.run {
                    metadata.uploadStatus = .failed
                    try? modelContext.save()
                }
                return
            }
        } catch {
            await MainActor.run {
                metadata.uploadStatus = .failed
                try? modelContext.save()
            }
            return
        }

        // Create CloudKit record
        let recordID = CKRecord.ID(recordName: metadata.id.uuidString)
        let record = CKRecord(recordType: Self.recordType, recordID: recordID)

        // Write image to temporary file for CKAsset
        let tempURL = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString)
            .appendingPathExtension("jpg")

        do {
            try imageData.write(to: tempURL)

            let asset = CKAsset(fileURL: tempURL)
            record["imageAsset"] = asset
            record["visitId"] = metadata.visitId.uuidString
            record["caption"] = metadata.caption
            record["orderIndex"] = metadata.orderIndex as CKRecordValue

            // Upload to CloudKit
            let savedRecord = try await privateDatabase.save(record)

            // Clean up temp file
            try? FileManager.default.removeItem(at: tempURL)

            // Update metadata
            await MainActor.run {
                metadata.cloudKitAssetId = savedRecord.recordID.recordName
                metadata.uploadStatus = .uploaded
                try? modelContext.save()
            }

        } catch let error as CKError {
            // Clean up temp file
            try? FileManager.default.removeItem(at: tempURL)

            await MainActor.run {
                metadata.uploadStatus = .failed
                try? modelContext.save()
            }
        } catch {
            // Clean up temp file
            try? FileManager.default.removeItem(at: tempURL)

            await MainActor.run {
                metadata.uploadStatus = .failed
                try? modelContext.save()
            }
        }
    }

    private func generateThumbnail(from image: UIImage) -> UIImage? {
        let size = Self.thumbnailSize
        let aspectRatio = image.size.width / image.size.height

        let targetSize: CGSize
        if aspectRatio > 1 {
            // Landscape
            targetSize = CGSize(width: size.width, height: size.width / aspectRatio)
        } else {
            // Portrait or square
            targetSize = CGSize(width: size.height * aspectRatio, height: size.height)
        }

        let renderer = UIGraphicsImageRenderer(size: targetSize)
        return renderer.image { context in
            image.draw(in: CGRect(origin: .zero, size: targetSize))
        }
    }

    private func mapCloudKitError(_ error: CKError) -> PhotoServiceError {
        switch error.code {
        case .notAuthenticated:
            return .notSignedIn
        case .quotaExceeded:
            return .quotaExceeded
        case .unknownItem:
            return .assetNotFound
        default:
            return .downloadFailed(error.localizedDescription)
        }
    }
}

// MARK: - Photo Gallery View Model

@Observable
@MainActor
final class PhotoGalleryViewModel {
    var photos: [VisitPhotoMetadata] = []
    var selectedPhoto: VisitPhotoMetadata?
    var fullResolutionImage: UIImage?
    var isLoadingFullImage = false
    var error: PhotoServiceError?

    private let photoService: VisitPhotoService
    private let visit: StadiumVisit

    init(visit: StadiumVisit, modelContext: ModelContext) {
        self.visit = visit
        self.photoService = VisitPhotoService(modelContext: modelContext)
        loadPhotos()
    }

    func loadPhotos() {
        photos = (visit.photoMetadata ?? []).sorted { $0.orderIndex < $1.orderIndex }
    }

    func addPhoto(_ image: UIImage, caption: String? = nil) async {
        do {
            let metadata = try await photoService.addPhoto(to: visit, image: image, caption: caption)
            photos.append(metadata)
            photos.sort { $0.orderIndex < $1.orderIndex }
        } catch let error as PhotoServiceError {
            self.error = error
        } catch {
            self.error = .uploadFailed(error.localizedDescription)
        }
    }

    func selectPhoto(_ metadata: VisitPhotoMetadata) {
        selectedPhoto = metadata
        loadFullResolution(for: metadata)
    }

    func loadFullResolution(for metadata: VisitPhotoMetadata) {
        guard metadata.cloudKitAssetId != nil else {
            // Photo not uploaded yet, use thumbnail
            if let data = metadata.thumbnailData {
                fullResolutionImage = UIImage(data: data)
            }
            return
        }

        isLoadingFullImage = true
        Task {
            do {
                let image = try await photoService.fetchFullImage(for: metadata)
                fullResolutionImage = image
            } catch let error as PhotoServiceError {
                self.error = error
                // Fall back to thumbnail
                if let data = metadata.thumbnailData {
                    fullResolutionImage = UIImage(data: data)
                }
            } catch {
                self.error = .downloadFailed(error.localizedDescription)
            }
            isLoadingFullImage = false
        }
    }

    func deletePhoto(_ metadata: VisitPhotoMetadata) async {
        do {
            try await photoService.deletePhoto(metadata)
            photos.removeAll { $0.id == metadata.id }
            if selectedPhoto?.id == metadata.id {
                selectedPhoto = nil
                fullResolutionImage = nil
            }
        } catch let error as PhotoServiceError {
            self.error = error
        } catch {
            self.error = .uploadFailed(error.localizedDescription)
        }
    }

    func clearError() {
        error = nil
    }
}
