import Foundation
import ComposeApp
import SwiftUI
import Combine

// MARK: - Architecture Note
//
// Two document ViewModels coexist with distinct responsibilities:
//
// DocumentViewModel (DocumentViewModel.swift):
//   - Used by list views (DocumentsView, DocumentListView)
//   - Observes DataManager via DataManagerObservable for reactive list updates
//   - Handles CRUD operations that update DataManager cache (create, update, delete)
//   - Supports image upload workflows
//   - Uses @MainActor for thread safety
//
// DocumentViewModelWrapper (this file):
//   - Used by detail views (DocumentDetailView, EditDocumentView)
//   - Manages explicit state types (Loading/Success/Error) for single-document operations
//   - Loads individual document detail, handles update and delete with state feedback
//   - Observes DataManager for automatic detail updates after mutations
//   - Uses protocol-based state enums for SwiftUI view branching
//
// Both call through APILayer (which updates DataManager), so list views
// auto-refresh when detail views perform mutations.

// State wrappers for SwiftUI
protocol DocumentState {}
struct DocumentStateIdle: DocumentState {}
struct DocumentStateLoading: DocumentState {}
struct DocumentStateSuccess: DocumentState {
    let documents: [Document]
}
struct DocumentStateError: DocumentState {
    let message: String
}

protocol DocumentDetailState {}
struct DocumentDetailStateIdle: DocumentDetailState {}
struct DocumentDetailStateLoading: DocumentDetailState {}
struct DocumentDetailStateSuccess: DocumentDetailState {
    let document: Document
}
struct DocumentDetailStateError: DocumentDetailState {
    let message: String
}

protocol UpdateState {}
struct UpdateStateIdle: UpdateState {}
struct UpdateStateLoading: UpdateState {}
struct UpdateStateSuccess: UpdateState {
    let document: Document
}
struct UpdateStateError: UpdateState {
    let message: String
}

protocol DeleteState {}
struct DeleteStateIdle: DeleteState {}
struct DeleteStateLoading: DeleteState {}
struct DeleteStateSuccess: DeleteState {}
struct DeleteStateError: DeleteState {
    let message: String
}

protocol DeleteImageState {}
struct DeleteImageStateIdle: DeleteImageState {}
struct DeleteImageStateLoading: DeleteImageState {}
struct DeleteImageStateSuccess: DeleteImageState {}
struct DeleteImageStateError: DeleteImageState {
    let message: String
}

@MainActor
class DocumentViewModelWrapper: ObservableObject {
    @Published var documentsState: DocumentState = DocumentStateIdle()
    @Published var documentDetailState: DocumentDetailState = DocumentDetailStateIdle()
    @Published var updateState: UpdateState = UpdateStateIdle()
    @Published var deleteState: DeleteState = DeleteStateIdle()
    @Published var deleteImageState: DeleteImageState = DeleteImageStateIdle()

    /// The document ID currently loaded in detail view, used for auto-update
    private var loadedDocumentId: Int32?
    private var cancellables = Set<AnyCancellable>()

    /// - Parameter initialDocument: Pre-seeded detail document. Used by
    ///   snapshot tests / previews so `DocumentDetailView` renders
    ///   populated on first composition without waiting for the
    ///   asynchronous `loadDocumentDetail` → APILayer round-trip.
    init(initialDocument: Document? = nil) {
        if let doc = initialDocument {
            self.documentDetailState = DocumentDetailStateSuccess(document: doc)
            self.loadedDocumentId = doc.id?.int32Value
        }
        // Observe DataManager documents for auto-update of loaded detail
        DataManagerObservable.shared.$documents
            .receive(on: DispatchQueue.main)
            .sink { [weak self] documents in
                guard let self, let docId = self.loadedDocumentId else { return }
                // Only auto-update if we're in a success state
                guard self.documentDetailState is DocumentDetailStateSuccess else { return }
                if let updated = documents.first(where: { $0.id?.int32Value == docId }) {
                    self.documentDetailState = DocumentDetailStateSuccess(document: updated)
                }
            }
            .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
    ) {
        self.documentsState = DocumentStateLoading()

        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: false
                )

                if let success = result as? ApiResultSuccess<NSArray> {
                    let documents = success.data as? [Document] ?? []
                    self.documentsState = DocumentStateSuccess(documents: documents)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.documentsState = DocumentStateError(message: error.message)
                } else {
                    self.documentsState = DocumentStateError(message: "Failed to load documents")
                }
            } catch {
                self.documentsState = DocumentStateError(message: error.localizedDescription)
            }
        }
    }

    func loadDocumentDetail(id: Int32) {
        loadedDocumentId = id
        // Don't clobber existing success state for the same id — keeps
        // the pre-seeded document visible through `onAppear`-triggered
        // background refreshes (critical for snapshot-test/preview
        // hermeticity) and avoids flashing a spinner over content the
        // user already sees on re-entry.
        if let existing = documentDetailState as? DocumentDetailStateSuccess,
           existing.document.id?.int32Value == id {
            // Skip the loading flash; the Task below will still refresh
            // in the background if the API responds.
        } else {
            self.documentDetailState = DocumentDetailStateLoading()
        }

        Task {
            do {
                let result = try await APILayer.shared.getDocument(id: id, forceRefresh: false)

                if let success = result as? ApiResultSuccess<Document>, let document = success.data {
                    self.documentDetailState = DocumentDetailStateSuccess(document: document)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.documentDetailState = DocumentDetailStateError(message: error.message)
                } else {
                    self.documentDetailState = DocumentDetailStateError(message: "Failed to load document")
                }
            } catch {
                self.documentDetailState = DocumentDetailStateError(message: error.localizedDescription)
            }
        }
    }

    func updateDocument(
        id: Int32,
        title: String,
        documentType: String,
        description: String? = nil,
        category: String? = nil,
        tags: String? = nil,
        notes: String? = 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
    ) {
        self.updateState = UpdateStateLoading()

        Task {
            do {
                let result = try await APILayer.shared.updateDocument(
                    id: id,
                    title: title,
                    documentType: documentType,
                    description: description,
                    category: category,
                    tags: tags,
                    notes: notes,
                    contractorId: nil,
                    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 let success = result as? ApiResultSuccess<Document>, let document = success.data {
                    self.updateState = UpdateStateSuccess(document: document)
                    // Also refresh the detail state
                    self.documentDetailState = DocumentDetailStateSuccess(document: document)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.updateState = UpdateStateError(message: error.message)
                } else {
                    self.updateState = UpdateStateError(message: "Failed to update document")
                }
            } catch {
                self.updateState = UpdateStateError(message: error.localizedDescription)
            }
        }
    }

    func deleteDocument(id: Int32) {
        self.deleteState = DeleteStateLoading()

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

                if result is ApiResultSuccess<KotlinUnit> {
                    self.deleteState = DeleteStateSuccess()
                } else if let error = ApiResultBridge.error(from: result) {
                    self.deleteState = DeleteStateError(message: error.message)
                } else {
                    self.deleteState = DeleteStateError(message: "Failed to delete document")
                }
            } catch {
                self.deleteState = DeleteStateError(message: error.localizedDescription)
            }
        }
    }

    func resetUpdateState() {
        self.updateState = UpdateStateIdle()
    }

    func resetDeleteState() {
        self.deleteState = DeleteStateIdle()
    }

    func deleteDocumentImage(documentId: Int32, imageId: Int32) {
        self.deleteImageState = DeleteImageStateLoading()

        Task {
            do {
                let result = try await APILayer.shared.deleteDocumentImage(documentId: documentId, imageId: imageId)

                if let success = result as? ApiResultSuccess<Document>, let document = success.data {
                    self.deleteImageState = DeleteImageStateSuccess()
                    // Refresh detail state with updated document (image removed)
                    self.documentDetailState = DocumentDetailStateSuccess(document: document)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.deleteImageState = DeleteImageStateError(message: error.message)
                } else {
                    self.deleteImageState = DeleteImageStateError(message: "Failed to delete image")
                }
            } catch {
                self.deleteImageState = DeleteImageStateError(message: error.localizedDescription)
            }
        }
    }

    func resetDeleteImageState() {
        self.deleteImageState = DeleteImageStateIdle()
    }
}
