import Combine
import Foundation

enum PairingMode: String, CaseIterable, Identifiable {
    case create = "Create Code"
    case join = "Enter Code"

    var id: Self { self }
}

@MainActor
final class PairingViewModel: ObservableObject {
    @Published var mode: PairingMode = .create {
        didSet {
            errorMessage = nil
            generatedCode = nil
            isWaitingForPeer = false
        }
    }
    @Published var displayName = ""
    @Published var code = "" {
        didSet {
            let uppercasedCode = code.uppercased()
            if code != uppercasedCode {
                code = uppercasedCode
            }
        }
    }
    @Published private(set) var generatedCode: String?
    @Published private(set) var errorMessage: String?
    @Published private(set) var isSubmitting = false
    @Published private(set) var isWaitingForPeer = false

    private let session: AppSession

    init(session: AppSession) {
        self.session = session
    }

    var canSubmit: Bool {
        guard !displayName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            return false
        }

        if mode == .join {
            return !code.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
        }

        return true
    }

    func createCode() async {
        guard canSubmit else {
            return
        }

        isSubmitting = true
        errorMessage = nil

        do {
            generatedCode = try await session.createPairingCode(displayName: displayName.trimmed)
            isWaitingForPeer = true
        } catch {
            errorMessage = message(for: error)
        }

        isSubmitting = false
    }

    func joinCode() async {
        guard canSubmit else {
            return
        }

        isSubmitting = true
        errorMessage = nil

        do {
            try await session.joinWithCode(normalizedCode, displayName: displayName.trimmed)
        } catch {
            errorMessage = message(for: error)
        }

        isSubmitting = false
    }

    var normalizedCode: String {
        Self.normalizedCode(from: code)
    }

    nonisolated static func normalizedCode(from code: String) -> String {
        let compactCode = code
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: " ", with: "")
            .uppercased()

        guard !compactCode.hasPrefix("FCHT-") else {
            return compactCode
        }

        return "FCHT-\(compactCode)"
    }

    private func message(for error: Error) -> String {
        if let error = error as? LocalizedError, let description = error.errorDescription {
            return description
        }

        return "Unable to complete pairing."
    }
}

private extension String {
    var trimmed: String {
        trimmingCharacters(in: .whitespacesAndNewlines)
    }
}
