import Capacitor
import GameKit

/// GameKit Saved Games: load/save/list/delete by stable name. Conflicts resolve
/// most-recently-modified-wins. Requires Game Center auth; otherwise GKLocalPlayer
/// fails with `GKErrorNotAuthenticated`, forwarded to the caller.
internal class SavedGamesModule: PgsModule {
    private var player: GKLocalPlayer { GKLocalPlayer.local }

    func load(_ call: CAPPluginCall) {
        guard let name = call.getString("name") else {
            call.reject("missing name"); return
        }
        let loadingPlayer = player
        let ownerPlayerID = loadingPlayer.gamePlayerID
        loadingPlayer.fetchSavedGames { saved, error in
            if let error = error {
                call.reject(error.localizedDescription, nil, error)
                return
            }
            guard loadingPlayer.isAuthenticated, loadingPlayer.gamePlayerID == ownerPlayerID else {
                call.reject("Game Center player changed while loading snapshot")
                return
            }
            let matches = (saved ?? []).filter { $0.name == name }
            guard let pick = matches.max(by: { ($0.modificationDate ?? .distantPast) < ($1.modificationDate ?? .distantPast) }) else {
                call.resolve(["snapshot": NSNull()])
                return
            }
            let resolveConflicts: SavedGameConflictResolver? = matches.count > 1 ? { data, completion in
                guard loadingPlayer.isAuthenticated, loadingPlayer.gamePlayerID == ownerPlayerID else {
                    completion(savedGamePlayerChangedError())
                    return
                }
                loadingPlayer.resolveConflictingSavedGames(matches, with: data) { _, resolutionError in
                    completion(resolutionError)
                }
            } : nil
            loadSavedGameData(load: pick.loadData, resolveConflicts: resolveConflicts) { data, dataError in
                if let dataError = dataError {
                    call.reject(dataError.localizedDescription, nil, dataError)
                    return
                }
                guard loadingPlayer.isAuthenticated, loadingPlayer.gamePlayerID == ownerPlayerID else {
                    call.reject("Game Center player changed while loading snapshot")
                    return
                }
                guard let bytes = data else {
                    call.reject("snapshot data unavailable")
                    return
                }
                let payload: [String: Any] = [
                    "name": pick.name ?? name,
                    // GKSavedGame has no description field; empty for API parity.
                    "description": "",
                    "modifiedAt": Int((pick.modificationDate ?? Date()).timeIntervalSince1970 * 1000),
                    "data": String(data: bytes, encoding: .utf8) ?? "",
                ]
                call.resolve(["snapshot": payload])
            }
        }
    }

    func save(_ call: CAPPluginCall) {
        guard let name = call.getString("name") else {
            call.reject("missing name"); return
        }
        guard let data = call.getString("data") else {
            call.reject("missing data"); return
        }
        guard let expectedPlayerId = call.getString("expectedPlayerId"),
              !expectedPlayerId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            call.reject("expectedPlayerId is required"); return
        }
        let savingPlayer = player
        guard savingPlayer.isAuthenticated, savingPlayer.gamePlayerID == expectedPlayerId else {
            call.reject("Game Center player changed before snapshot write"); return
        }
        let bytes = Data(data.utf8)
        savingPlayer.saveGameData(bytes, withName: name) { _, error in
            if let error = error {
                call.reject(error.localizedDescription, nil, error)
            } else {
                call.resolve()
            }
        }
    }

    func list(_ call: CAPPluginCall) {
        player.fetchSavedGames { saved, error in
            if let error = error {
                call.reject(error.localizedDescription, nil, error)
                return
            }
            // Collapse cross-device conflicts to one entry per name.
            var byName: [String: GKSavedGame] = [:]
            for savedGame in saved ?? [] {
                let key = savedGame.name ?? ""
                if key.isEmpty { continue }
                if let existing = byName[key],
                   (existing.modificationDate ?? .distantPast) >= (savedGame.modificationDate ?? .distantPast) {
                    continue
                }
                byName[key] = savedGame
            }
            let snapshots = byName.values.map { savedGame -> [String: Any] in
                [
                    "name": savedGame.name ?? "",
                    "description": "",
                    "modifiedAt": Int((savedGame.modificationDate ?? Date()).timeIntervalSince1970 * 1000),
                ]
            }
            call.resolve(["snapshots": snapshots])
        }
    }

    func delete(_ call: CAPPluginCall) {
        guard let name = call.getString("name") else {
            call.reject("missing name"); return
        }
        player.deleteSavedGames(withName: name) { error in
            if let error = error {
                call.reject(error.localizedDescription, nil, error)
            } else {
                call.resolve()
            }
        }
    }
}

private func savedGamePlayerChangedError() -> NSError {
    NSError(
        domain: "PlayGames",
        code: 1,
        userInfo: [NSLocalizedDescriptionKey: "Game Center player changed while loading snapshot"]
    )
}
