import Foundation
import UIKit
import ComposeApp
import Combine

/// ViewModel for document management.
/// Observes DataManagerObservable for documents list.
/// Calls APILayer directly for all operations.
@MainActor
class DocumentViewModel: ObservableObject {
    @Published var documents: [Document] = []
    @Published var isLoading = false
    @Published var errorMessage: String?

    // MARK: - Private Properties
    private var cancellables = Set<AnyCancellable>()
    private let dataManager: DataManagerObservable

    /// - Parameter dataManager: Observable cache the VM subscribes to.
    ///   Defaults to the shared singleton.
    init(dataManager: DataManagerObservable = .shared) {
        self.dataManager = dataManager

        // Seed from current cache so snapshot tests/previews render
        // populated state without waiting for Combine's async dispatch.
        self.documents = dataManager.documents

        // Observe documents from injected DataManagerObservable
        dataManager.$documents
            .receive(on: DispatchQueue.main)
            .sink { [weak self] documents in
                self?.documents = documents
            }
            .store(in: &cancellables)
    }

    func loadDocuments(
        residenceId: Int32? = nil,
        documentType: String? = nil,
        category: String? = nil,
        contractorId: Int32? = nil,
        isActive: Bool? = nil,
        expiringSoon: Int32? = nil,
        tags: String? = nil,
        search: String? = nil,
        forceRefresh: Bool = false
    ) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.getDocuments(
                    residenceId: residenceId.asKotlin,
                    documentType: documentType,
                    category: category,
                    contractorId: contractorId.asKotlin,
                    isActive: isActive.asKotlin,
                    expiringSoon: expiringSoon.asKotlin,
                    tags: tags,
                    search: search,
                    forceRefresh: forceRefresh
                )

                // API updates DataManager on success, which triggers our observation
                if result is ApiResultSuccess<NSArray> {
                    self.isLoading = false
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isLoading = false
                } else {
                    self.errorMessage = "Failed to load documents"
                    self.isLoading = false
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
            }
        }
    }

    func createDocument(
        title: String,
        documentType: String,
        residenceId: Int32,
        description: String? = nil,
        category: String? = nil,
        tags: String? = nil,
        notes: String? = nil,
        contractorId: Int32? = nil,
        isActive: Bool = true,
        itemName: String? = nil,
        modelNumber: String? = nil,
        serialNumber: String? = nil,
        provider: String? = nil,
        providerContact: String? = nil,
        claimPhone: String? = nil,
        claimEmail: String? = nil,
        claimWebsite: String? = nil,
        purchaseDate: String? = nil,
        startDate: String? = nil,
        endDate: String? = nil,
        images: [UIImage] = [],
        completion: @escaping (Bool, String?) -> Void
    ) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.createDocument(
                    title: title,
                    documentType: documentType,
                    residenceId: residenceId,
                    description: description,
                    category: category,
                    tags: tags,
                    notes: notes,
                    contractorId: contractorId.asKotlin,
                    isActive: isActive,
                    itemName: itemName,
                    modelNumber: modelNumber,
                    serialNumber: serialNumber,
                    provider: provider,
                    providerContact: providerContact,
                    claimPhone: claimPhone,
                    claimEmail: claimEmail,
                    claimWebsite: claimWebsite,
                    purchaseDate: purchaseDate,
                    startDate: startDate,
                    endDate: endDate,
                    fileBytes: nil,
                    fileName: nil,
                    mimeType: nil,
                    fileBytesList: nil,
                    fileNamesList: nil,
                    mimeTypesList: nil
                )

                if let success = result as? ApiResultSuccess<Document> {
                    if !images.isEmpty {
                        guard let document = success.data,
                              let documentId = document.id?.int32Value,
                              document.title != nil else {
                            let missingFields = [
                                success.data == nil ? "data" : nil,
                                success.data?.id == nil ? "id" : nil,
                                success.data?.title == nil ? "title" : nil
                            ].compactMap { $0 }.joined(separator: ", ")
                            print("DocumentViewModel: Document creation returned incomplete data (missing: \(missingFields)), skipping image upload")
                            self.errorMessage = "Document created with incomplete data — images were not uploaded"
                            self.isLoading = false
                            completion(false, self.errorMessage)
                            return
                        }

                        if let uploadError = await self.uploadImages(documentId: documentId, images: images) {
                            print("DocumentViewModel: Image upload failed for document \(documentId): \(uploadError)")
                            self.errorMessage = uploadError
                            self.isLoading = false
                            completion(false, self.errorMessage)
                            return
                        }
                    }

                    self.isLoading = false
                    completion(true, nil)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isLoading = false
                    completion(false, self.errorMessage)
                } else {
                    self.errorMessage = "Failed to create document"
                    self.isLoading = false
                    completion(false, self.errorMessage)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
                completion(false, self.errorMessage)
            }
        }
    }

    func updateDocument(
        id: Int32,
        title: String,
        documentType: String,
        description: String? = nil,
        category: String? = nil,
        tags: String? = nil,
        notes: String? = nil,
        contractorId: Int32? = nil,
        isActive: Bool = true,
        itemName: String? = nil,
        modelNumber: String? = nil,
        serialNumber: String? = nil,
        provider: String? = nil,
        providerContact: String? = nil,
        claimPhone: String? = nil,
        claimEmail: String? = nil,
        claimWebsite: String? = nil,
        purchaseDate: String? = nil,
        startDate: String? = nil,
        endDate: String? = nil,
        newImages: [UIImage] = [],
        completion: @escaping (Bool, String?) -> Void
    ) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.updateDocument(
                    id: id,
                    title: title,
                    documentType: documentType,
                    description: description,
                    category: category,
                    tags: tags,
                    notes: notes,
                    contractorId: contractorId.asKotlin,
                    isActive: isActive,
                    itemName: itemName,
                    modelNumber: modelNumber,
                    serialNumber: serialNumber,
                    provider: provider,
                    providerContact: providerContact,
                    claimPhone: claimPhone,
                    claimEmail: claimEmail,
                    claimWebsite: claimWebsite,
                    purchaseDate: purchaseDate,
                    startDate: startDate,
                    endDate: endDate
                )

                if result is ApiResultSuccess<Document> {
                    if !newImages.isEmpty,
                       let uploadError = await self.uploadImages(documentId: id, images: newImages) {
                        self.errorMessage = uploadError
                        self.isLoading = false
                        completion(false, self.errorMessage)
                        return
                    }

                    self.isLoading = false
                    completion(true, nil)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isLoading = false
                    completion(false, self.errorMessage)
                } else {
                    self.errorMessage = "Failed to update document"
                    self.isLoading = false
                    completion(false, self.errorMessage)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
                completion(false, self.errorMessage)
            }
        }
    }

    func deleteDocument(id: Int32, completion: @escaping (Bool) -> Void = { _ in }) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.deleteDocument(id: id)

                if result is ApiResultSuccess<KotlinUnit> {
                    self.isLoading = false
                    // DataManager is updated by APILayer, view updates via observation
                    completion(true)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isLoading = false
                    completion(false)
                } else {
                    self.errorMessage = "Failed to delete document"
                    self.isLoading = false
                    completion(false)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
                completion(false)
            }
        }
    }

    func downloadDocument(url: String) -> Task<Data?, Error> {
        return Task {
            do {
                let result = try await APILayer.shared.downloadDocument(url: url)

                if let success = result as? ApiResultSuccess<KotlinByteArray>, let byteArray = success.data {
                    // Convert Kotlin ByteArray to Swift Data
                    var data = Data()
                    for i in 0..<byteArray.size {
                        data.append(UInt8(bitPattern: byteArray.get(index: i)))
                    }
                    return data
                } else if let error = ApiResultBridge.error(from: result) {
                    throw NSError(domain: error.message, code: error.code?.intValue ?? 0)
                }
                return nil
            } catch {
                throw error
            }
        }
    }

    /// Uploads additional document images after create/update metadata succeeds.
    private func uploadImages(documentId: Int32, images: [UIImage]) async -> String? {
        for (index, image) in images.enumerated() {
            guard let compressedData = ImageCompression.compressImage(image) else {
                return "Failed to process image \(index + 1)"
            }

            let uploadResult: Any
            do {
                uploadResult = try await APILayer.shared.uploadDocumentImage(
                    documentId: documentId,
                    imageBytes: self.kotlinByteArray(from: compressedData),
                    fileName: "document_image_\(index + 1).jpg",
                    mimeType: "image/jpeg",
                    caption: nil
                )
            } catch {
                return ErrorMessageParser.parse(error.localizedDescription)
            }

            if let error = ApiResultBridge.error(from: uploadResult) {
                return ErrorMessageParser.parse(error.message)
            }

            if !(uploadResult is ApiResultSuccess<Document>) {
                return "Failed to upload image \(index + 1)"
            }
        }

        return nil
    }

    private func kotlinByteArray(from data: Data) -> KotlinByteArray {
        let byteArray = KotlinByteArray(size: Int32(data.count))
        for (index, byte) in data.enumerated() {
            byteArray.set(index: Int32(index), value: Int8(bitPattern: byte))
        }
        return byteArray
    }
}
