import AppIntents
import NucleicProtocol

/// A Nucleic agent session exposed to Siri / Shortcuts / Spotlight (docs/APP_INTENTS_OPPORTUNITIES
/// §4.6). Backed by the host's `SessionSummary` projection the phone already holds; the entity id
/// is the wire `SessionID`, so it maps straight onto the deep-link and control APIs.
///
/// A thin, `Sendable` value snapshot — never the live store row. The `EntityQuery` re-reads
/// `RemoteStore` each time so a stale Siri suggestion never acts on outdated state.
struct SessionEntity: AppEntity, Identifiable {
    static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Session")
    static let defaultQuery = SessionEntityQuery()

    /// `SessionID.rawValue`.
    var id: String
    var title: String
    var projectName: String
    var statusLabel: String
    /// Whether this session is waiting on the user (approval, or a non-completed input turn).
    var needsAttention: Bool

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "\(title)",
            subtitle: "\(projectName) · \(statusLabel)")
    }
}

extension SessionEntity {
    /// Project a wire summary into the entity (falling back to the project name for an untitled
    /// session, exactly as the session list does).
    init(_ s: WireSessionSummary) {
        self.init(
            id: s.sessionID.rawValue,
            title: s.title.isEmpty ? s.projectName : s.title,
            projectName: s.projectName,
            statusLabel: StatusStyle.label(s.status, disposition: s.disposition),
            needsAttention: s.status.needsYou(s.disposition))
    }
}

/// Resolves `SessionEntity` values for Shortcuts/Siri from the phone's current (or cached) session
/// list. Discovery paths (`suggestedEntities`, string matching) bootstrap the channel first so a
/// cold intent process still has data; id resolution stays fast and offline-tolerant.
struct SessionEntityQuery: EntityQuery {
    @MainActor
    func entities(for identifiers: [SessionEntity.ID]) async throws -> [SessionEntity] {
        let wanted = Set(identifiers)
        return RemoteStore.shared.liveSessions
            .filter { wanted.contains($0.sessionID.rawValue) }
            .map(SessionEntity.init)
    }

    @MainActor
    func suggestedEntities() async throws -> [SessionEntity] {
        let store = RemoteStore.shared
        store.bootstrapForIntent()
        return store.liveSessions
            .sorted(by: StatusStyle.attentionThenRecency)
            .prefix(12)
            .map(SessionEntity.init)
    }
}

/// Lets Siri match a session by spoken name ("open payment-flow") against title or project.
extension SessionEntityQuery: EntityStringQuery {
    @MainActor
    func entities(matching string: String) async throws -> [SessionEntity] {
        let store = RemoteStore.shared
        store.bootstrapForIntent()
        let needle = string.lowercased()
        return store.liveSessions
            .filter {
                let title = ($0.title.isEmpty ? $0.projectName : $0.title).lowercased()
                return title.contains(needle) || $0.projectName.lowercased().contains(needle)
            }
            .sorted(by: StatusStyle.attentionThenRecency)
            .map(SessionEntity.init)
    }
}
