import Foundation
import ComposeApp
import Combine

/// ViewModel for contractor management.
/// Observes DataManagerObservable for contractors list (automatically updated after mutations).
/// Calls APILayer for operations - DataManager updates propagate automatically via observation.
@MainActor
class ContractorViewModel: ObservableObject {
    // MARK: - Published Properties
    @Published var contractors: [ContractorSummary] = []
    @Published var selectedContractor: Contractor?
    @Published var isLoading: Bool = false
    @Published var errorMessage: String?
    @Published var isCreating: Bool = false
    @Published var isUpdating: Bool = false
    @Published var isDeleting: Bool = false
    @Published var successMessage: String?

    // MARK: - Private Properties
    private var cancellables = Set<AnyCancellable>()
    private let dataManager: DataManagerObservable
    /// Timestamp of the last mutation that already set selectedContractor from its response.
    /// Used to suppress redundant detail reloads within 1 second of a mutation.
    /// Unlike a boolean flag, this naturally expires and can never get stuck.
    private var lastMutationTime: Date?

    // MARK: - Initialization

    /// - Parameters:
    ///   - dataManager: Observable cache the VM subscribes to. Defaults
    ///     to the shared singleton.
    ///   - initialSelectedContractor: Pre-seeded detail state. Used by
    ///     snapshot tests/previews so `ContractorDetailView` renders
    ///     populated on first composition without waiting for the
    ///     asynchronous `loadContractorDetail` → APILayer round-trip.
    init(
        dataManager: DataManagerObservable = .shared,
        initialSelectedContractor: Contractor? = nil
    ) {
        self.dataManager = dataManager
        self.selectedContractor = initialSelectedContractor

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

        // Observe contractors from injected DataManagerObservable
        dataManager.$contractors
            .receive(on: DispatchQueue.main)
            .sink { [weak self] contractors in
                self?.contractors = contractors
                // Auto-refresh selectedContractor when the list changes,
                // so detail views stay current after mutations from other ViewModels.
                // ContractorSummary and Contractor are different types, so we can't
                // copy fields directly. Instead, if selectedContractor exists and a
                // matching summary is found, reload the full detail from the API.
                if let self = self,
                   let currentId = self.selectedContractor?.id,
                   contractors.contains(where: { $0.id == currentId }) {
                    // Skip reload if a mutation just updated selectedContractor within the last second
                    if let mutationTime = self.lastMutationTime,
                       Date().timeIntervalSince(mutationTime) < 1.0 {
                        // Mutation was recent, no need to reload
                    } else {
                        self.reloadSelectedContractorQuietly(id: currentId)
                    }
                }
            }
            .store(in: &cancellables)
    }

    // MARK: - Public Methods

    /// Load contractors list - delegates to APILayer which handles cache timeout
    func loadContractors(forceRefresh: Bool = false) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.getContractors(
                    specialty: nil,
                    isFavorite: nil,
                    isActive: nil,
                    search: nil,
                    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 contractors"
                    self.isLoading = false
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
            }
        }
    }

    func loadContractorDetail(id: Int32) {
        isLoading = true
        errorMessage = nil

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

                if let success = result as? ApiResultSuccess<Contractor> {
                    self.selectedContractor = success.data
                    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 contractor details"
                    self.isLoading = false
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isLoading = false
            }
        }
    }

    func createContractor(request: ContractorCreateRequest, completion: @escaping (Bool) -> Void) {
        isCreating = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.createContractor(request: request)

                if let success = result as? ApiResultSuccess<Contractor> {
                    self.successMessage = "Contractor added successfully"
                    self.isCreating = false
                    // Update selectedContractor with the newly created contractor
                    self.lastMutationTime = Date()
                    self.selectedContractor = success.data
                    completion(true)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isCreating = false
                    completion(false)
                } else {
                    self.errorMessage = "Failed to create contractor"
                    self.isCreating = false
                    completion(false)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isCreating = false
                completion(false)
            }
        }
    }

    func updateContractor(id: Int32, request: ContractorUpdateRequest, completion: @escaping (Bool) -> Void) {
        isUpdating = true
        errorMessage = nil

        Task {
            do {
                let result = try await APILayer.shared.updateContractor(id: id, request: request)

                if let success = result as? ApiResultSuccess<Contractor> {
                    self.successMessage = "Contractor updated successfully"
                    self.isUpdating = false
                    // Update selectedContractor immediately so detail views stay fresh
                    self.lastMutationTime = Date()
                    self.selectedContractor = success.data
                    completion(true)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    self.isUpdating = false
                    completion(false)
                } else {
                    self.errorMessage = "Failed to update contractor"
                    self.isUpdating = false
                    completion(false)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isUpdating = false
                completion(false)
            }
        }
    }

    func deleteContractor(id: Int32, completion: @escaping (Bool) -> Void) {
        isDeleting = true
        errorMessage = nil

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

                if result is ApiResultSuccess<KotlinUnit> {
                    self.successMessage = "Contractor deleted successfully"
                    self.isDeleting = 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.isDeleting = false
                    completion(false)
                } else {
                    self.errorMessage = "Failed to delete contractor"
                    self.isDeleting = false
                    completion(false)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                self.isDeleting = false
                completion(false)
            }
        }
    }

    func toggleFavorite(id: Int32, completion: @escaping (Bool) -> Void) {
        Task {
            do {
                let result = try await APILayer.shared.toggleFavorite(id: id)

                if let success = result as? ApiResultSuccess<Contractor> {
                    // Update selectedContractor immediately so detail views stay fresh
                    self.lastMutationTime = Date()
                    self.selectedContractor = success.data
                    completion(true)
                } else if let error = ApiResultBridge.error(from: result) {
                    self.errorMessage = ErrorMessageParser.parse(error.message)
                    completion(false)
                } else {
                    self.errorMessage = "Failed to update favorite status"
                    completion(false)
                }
            } catch {
                self.errorMessage = ErrorMessageParser.parse(error.localizedDescription)
                completion(false)
            }
        }
    }

    func clearMessages() {
        errorMessage = nil
        successMessage = nil
    }

    // MARK: - Private Helpers

    /// Silently reload the selected contractor detail without showing loading state.
    /// Used when the contractors list updates and we need to keep selectedContractor fresh.
    private func reloadSelectedContractorQuietly(id: Int32) {
        Task {
            do {
                let result = try await APILayer.shared.getContractor(id: id, forceRefresh: true)
                if let success = result as? ApiResultSuccess<Contractor> {
                    self.selectedContractor = success.data
                }
            } catch {
                // Silently ignore — this is a background refresh, not user-initiated
            }
        }
    }
}
