package db

import "time"

// SyncGitHubConnection is process-level metadata for one GitHub App
// installation. It never grants picker or binding-creation access by itself;
// user authorization lives in SyncGitHubUserConnection and
// SyncGitHubUserInstallation. The App private key and installation tokens are
// process-owned configuration and are never stored in this row.
type SyncGitHubConnection struct {
	ID uint `gorm:"primaryKey;autoIncrement"`

	ConnectionID        string `gorm:"size:255;not null;uniqueIndex"`
	InstallationID      int64  `gorm:"not null;uniqueIndex"`
	AccountLogin        string `gorm:"size:255;not null"`
	AccountType         string `gorm:"size:64"`
	AccountAvatarURL    string `gorm:"size:2048"`
	ManagementURL       string `gorm:"size:2048"`
	RepositorySelection string `gorm:"size:32"`
	PermissionsJSON     LargeText
	Status              string `gorm:"size:32;not null;default:'active';index"`
	ConnectedByID       uint   `gorm:"index;not null"`
	ConnectedBy         User   `gorm:"foreignKey:ConnectedByID"`
	LastValidatedAt     *time.Time
	CreatedAt           time.Time
	UpdatedAt           time.Time
}

// SyncGitHubUserConnection records the GitHub identity that one AGS user
// explicitly authorized. GitHub App installation credentials remain
// process-owned; this row is only the user-scoped authorization boundary used
// by the setup UI and binding creation APIs.
type SyncGitHubUserConnection struct {
	ID uint `gorm:"primaryKey;autoIncrement"`

	UserID          uint   `gorm:"not null;uniqueIndex"`
	User            User   `gorm:"foreignKey:UserID"`
	GitHubUserID    int64  `gorm:"not null;index"`
	GitHubLogin     string `gorm:"size:255;not null"`
	GitHubAvatarURL string `gorm:"size:2048"`
	Status          string `gorm:"size:32;not null;default:'active';index"`
	LastValidatedAt *time.Time
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

// SyncGitHubUserInstallation grants one AGS user's connected GitHub identity
// access to one process-owned GitHub App installation. The composite unique
// index prevents one user connection from receiving the same installation
// more than once while allowing different AGS users to authorize it
// independently.
type SyncGitHubUserInstallation struct {
	ID uint `gorm:"primaryKey;autoIncrement"`

	UserConnectionID uint                     `gorm:"not null;uniqueIndex:idx_sync_github_user_installation,priority:1;index"`
	UserConnection   SyncGitHubUserConnection `gorm:"foreignKey:UserConnectionID"`
	ConnectionID     string                   `gorm:"size:255;not null;uniqueIndex:idx_sync_github_user_installation,priority:2;index"`
	InstallationID   int64                    `gorm:"not null;index"`
	CreatedAt        time.Time
	UpdatedAt        time.Time
}

// SyncGitHubConnectionRequest is the short-lived, one-time state used to bind
// GitHub's public callback to the authenticated AGS user who started it. Only
// a SHA-256 hash of the browser-visible state is persisted.
type SyncGitHubConnectionRequest struct {
	ID uint `gorm:"primaryKey;autoIncrement"`

	StateHash                string `gorm:"size:64;not null;uniqueIndex"`
	RequestedByID            uint   `gorm:"index;not null"`
	RequestedBy              User   `gorm:"foreignKey:RequestedByID"`
	ReturnTo                 string `gorm:"size:1024;not null"`
	Flow                     string `gorm:"size:32;not null;default:'authorize';index"`
	InstallationBaselineJSON LargeText
	Status                   string    `gorm:"size:32;not null;default:'pending';index"`
	ExpiresAt                time.Time `gorm:"index;not null"`
	CompletedAt              *time.Time
	CreatedAt                time.Time
	UpdatedAt                time.Time
}

// SyncBinding connects exactly one AGS repository with one external repository.
// Side a is the AGS endpoint and side b is the external endpoint in v1. Only
// AuthoritySide is writable by users; the other side is managed by the sync
// worker.
type SyncBinding struct {
	ID uint `gorm:"primaryKey;autoIncrement"`

	Status         string `gorm:"size:32;not null;default:'active';index"`
	AuthoritySide  string `gorm:"size:1;not null;default:'a'"`
	AuthorityEpoch uint64 `gorm:"not null;default:1"`

	ScheduleMode string     `gorm:"size:32;not null;default:'manual'"`
	ScheduleCron string     `gorm:"size:255"`
	SyncEnabled  bool       `gorm:"not null;default:false;index"`
	NextRunAt    *time.Time `gorm:"index"`
	// VisibilityConfirmation is a hash bound to both immutable repositories,
	// direction, and current visibility values. It is intentionally not a
	// blanket acknowledgement for future visibility changes.
	VisibilityConfirmation string `gorm:"size:128"`
	// RefConflictPolicy controls an explicitly confirmed first-baseline branch
	// collision. The default always preserves a divergent unmanaged target ref.
	RefConflictPolicy string `gorm:"size:32;not null;default:'preserve'"`

	LeaseOwner      string     `gorm:"size:255;index"`
	LeaseGeneration uint64     `gorm:"not null;default:0"`
	LeaseExpiresAt  *time.Time `gorm:"index"`

	LastRunID          *uint `gorm:"index"`
	LastConvergedRunID *uint `gorm:"index"`
	LastSyncAt         *time.Time
	LastFullAuditAt    *time.Time `gorm:"index"`

	CreatedByID uint                  `gorm:"index;not null"`
	CreatedBy   User                  `gorm:"foreignKey:CreatedByID"`
	Endpoints   []SyncBindingEndpoint `gorm:"foreignKey:BindingID"`
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

// SyncBindingEndpoint stores the immutable identity for one side of a binding.
// ActiveClaimKey is nullable so a closed binding can release an endpoint while
// keeping its historical identity.
type SyncBindingEndpoint struct {
	BindingID uint        `gorm:"primaryKey;autoIncrement:false"`
	Binding   SyncBinding `gorm:"foreignKey:BindingID"`
	Side      string      `gorm:"primaryKey;size:1;autoIncrement:false"`

	Provider           string  `gorm:"size:32;not null;index:idx_sync_endpoint_identity,priority:1"`
	RepositoryID       string  `gorm:"size:255;not null;index:idx_sync_endpoint_identity,priority:2"`
	RepositoryFullName string  `gorm:"size:512;not null"`
	ConnectionID       string  `gorm:"size:512"`
	AGSRepositoryID    *uint   `gorm:"index"`
	ActiveClaimKey     *string `gorm:"size:512;uniqueIndex"`

	CreatedAt time.Time
	UpdatedAt time.Time
}

// SyncRun is one immutable-direction attempt to converge the standby endpoint.
type SyncRun struct {
	ID        uint        `gorm:"primaryKey;autoIncrement"`
	BindingID uint        `gorm:"index;not null"`
	Binding   SyncBinding `gorm:"foreignKey:BindingID"`

	Kind                          string `gorm:"size:32;not null;default:'sync'"`
	Mode                          string `gorm:"size:32;not null;default:'apply'"`
	AuthorityEpoch                uint64 `gorm:"not null"`
	SourceSide                    string `gorm:"size:1;not null"`
	TargetSide                    string `gorm:"size:1;not null"`
	HandoffToSide                 string `gorm:"size:1"`
	Status                        string `gorm:"size:64;not null;default:'queued';index"`
	Phase                         string `gorm:"size:64;not null;default:'sync'"`
	Stage                         string `gorm:"size:64;not null;default:'queued';index"`
	PlannerMode                   string `gorm:"size:32;not null;default:'auto'"`
	AuditMode                     string `gorm:"size:32;not null;default:'checkpoint'"`
	MapperVersion                 string `gorm:"size:128"`
	VisibilityConfirmation        string `gorm:"size:128"`
	HandoffVisibilityConfirmation string `gorm:"size:128"`

	SourceInventoryDigest string `gorm:"size:128"`
	TargetInventoryDigest string `gorm:"size:128"`
	CursorJSON            LargeText
	CountersJSON          LargeText
	ReportJSON            LargeText
	LeaseGeneration       uint64 `gorm:"not null;default:0"`
	CancelRequested       bool   `gorm:"not null;default:false"`

	StartedAt   *time.Time
	CompletedAt *time.Time
	// LastProgressAt is updated by stage changes and successful lease
	// heartbeats so a watchdog can detect orphaned work safely.
	LastProgressAt *time.Time `gorm:"index"`
	// RetryAfterAt is set only for a safe, provider-directed pause such as a
	// GitHub API rate limit. The worker automatically reclaims the queued Run
	// after this instant; it is not a terminal error or a lost task.
	RetryAfterAt *time.Time `gorm:"index"`
	ErrorCode    string     `gorm:"size:128"`
	ErrorSummary LargeText
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

// SyncObjectLink connects the immutable identities of one logical object on
// both endpoints. Empty endpoint identities are NULL while create is pending.
type SyncObjectLink struct {
	ID        uint        `gorm:"primaryKey;autoIncrement"`
	BindingID uint        `gorm:"uniqueIndex:idx_sync_link_logical,priority:1;uniqueIndex:idx_sync_link_side_a,priority:1;uniqueIndex:idx_sync_link_side_b,priority:1;uniqueIndex:idx_sync_link_idempotency,priority:1;index"`
	Binding   SyncBinding `gorm:"foreignKey:BindingID"`

	LogicalObjectType string `gorm:"size:64;not null;uniqueIndex:idx_sync_link_logical,priority:2"`
	LogicalKey        string `gorm:"size:512;not null;uniqueIndex:idx_sync_link_logical,priority:3"`
	OriginSide        string `gorm:"size:1;not null"`

	SideAObjectType string  `gorm:"size:64;uniqueIndex:idx_sync_link_side_a,priority:2"`
	SideAID         *string `gorm:"size:255;uniqueIndex:idx_sync_link_side_a,priority:3"`
	SideANumber     *int
	SideAURL        string  `gorm:"size:1024"`
	SideARevision   string  `gorm:"size:255"`
	SideAHash       string  `gorm:"size:128"`
	SideBObjectType string  `gorm:"size:64;uniqueIndex:idx_sync_link_side_b,priority:2"`
	SideBID         *string `gorm:"size:255;uniqueIndex:idx_sync_link_side_b,priority:3"`
	SideBNumber     *int
	SideBURL        string `gorm:"size:1024"`
	SideBRevision   string `gorm:"size:255"`
	SideBHash       string `gorm:"size:128"`

	IdempotencyKey         string `gorm:"size:255;not null;uniqueIndex:idx_sync_link_idempotency,priority:2"`
	RepresentationA        string `gorm:"size:32"`
	RepresentationB        string `gorm:"size:32"`
	VerifiedMappingVersion string `gorm:"size:128;index"`
	LastVerifiedAt         *time.Time
	MetadataJSON           LargeText
	Status                 string `gorm:"size:32;not null;default:'active';index"`

	CreatedAt time.Time
	UpdatedAt time.Time
}

// SyncRunItem is the object-level intent, audit, idempotency, and recovery
// boundary. Workers may group compatible rows into physical batches without
// changing this logical state model.
type SyncRunItem struct {
	ID           uint           `gorm:"primaryKey;autoIncrement"`
	RunID        uint           `gorm:"uniqueIndex:idx_sync_run_item_key,priority:1;index"`
	Run          SyncRun        `gorm:"foreignKey:RunID"`
	ObjectLinkID *uint          `gorm:"index"`
	ObjectLink   SyncObjectLink `gorm:"foreignKey:ObjectLinkID"`

	Phase      string `gorm:"size:64;not null;uniqueIndex:idx_sync_run_item_key,priority:2"`
	ObjectType string `gorm:"size:64;not null;uniqueIndex:idx_sync_run_item_key,priority:3"`
	ObjectKey  string `gorm:"size:512;not null;uniqueIndex:idx_sync_run_item_key,priority:4"`
	Action     string `gorm:"size:32;not null;index"`
	State      string `gorm:"size:32;not null;default:'planned';index"`

	DependencyPhase        int
	PayloadBytes           int64
	SourceRevision         string `gorm:"size:255"`
	SourceHash             string `gorm:"size:128"`
	DesiredHash            string `gorm:"size:128"`
	ExpectedTargetRevision string `gorm:"size:255"`
	ExpectedTargetHash     string `gorm:"size:128"`
	IdempotencyKey         string `gorm:"size:255"`
	IntentJSON             LargeText
	AttemptCount           int    `gorm:"not null;default:0"`
	ErrorCode              string `gorm:"size:128"`
	ErrorMessage           LargeText

	CreatedAt   time.Time
	UpdatedAt   time.Time
	CompletedAt *time.Time
}
