// Package workspace 承载 Workspace / Worktree / GitOps 三个 service 的领域类型与
// 接口契约。沿 PM 模块（internal/project）的"单包多 service"风格：聚合在 schema
// 上紧耦合，分包反而要互相 import。
package workspace

import (
	"context"
	"time"

	"github.com/google/uuid"

	"github.com/yan1h/agent-coding-workflow/internal/infra/git"
)

// SyncStatus 与 DB CHECK 约束完全一致。
type SyncStatus string

// SyncStatus 枚举值，与 migrations/0003 的 CHECK 约束完全一致。
const (
	SyncStatusCloning SyncStatus = "cloning"
	SyncStatusIdle    SyncStatus = "idle"
	SyncStatusSyncing SyncStatus = "syncing"
	SyncStatusError   SyncStatus = "error"
)

// IsValid 报告 s 是否在枚举集合内。
func (s SyncStatus) IsValid() bool {
	switch s {
	case SyncStatusCloning, SyncStatusIdle, SyncStatusSyncing, SyncStatusError:
		return true
	}
	return false
}

// WorktreeStatus 与 DB CHECK 约束完全一致。
type WorktreeStatus string

// WorktreeStatus 枚举值，与 migrations/0003 的 CHECK 约束完全一致。
const (
	WorktreeStatusIdle    WorktreeStatus = "idle"
	WorktreeStatusActive  WorktreeStatus = "active"
	WorktreeStatusPruning WorktreeStatus = "pruning"
)

// CredentialKind 与 DB CHECK 约束完全一致；与 git.CredentialKind 同值，但分开
// 定义避免业务包对 infra/git 类型的强依赖。
type CredentialKind string

// CredentialKind 枚举值，与 migrations/0003 的 CHECK 约束完全一致。
const (
	CredKindPAT    CredentialKind = "pat"
	CredKindSSHKey CredentialKind = "ssh_key"
	CredKindNone   CredentialKind = "none"
)

// IsValid 报告 k 是否在枚举集合内。
func (k CredentialKind) IsValid() bool {
	return k == CredKindPAT || k == CredKindSSHKey || k == CredKindNone
}

// Workspace 是顶层聚合根。
type Workspace struct {
	ID            uuid.UUID
	ProjectID     uuid.UUID
	Slug          string
	Name          string
	Description   string
	GitRemoteURL  string
	DefaultBranch string
	MainPath      string
	SyncStatus    SyncStatus
	LastSyncedAt  *time.Time
	LastSyncError string
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

// Worktree 表示一个支线 worktree（不含 main_path）。
type Worktree struct {
	ID             uuid.UUID
	WorkspaceID    uuid.UUID
	Branch         string
	Path           string
	Status         WorktreeStatus
	ActiveHolder   string // "user:<uid>" / "session:<sid>" / 空
	AcquiredAt     *time.Time
	LastUsedAt     time.Time
	PruneWarningAt *time.Time
	CreatedAt      time.Time
}

// Credential 是 git_credentials 表的领域投影。Secret 仅在创建/更新时有值，
// 读取路径上 Service 永远不返回明文 secret。Fingerprint 是给前端展示的脱敏串。
type Credential struct {
	ID          uuid.UUID
	WorkspaceID uuid.UUID
	Kind        CredentialKind
	Username    string
	Secret      []byte // 写路径填明文；读路径恒为 nil
	Fingerprint string
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

// FetchTarget 是 workspace 包暴露给 jobs 模块的"周期 fetch 目标"投影。
// owner_id 通过 JOIN projects 取，避免 workspaces 表加冗余列。
type FetchTarget struct {
	ID       uuid.UUID
	OwnerID  uuid.UUID
	Name     string
	MainPath string
}

// Caller 表示发起请求的用户上下文。Service 据此判定鉴权与 holder 字符串。
// SessionID 非 nil 时，Holder() 返回 "session:<uuid>"（ACP 模块用），
// 否则返回 "user:<uuid>"。
type Caller struct {
	UserID    uuid.UUID
	IsAdmin   bool
	SessionID *uuid.UUID
}

// Holder 把 Caller 折成 acquire_holder 字符串。
//   - SessionID 非 nil → "session:<sid>"（ACP supervisor）
//   - 否则 → "user:<uid>"（普通 HTTP 调用）
func (c Caller) Holder() string {
	if c.SessionID != nil {
		return "session:" + c.SessionID.String()
	}
	return "user:" + c.UserID.String()
}

// ===== Service 接口 =====

// WorkspaceService 暴露 Workspace 聚合根的应用服务方法。
//
//nolint:revive // 名字带聚合前缀比 "Service" 更准确：包内有 3 个并列服务接口
type WorkspaceService interface {
	Create(ctx context.Context, c Caller, projectSlug string, in CreateWorkspaceInput) (*Workspace, error)
	Get(ctx context.Context, c Caller, wsID uuid.UUID) (*Workspace, error)
	List(ctx context.Context, c Caller, projectSlug string) ([]*Workspace, error)
	Update(ctx context.Context, c Caller, wsID uuid.UUID, in UpdateWorkspaceInput) (*Workspace, error)
	Delete(ctx context.Context, c Caller, wsID uuid.UUID) error
	Sync(ctx context.Context, c Caller, wsID uuid.UUID) (*Workspace, error)

	UpsertCredential(ctx context.Context, c Caller, wsID uuid.UUID, in UpsertCredentialInput) (*Credential, error)
	GetCredentialMeta(ctx context.Context, c Caller, wsID uuid.UUID) (*Credential, error)
	ListBranches(ctx context.Context, c Caller, wsID uuid.UUID) ([]string, error)
}

// WorktreeService 暴露支线 worktree 的应用服务方法。
type WorktreeService interface {
	Create(ctx context.Context, c Caller, wsID uuid.UUID, in CreateWorktreeInput) (*Worktree, error)
	List(ctx context.Context, c Caller, wsID uuid.UUID) ([]*Worktree, error)
	Delete(ctx context.Context, c Caller, wtID uuid.UUID) error
	Acquire(ctx context.Context, c Caller, wtID uuid.UUID) (*Worktree, error)
	Release(ctx context.Context, c Caller, wtID uuid.UUID) (*Worktree, error)
	// ReleaseByHolder 按 holder 字符串释放所有该 holder 持有的 worktree。
	// 由 ACP startup reaper / supervisor.onExit 在不知道 wtID 时使用。
	// 不写 audit（caller 自己写 — audit 上下文是 caller 知道的）。
	ReleaseByHolder(ctx context.Context, holder string) ([]*Worktree, error)
}

// GitOpsService 暴露 status/commit/push 操作。target 既可以是 Workspace（main_path）
// 也可以是 Worktree。
type GitOpsService interface {
	StatusOnMain(ctx context.Context, c Caller, wsID uuid.UUID) ([]git.FileStatus, error)
	CommitOnMain(ctx context.Context, c Caller, wsID uuid.UUID, in CommitInput) (string, error)
	PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID) error
	LogOnMain(ctx context.Context, c Caller, wsID uuid.UUID, n int) ([]git.Commit, error)
	DiffOnMain(ctx context.Context, c Caller, wsID uuid.UUID, opts git.DiffOptions) (git.DiffResult, error)

	StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error)
	CommitOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, in CommitInput) (string, error)
	PushOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) error
	LogOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, n int) ([]git.Commit, error)
	DiffOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, opts git.DiffOptions) (git.DiffResult, error)
}

// ===== Input DTO =====

// CreateWorkspaceInput 携带创建 Workspace 与其凭据的字段。
type CreateWorkspaceInput struct {
	Slug          string
	Name          string
	Description   string
	GitRemoteURL  string
	DefaultBranch string
	Credential    UpsertCredentialInput
}

// UpdateWorkspaceInput 是 patch 输入：nil = 保持原值。
type UpdateWorkspaceInput struct {
	Name          *string
	Description   *string
	DefaultBranch *string
}

// UpsertCredentialInput 创建或替换 git 凭据。Secret 是明文（PAT token / SSH 私钥 PEM）。
type UpsertCredentialInput struct {
	Kind     CredentialKind
	Username string // PAT 时填
	Secret   []byte // PAT/SSH 时填；None 时为空
}

// CreateWorktreeInput 创建支线 worktree。BaseBranch 留空时用 workspace.DefaultBranch。
type CreateWorktreeInput struct {
	Branch     string
	BaseBranch string
}

// CommitInput 提交描述。AddAll 留 false 等价于"已经手动 git add"，默认 service
// 调用方传 true（与 4.4 端点 default 对齐）。
type CommitInput struct {
	Message string
	Push    bool
	AddAll  bool
}
