import Foundation
import Combine
import WatchConnectivity
import RideFormCore

@MainActor
final class WatchSessionTransfer: NSObject, ObservableObject, WCSessionDelegate {
    @Published private(set) var status = "Watch transfers are local to your paired devices."
    @Published private(set) var preparing = false
    @Published private(set) var received: [URL] = []
    override init() {
        super.init()
        refresh()
        if WCSession.isSupported() { WCSession.default.delegate = self; WCSession.default.activate() }
    }
    nonisolated static var inbox: URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("WatchTransfers", isDirectory: true)
    }
    nonisolated static var outbox: URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("WatchOutbox", isDirectory: true)
    }
    nonisolated static func cleanOutbox(_ directory: URL, preserving pending: [URL]) throws {
        guard FileManager.default.fileExists(atPath: directory.path) else { return }
        let retained = Set(pending.map { $0.standardizedFileURL.path })
        for file in try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil)
        where file.pathExtension == "zip" && !retained.contains(file.standardizedFileURL.path) {
            try FileManager.default.removeItem(at: file)
        }
    }
    private func cleanCompletedTransfers() {
        // Preparation is not yet registered with WCSession; never remove its output.
        guard !preparing else { return }
        try? Self.cleanOutbox(Self.outbox, preserving: WCSession.default.outstandingFileTransfers.map { $0.file.fileURL })
    }
    func refresh() {
        received = ((try? FileManager.default.contentsOfDirectory(at: Self.inbox, includingPropertiesForKeys: nil)) ?? [])
            .filter { $0.pathExtension == "zip" }.sorted { $0.lastPathComponent > $1.lastPathComponent }
    }
    func send(bundle: URL) {
        guard !preparing, WCSession.isSupported(), WCSession.default.activationState == .activated else { status = "Open both apps and try again when the connection is ready."; return }
        #if os(watchOS)
        guard WCSession.default.isCompanionAppInstalled else { status = "Install RideForm on the paired iPhone first."; return }
        #endif
        preparing = true; status = "Preparing exact session archive…"
        Task {
            let result = await Task.detached { Result { () -> URL in
                let root = Self.outbox
                try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
                let archive = root.appendingPathComponent(UUID().uuidString + ".zip")
                try MasterArchive.write(bundle: bundle, to: archive)
                return archive
            } }.value
            preparing = false
            switch result {
            case .success(let file):
                WCSession.default.transferFile(file, metadata: ["rideformArchive": 1])
                status = "Queued for iPhone. Keep the original session until received."
            case .failure(let error): status = "Could not prepare transfer: \(error.localizedDescription)"
            }
            cleanCompletedTransfers()
        }
    }
    nonisolated func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        Task { @MainActor in
            if let error { self.status = "Connection unavailable: \(error.localizedDescription)" }
            if activationState == .activated { self.cleanCompletedTransfers() }
        }
    }
    nonisolated func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?) {
        Task { @MainActor in
            self.cleanCompletedTransfers()
            self.status = error.map { "Transfer failed: \($0.localizedDescription). The original is still saved; retry when connected." } ?? "Transfer delivered to paired device."
        }
    }
    nonisolated static func retainIncoming(_ source: URL, in directory: URL = inbox) throws -> URL {
        // The system deletes its temporary file after the delegate returns. Copy synchronously.
        // Never trust sender filenames or unpack incoming archives into session masters.
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        let target = directory.appendingPathComponent(UUID().uuidString + ".zip")
        try FileManager.default.copyItem(at: source, to: target)
        return target
    }
    nonisolated func session(_ session: WCSession, didReceive file: WCSessionFile) {
        guard file.metadata?["rideformArchive"] as? Int == 1 else { return }
        do {
            _ = try Self.retainIncoming(file.fileURL)
            Task { @MainActor in self.refresh(); self.status = "Watch archive received. Open Watch transfers to share it." }
        } catch { Task { @MainActor in self.status = "Could not retain Watch archive: \(error.localizedDescription). Send it again from the Watch." } }
    }
    #if os(iOS)
    nonisolated func sessionDidBecomeInactive(_ session: WCSession) {}
    nonisolated func sessionDidDeactivate(_ session: WCSession) { session.activate() }
    #endif
}
