import AppKit
import Foundation
import TetherKit

/// macOS side: same TetherClient wire layer, but we CAN poll NSPasteboard
/// in the background — so clipboard changes auto-send without user action.
@MainActor @Observable
final class MacTetherStore {
    var serverURL: String = UserDefaults.standard.string(forKey: "serverURL") ?? "http://tether.lan:8765"
    var room: String = RoomIdentity.derive()
    var feed: [Message] = []
    var connected = false
    var lastError: String?
    var hasAttemptedConnect = false

    private let deviceID = Host.current().localizedName ?? UUID().uuidString
    private var client: TetherClient?
    private var receiveTask: Task<Void, Never>?
    private var pollTask: Task<Void, Never>?
    private var lastChangeCount = NSPasteboard.general.changeCount

    func connectIfSaved() {
        guard UserDefaults.standard.string(forKey: "serverURL") != nil else { return }
        connect()
    }

    func connect() {
        guard let base = URL(string: serverURL.trimmingCharacters(in: .whitespaces)) else {
            hasAttemptedConnect = true
            lastError = "Invalid server URL."
            return
        }
        hasAttemptedConnect = true
        UserDefaults.standard.set(serverURL, forKey: "serverURL")
        disconnect()

        let source = "macos-\(ProcessInfo.processInfo.hostName)"
        let cfg = TetherClient.Config(baseURL: base, room: room,
                                      from: deviceID, source: "macos-app")
        let c = TetherClient(config: cfg)
        client = c
        connected = true
        lastError = nil

        // Receive: clipboard messages from peers → write to NSPasteboard.
        receiveTask = Task {
            for await msg in await c.clipboardStream() {
                feed.insert(msg, at: 0)
                if feed.count > 100 { feed.removeLast() }
                if let text = msg.text {
                    NSPasteboard.general.clearContents()
                    NSPasteboard.general.setString(text, forType: .string)
                    lastChangeCount = NSPasteboard.general.changeCount // don't echo back
                }
            }
            connected = false
        }

        // Send: poll NSPasteboard for local changes → push to room.
        lastChangeCount = NSPasteboard.general.changeCount
        pollTask = Task {
            while !Task.isCancelled {
                try? await Task.sleep(for: .milliseconds(500))
                let count = NSPasteboard.general.changeCount
                guard count != lastChangeCount else { continue }
                lastChangeCount = count
                if let text = NSPasteboard.general.string(forType: .string), !text.isEmpty {
                    try? await c.send(text)
                }
            }
        }
    }

    func disconnect() {
        receiveTask?.cancel(); receiveTask = nil
        pollTask?.cancel(); pollTask = nil
        connected = false
    }

    func send(_ text: String) {
        guard let c = client else { return }
        Task {
            do { try await c.send(text) }
            catch { lastError = "Send failed: \(error.localizedDescription)" }
        }
    }

    func copyToPasteboard(_ msg: Message) {
        guard let text = msg.text else { return }
        NSPasteboard.general.clearContents()
        NSPasteboard.general.setString(text, forType: .string)
        lastChangeCount = NSPasteboard.general.changeCount
    }
}
