package dto

import "time"

// Review is the API wire shape for a workspace review (git-compare target). In
// the single-table model a review context carries one optional thread and one
// source agent, projected as threadId/sourceAgentId for the Review Center.
type Review struct {
	ID            string    `json:"id"`
	WorkspaceID   string    `json:"workspaceId"`
	ProjectID     string    `json:"projectId,omitempty"`
	ThreadID      string    `json:"threadId,omitempty"`
	TaskScopeID   string    `json:"taskScopeId,omitempty"`
	SourceAgentID string    `json:"sourceAgentId,omitempty"`
	MachineID     string    `json:"machineId"`
	RootPath      string    `json:"rootPath"`
	BaseRef       string    `json:"baseRef,omitempty"`
	HeadRef       string    `json:"headRef,omitempty"`
	DisplayName   string    `json:"displayName"`
	PRURL         string    `json:"prUrl,omitempty"`
	CreatedAt     time.Time `json:"createdAt"`
}

// WorkspaceFile describes one daemon workspace/output-target file tree node.
type WorkspaceFile struct {
	Name           string     `json:"name"`
	Path           string     `json:"path"`
	IsDirectory    bool       `json:"isDirectory"`
	Size           int64      `json:"size,omitempty"`
	ModifiedAt     *time.Time `json:"modifiedAt,omitempty"`
	ChildDirCount  int        `json:"childDirCount,omitempty"`
	ChildFileCount int        `json:"childFileCount,omitempty"`
}

// WorkspaceFileContent is the daemon workspace/output-target file content response.
type WorkspaceFileContent struct {
	Content *string `json:"content"`
	Binary  bool    `json:"binary"`
}

// WorkspaceDirectory describes one scanned machine workspace directory.
type WorkspaceDirectory struct {
	DirectoryName  string    `json:"directoryName"`
	TotalSizeBytes int64     `json:"totalSizeBytes"`
	LastModified   time.Time `json:"lastModified"`
	FileCount      int64     `json:"fileCount"`
}

// AgentReviewSummary is the browser-facing git review summary response.
type AgentReviewSummary struct {
	Status               string    `json:"status"`
	Message              string    `json:"message,omitempty"`
	RepositoryRoot       string    `json:"repositoryRoot,omitempty"`
	Branch               string    `json:"branch,omitempty"`
	DefaultBranch        string    `json:"defaultBranch,omitempty"`
	BaseRef              string    `json:"baseRef,omitempty"`
	HeadRef              string    `json:"headRef,omitempty"`
	ComparisonMode       string    `json:"comparisonMode,omitempty"`
	AvailableRefs        []string  `json:"availableRefs,omitempty"`
	CompareRef           string    `json:"compareRef,omitempty"`
	AvailableCompareRefs []string  `json:"availableCompareRefs,omitempty"`
	HeadCommit           string    `json:"headCommit,omitempty"`
	MergeBaseCommit      string    `json:"mergeBaseCommit,omitempty"`
	AheadCount           int       `json:"aheadCount,omitempty"`
	BehindCount          int       `json:"behindCount,omitempty"`
	Dirty                bool      `json:"dirty"`
	BranchFileCount      int       `json:"branchFileCount,omitempty"`
	BranchAdditionCount  int       `json:"branchAdditionCount,omitempty"`
	BranchDeletionCount  int       `json:"branchDeletionCount,omitempty"`
	WorkingAdditionCount int       `json:"workingAdditionCount,omitempty"`
	WorkingDeletionCount int       `json:"workingDeletionCount,omitempty"`
	StagedFileCount      int       `json:"stagedFileCount,omitempty"`
	UnstagedFileCount    int       `json:"unstagedFileCount,omitempty"`
	LastScannedAt        time.Time `json:"lastScannedAt"`
}

// AgentReviewFile is one git review file row.
type AgentReviewFile struct {
	Path               string `json:"path"`
	OldPath            string `json:"oldPath,omitempty"`
	ChangeType         string `json:"changeType,omitempty"`
	Staged             bool   `json:"staged,omitempty"`
	Unstaged           bool   `json:"unstaged,omitempty"`
	StagedChangeType   string `json:"stagedChangeType,omitempty"`
	UnstagedChangeType string `json:"unstagedChangeType,omitempty"`
	// Token is the opaque per-file change-detection token. Equal tokens
	// imply the file's review-relevant state is unchanged; the frontend uses
	// it to skip re-fetching the per-file diff. Strength (authoritative vs
	// hint) follows the enclosing fingerprint's WorkingStrength.
	Token string `json:"token,omitempty"`
}

type AgentReviewSnapshot struct {
	Summary             AgentReviewSummary         `json:"summary"`
	Files               []AgentReviewFile          `json:"files"`
	WorkingFiles        []AgentReviewFile          `json:"workingFiles"`
	InitialDiffs        map[string]AgentReviewDiff `json:"initialDiffs,omitempty"`
	InitialWorkingDiffs map[string]AgentReviewDiff `json:"initialWorkingDiffs,omitempty"`
}

// AgentReviewFingerprint is the cheap polling shape the frontend hits on a
// low cadence. Equal token values imply no underlying change; the frontend
// only re-fetches the full snapshot when one of the tokens moves.
// WorkingStrength is "hint" — equal working tokens strongly suggest no
// change but cannot guarantee it (mtime is involved); the snapshot is the
// source of truth.
type AgentReviewFingerprint struct {
	HeadCommit        string    `json:"headCommit,omitempty"`
	BaseRef           string    `json:"baseRef,omitempty"`
	HeadRef           string    `json:"headRef,omitempty"`
	ComparisonMode    string    `json:"comparisonMode,omitempty"`
	RepositoryToken   string    `json:"repositoryToken,omitempty"`
	BranchToken       string    `json:"branchToken,omitempty"`
	WorkingToken      string    `json:"workingToken,omitempty"`
	WorkingStrength   string    `json:"workingStrength,omitempty"`
	ObservedAt        time.Time `json:"observedAt"`
	NextRefreshAt     time.Time `json:"nextRefreshAt"`
	WorkingStateFresh bool      `json:"workingStateFresh"`
	BranchFileCount   int       `json:"branchFileCount,omitempty"`
	StagedFileCount   int       `json:"stagedFileCount,omitempty"`
	UnstagedFileCount int       `json:"unstagedFileCount,omitempty"`
}

// AgentReviewDiffSection is one section in a git review diff.
type AgentReviewDiffSection struct {
	Key        string `json:"key"`
	Label      string `json:"label"`
	Diff       string `json:"diff,omitempty"`
	OldText    string `json:"oldText,omitempty"`
	NewText    string `json:"newText,omitempty"`
	ChangeType string `json:"changeType,omitempty"`
	Message    string `json:"message,omitempty"`
	Binary     bool   `json:"binary,omitempty"`
	TooLarge   bool   `json:"tooLarge,omitempty"`
}

// AgentReviewDiff is a browser-facing git review diff response.
type AgentReviewDiff struct {
	Status     string                   `json:"status"`
	Message    string                   `json:"message,omitempty"`
	Path       string                   `json:"path,omitempty"`
	OldPath    string                   `json:"oldPath,omitempty"`
	ChangeType string                   `json:"changeType,omitempty"`
	Sections   []AgentReviewDiffSection `json:"sections,omitempty"`
}

// ReviewDiffBatchRequest is the body for POST
// /reviews/:reviewId/diff/batch and POST
// /threads/:threadChannelId/review/diff/batch. The frontend uses these to
// fetch multiple file diffs in one round-trip instead of fanning out N
// parallel GETs.
//
// Paths are raw Git paths: leading/trailing whitespace and newlines are
// preserved verbatim (no normalization). The handler MUST NOT TrimSpace any
// entry — see webagent.ReviewHandler.Diff for the raw-path discipline
// rationale.
//
// ContextID selects a specific review context on the thread-bound endpoint,
// where a reply thread may have multiple review contexts bound to it (e.g.
// one per shared PR). The workspace-scoped endpoint ignores ContextID — it
// resolves the review context from the URL :reviewId parameter instead.
type ReviewDiffBatchRequest struct {
	Paths          []string `json:"paths"`
	ContextID      string   `json:"contextId,omitempty"`
	Scope          string   `json:"scope,omitempty"`
	BaseRef        string   `json:"baseRef,omitempty"`
	HeadRef        string   `json:"headRef,omitempty"`
	ComparisonMode string   `json:"comparisonMode,omitempty"`
}

// ReviewDiffBatchResponse is the response body for the batch diff endpoints.
//
// Diffs holds every path the backend was able to compute (including
// product-limitation payloads like unsupported_target, which are encoded as
// AgentReviewDiff with a stable Status). Errors holds a path-keyed message
// for paths that hit a non-diff-shaped failure (transient I/O, sentinel
// remap, etc.); path keys are the exact raw bytes the request carried.
type ReviewDiffBatchResponse struct {
	Diffs  map[string]AgentReviewDiff `json:"diffs"`
	Errors map[string]string          `json:"errors,omitempty"`
}

// --- Legacy wire shapes (backward compatibility for deployed clients) ---
// These map the new review model onto the shapes old desktop/web clients expect.

// ReviewTaskScope identifies a canonical Task scope for review contexts.
type ReviewTaskScope struct {
	Kind    string `json:"kind"`
	ScopeID string `json:"scopeId"`
}

// AgentReviewContextRequest is the legacy request body for an agent review target.
type AgentReviewContextRequest struct {
	Kind           string           `json:"kind"`
	RootPath       string           `json:"rootPath"`
	DisplayName    string           `json:"displayName"`
	HeadRef        string           `json:"headRef"`
	BaseRef        string           `json:"baseRef"`
	ComparisonMode string           `json:"comparisonMode"`
	PRURL          string           `json:"prUrl"`
	TaskScope      *ReviewTaskScope `json:"taskScope,omitempty"`
}

// ReviewContext is the wire shape for a thread-owned review context.
type ReviewContext struct {
	ID                string    `json:"id"`
	WorkspaceID       string    `json:"workspaceId"`
	ThreadID          string    `json:"threadId,omitempty"`
	TaskScopeID       string    `json:"taskScopeId,omitempty"`
	MachineID         string    `json:"machineId"`
	SourceAgentID     string    `json:"sourceAgentId"`
	Kind              string    `json:"kind"`
	RootPath          string    `json:"rootPath"`
	DisplayName       string    `json:"displayName"`
	HeadRef           string    `json:"headRef,omitempty"`
	BaseRef           string    `json:"baseRef,omitempty"`
	ComparisonMode    string    `json:"comparisonMode,omitempty"`
	PRURL             string    `json:"prUrl,omitempty"`
	ProjectID         string    `json:"projectId,omitempty"`
	CreatedByAgentID  string    `json:"createdByAgentId,omitempty"`
	UpdatedByAgentID  string    `json:"updatedByAgentId,omitempty"`
	CreatedByMemberID string    `json:"createdByMemberId,omitempty"`
	UpdatedByMemberID string    `json:"updatedByMemberId,omitempty"`
	CreatedAt         time.Time `json:"createdAt"`
	UpdatedAt         time.Time `json:"updatedAt"`
}

// AgentConversationReviewContextResponse returns a conversation-owned review context.
type AgentConversationReviewContextResponse struct {
	Context *ReviewContext `json:"context"`
}
