import Foundation

/// Per-session scratch workspaces for ephemeral, out-of-project chats. Each ephemeral chat runs
/// the full agent (tools and all) in its own throwaway directory `<base>/<sessionID>` instead of a
/// project worktree — there's no branch, no worktree, and nothing to ship. The directory is
/// `git init`'d and seeded with an empty commit so the agent's Edit/diff/conflict tooling behaves
/// exactly as it does in a real worktree; if git init fails (an odd filesystem) the plain directory
/// is still returned so the agent can work, only diffs degrade.
///
/// Lifecycle is caller-driven (`AppStore`): `create` on session start, `remove` when a
/// truly-ephemeral chat's window closes or on the launch sweep that reclaims orphans.
/// User preference governing whether new ephemeral chats are kept (saved, project-less) or truly
/// discarded when their window closes. Backed by a single `UserDefaults` key so NucleicCore (which
/// seeds each session's choice at creation) and the SwiftUI Settings toggle read the same value.
public enum EphemeralChatPolicy {
    /// Whether new scratch chats are saved by default. Defaults to `true` (kept) when unset.
    public static let savedByDefaultKey = "nucleic.ephemeral.savedByDefault"

    public static var savedByDefault: Bool {
        UserDefaults.standard.object(forKey: savedByDefaultKey) as? Bool ?? true
    }
}

public enum EphemeralScratch {
    /// The scratch directory for a session, without touching the filesystem.
    public static func dir(base: URL, session: SessionID) -> URL {
        base.appendingPathComponent(session.rawValue, isDirectory: true)
    }

    /// Create (or reuse) the session's scratch directory and return its path. Best-effort
    /// `git init` + empty seed commit via the same `ProjectCloner` setup projects use, so the
    /// scratch dir is a valid repo; a git failure is swallowed so the agent still gets a usable dir.
    @discardableResult
    public static func create(
        base: URL, session: SessionID, git: GitRunner = GitRunner()
    ) async throws -> String {
        let dir = dir(base: base, session: session)
        try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
        // Only initialize a repo when the directory isn't one already (reuse on reopen).
        let isRepo = (try? await git.run(["rev-parse", "--is-inside-work-tree"], in: dir.path))?.ok == true
        if !isRepo {
            try? await ProjectCloner(git: git).initRepo(at: dir.path)
        }
        return dir.path
    }

    /// Remove a session's scratch directory and everything in it. Best-effort — a missing dir or a
    /// removal error is ignored (the launch sweep will retry orphans).
    public static func remove(base: URL, session: SessionID) {
        try? FileManager.default.removeItem(at: dir(base: base, session: session))
    }
}
