package mcpsrv
import (
"context"
"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)
// The seams this package calls, declared consumer-side — the house style
// web/deps.go sets — so that the tools can be driven by an in-process MCP client
// over fakes, with no Postgres and no store on disk (docs/DESIGN.mcp.md §12).
//
// They name exactly what the tools call, and what is *absent* from them is the
// design of docs/DESIGN.mcp.md §2 made structural rather than remembered:
//
// - No StoreManager. Nothing on this surface creates, initialises, evicts or
// deletes an on-disk store, so no handler here can. A write tool added later
// would have to widen this file first, which is a diff a reviewer sees.
// - No ACL mutation, no repository create/update/delete. Repos below is the
// read half of web's RepoStore and nothing more: a resolution, two listings
// and the caller's effective grant.
// - No key management. Dolt keys are a credential plane, and a surface reached
// with one credential has no business enumerating another.
// - No UserResolver. Resolving a username to a mirrored account is a write to
// the local user table on first sight; this surface reads.
// - No SQL engine anywhere. BrowseSession is the bare-store reader of
// browse/, which is the only reading of a hosted database that exists
// without a working set (docs/DESIGN.mcp.md §2, browse/open.go).
//
// The nil contract is the one every seam here shares and is not restated per
// method: a nil error means a usable result, no method returns (nil, nil) except
// EffectiveAccess — whose (nil, nil) *is* the answer "this caller holds no ACL
// entry", exactly as db.Store.EffectiveAccess defines it.
// Repos is the metadata store as this surface reads it: the repository rows and
// the caller's ACL grant on one of them.
//
// It is spelled with db.Store's own signatures, so *db.Store satisfies it as
// written and the production wiring binds it through the same request-scoped
// adapter web uses. There is no compile-time assertion here because that adapter
// is web's and unexported; the wiring commit is where the two meet.
//
// Visibility is *not* applied by these methods and must not be assumed from
// them. GetRepoByOwnerAndName answers about any repository that exists, and the
// caller here reproduces the browse dance — EffectiveAccess, core.Allowed,
// core.NotFoundForPrivate — exactly as web/router.go's loadRepoForBrowse does
// (docs/DESIGN.mcp.md §4.3). The two listings are the exception and say so
// themselves.
type Repos interface {
// GetRepoByOwnerAndName resolves a database by its owner's username (without
// the "~") and name, or reports db.ErrNotFound. It applies no visibility rule
// whatsoever: what the caller may see is decided afterwards, by core.Allowed
// over the grant EffectiveAccess returns.
GetRepoByOwnerAndName(ctx context.Context, ownerUsername, name string) (*core.Repo, error)
// ListReposByOwner lists one owner's databases that viewer may *list*, which
// is a narrower rule than "may read": PUBLIC to everyone including anonymity,
// plus anything viewer owns or holds an ACL entry on. An UNLISTED database of
// somebody else is therefore absent from the listing and still readable by
// direct address, which is the rule the dashboard already implements.
//
// viewer is nil for an anonymous caller, and anonymous is a normal caller
// here — it sees the PUBLIC ones.
ListReposByOwner(ctx context.Context, ownerUsername string, viewer *core.Caller) ([]*core.Repo, error)
// ListReposForDashboard lists every database userID owns or holds an ACL
// entry on, whatever its visibility. It is the signed-in caller's own view of
// the instance and takes no viewer argument because the user *is* the viewer.
ListReposForDashboard(ctx context.Context, userID int) ([]*core.Repo, error)
// EffectiveAccess resolves the caller's ACL grant on a repository, or
// (nil, nil) when there is none. Feed the result to core.Allowed; a nil grant
// is not a denial, it is a fall-through to visibility.
EffectiveAccess(ctx context.Context, userID, repoID int) (*core.AccessMode, error)
}
// BrowseSession is the read-only view of one bare store: the method set
// web/deps.go declares plus TableHash, so a *browse.DB satisfies both and the
// production adapter is the same one web uses.
//
// Everything here reads committed roots. There is no Write, no Commit and no
// working set to hold one — a bare NBS store has none (browse/open.go), which is
// what the whole pure-Go build stands on (docs/DESIGN.mcp.md §2).
type BrowseSession interface {
Branches(ctx context.Context) ([]browse.Branch, error)
Log(ctx context.Context, refStr, fromHash string, limit int) ([]browse.CommitInfo, string, error)
Tables(ctx context.Context, refStr string) ([]browse.TableInfo, error)
TableHash(ctx context.Context, refStr, table string) (string, bool, error)
Rows(ctx context.Context, refStr, table string, offset, limit int) (*browse.RowPage, error)
CommitSummary(ctx context.Context, hashStr string) (*browse.CommitDiff, error)
Close() error
}
// BrowseOpener opens a session over the bare store at diskPath.
//
// Open is paired with Close by the handler that called it (defer sess.Close()),
// which is the browse discipline and not an ornament: a session is one fresh
// read of the on-disk manifest, so a handler sees commits the push writer landed
// since the last call and nothing is cached between calls (browse/open.go).
type BrowseOpener interface {
Open(ctx context.Context, diskPath string) (BrowseSession, error)
}