// Package project defines persistence ports owned by the project catalog domain service.
package project

import (
	"context"
	"time"

	"github.com/google/uuid"

	"github.com/codex-k8s/kodex/services/internal/project-catalog/internal/domain/types/entity"
	"github.com/codex-k8s/kodex/services/internal/project-catalog/internal/domain/types/enum"
	"github.com/codex-k8s/kodex/services/internal/project-catalog/internal/domain/types/query"
)

// ServicesPolicyEventBuilder builds an outbox event after repository assigns the next policy version.
type ServicesPolicyEventBuilder func(entity.ServicesPolicy) (entity.OutboxEvent, error)

// Repository is the domain persistence contract for project-catalog use cases.
type Repository interface {
	// GetCommandResult returns a previously applied idempotent command result.
	GetCommandResult(ctx context.Context, identity query.CommandIdentity) (entity.CommandResult, error)
	// CreateProject stores a project, outbox event and command result atomically.
	CreateProject(ctx context.Context, project entity.Project, event entity.OutboxEvent, result entity.CommandResult) error
	// UpdateProject updates a project with optimistic concurrency.
	UpdateProject(ctx context.Context, project entity.Project, previousVersion int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetProject returns a project by id.
	GetProject(ctx context.Context, id uuid.UUID) (entity.Project, error)
	// ListProjects returns projects matching filter.
	ListProjects(ctx context.Context, filter query.ProjectFilter) ([]entity.Project, query.PageResult, error)
	// AttachRepository stores a repository binding, outbox event and command result atomically.
	AttachRepository(ctx context.Context, repository entity.RepositoryBinding, event entity.OutboxEvent, result entity.CommandResult) error
	// ReserveRepositoryBinding stores a pending repository binding and outbox event atomically.
	ReserveRepositoryBinding(ctx context.Context, repository entity.RepositoryBinding, event entity.OutboxEvent) error
	// UpdateRepository updates a repository binding with optimistic concurrency.
	UpdateRepository(ctx context.Context, repository entity.RepositoryBinding, previousVersion int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetRepository returns a repository binding by id.
	GetRepository(ctx context.Context, id uuid.UUID) (entity.RepositoryBinding, error)
	// GetRepositoryByProviderRef returns a non-archived repository binding by provider owner/name.
	GetRepositoryByProviderRef(ctx context.Context, provider enum.RepositoryProvider, owner string, name string) (entity.RepositoryBinding, error)
	// ListRepositories returns repository bindings matching filter.
	ListRepositories(ctx context.Context, filter query.RepositoryFilter) ([]entity.RepositoryBinding, query.PageResult, error)
	// RecordOnboardingSignalReconciliation stores safe project-side processing state for one onboarding signal.
	RecordOnboardingSignalReconciliation(ctx context.Context, signal entity.OnboardingSignalReconciliation) (entity.OnboardingSignalReconciliation, error)
	// ImportServicesPolicy stores checked policy, descriptors, documentation sources, outbox event and command result atomically.
	ImportServicesPolicy(ctx context.Context, policy entity.ServicesPolicy, descriptors []entity.ServiceDescriptor, documentationSources []entity.DocumentationSource, result entity.CommandResult, buildEvent ServicesPolicyEventBuilder) (entity.ServicesPolicy, error)
	// ImportBootstrapServicesPolicy stores checked onboarding policy and updates repository binding atomically.
	ImportBootstrapServicesPolicy(ctx context.Context, repository entity.RepositoryBinding, previousVersion int64, policy entity.ServicesPolicy, descriptors []entity.ServiceDescriptor, documentationSources []entity.DocumentationSource, repositoryEvent entity.OutboxEvent, result entity.CommandResult, buildPolicyEvent ServicesPolicyEventBuilder) (entity.ServicesPolicy, entity.RepositoryBinding, error)
	// GetServicesPolicy returns active or concrete checked services policy.
	GetServicesPolicy(ctx context.Context, projectID uuid.UUID, policyID *uuid.UUID) (entity.ServicesPolicy, error)
	// GetServicesPolicyBySource returns checked policy imported from a concrete source commit.
	GetServicesPolicyBySource(ctx context.Context, projectID uuid.UUID, sourceRepositoryID uuid.UUID, sourcePath string, sourceCommitSHA string) (entity.ServicesPolicy, error)
	// ListServiceDescriptors returns typed descriptors matching filter.
	ListServiceDescriptors(ctx context.Context, filter query.ServiceDescriptorFilter) ([]entity.ServiceDescriptor, query.PageResult, error)
	// CreatePolicyEditProposal stores a request to change services.yaml through provider PR.
	CreatePolicyEditProposal(ctx context.Context, proposal entity.PolicyEditProposal, result entity.CommandResult) error
	// GetPolicyEditProposal returns a policy edit proposal by id.
	GetPolicyEditProposal(ctx context.Context, id uuid.UUID) (entity.PolicyEditProposal, error)
	// CreatePolicyOverride stores an emergency override and its outbox event.
	CreatePolicyOverride(ctx context.Context, override entity.PolicyOverride, event entity.OutboxEvent, result entity.CommandResult) error
	// CancelPolicyOverride cancels an active emergency override with optimistic concurrency.
	CancelPolicyOverride(ctx context.Context, override entity.PolicyOverride, previousVersion int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetPolicyOverride returns a policy override by id without active-state filtering.
	GetPolicyOverride(ctx context.Context, id uuid.UUID) (entity.PolicyOverride, error)
	// ListPolicyOverrides returns operator overrides matching filter.
	ListPolicyOverrides(ctx context.Context, filter query.PolicyOverrideFilter) ([]entity.PolicyOverride, query.PageResult, error)
	// PutDocumentationSource creates or updates a documentation source with optimistic concurrency.
	PutDocumentationSource(ctx context.Context, source entity.DocumentationSource, previousVersion *int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetDocumentationSource returns a documentation source by id.
	GetDocumentationSource(ctx context.Context, id uuid.UUID) (entity.DocumentationSource, error)
	// ListDocumentationSources returns documentation sources matching filter.
	ListDocumentationSources(ctx context.Context, filter query.DocumentationSourceFilter) ([]entity.DocumentationSource, query.PageResult, error)
	// GetWorkspacePolicy returns allowed source set for an agent workspace.
	GetWorkspacePolicy(ctx context.Context, filter query.WorkspacePolicyFilter) (entity.WorkspacePolicy, error)
	// PutBranchRules creates or updates branch rules with optimistic concurrency.
	PutBranchRules(ctx context.Context, rules entity.BranchRules, previousVersion *int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetBranchRules returns branch rules by id.
	GetBranchRules(ctx context.Context, id uuid.UUID) (entity.BranchRules, error)
	// ListBranchRules returns branch rules matching filter.
	ListBranchRules(ctx context.Context, filter query.BranchRulesFilter) ([]entity.BranchRules, query.PageResult, error)
	// PutReleasePolicy creates or updates release policy with optimistic concurrency.
	PutReleasePolicy(ctx context.Context, policy entity.ReleasePolicy, previousVersion *int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetReleasePolicy returns release policy by id.
	GetReleasePolicy(ctx context.Context, id uuid.UUID) (entity.ReleasePolicy, error)
	// ListReleasePolicies returns release policies matching filter.
	ListReleasePolicies(ctx context.Context, filter query.ReleasePolicyFilter) ([]entity.ReleasePolicy, query.PageResult, error)
	// PutReleaseLine creates or updates release line with optimistic concurrency.
	PutReleaseLine(ctx context.Context, line entity.ReleaseLine, previousVersion *int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetReleaseLine returns release line by id.
	GetReleaseLine(ctx context.Context, id uuid.UUID) (entity.ReleaseLine, error)
	// ListReleaseLines returns release lines matching filter.
	ListReleaseLines(ctx context.Context, filter query.ReleaseLineFilter) ([]entity.ReleaseLine, query.PageResult, error)
	// PutPlacementPolicy creates or updates placement policy with optimistic concurrency.
	PutPlacementPolicy(ctx context.Context, policy entity.PlacementPolicy, previousVersion *int64, event entity.OutboxEvent, result *entity.CommandResult) error
	// GetPlacementPolicy returns placement policy by id.
	GetPlacementPolicy(ctx context.Context, id uuid.UUID) (entity.PlacementPolicy, error)
	// ListPlacementPolicies returns placement policies matching filter.
	ListPlacementPolicies(ctx context.Context, filter query.PlacementPolicyFilter) ([]entity.PlacementPolicy, query.PageResult, error)
	// ClaimOutboxEvents leases unpublished outbox events for delivery.
	ClaimOutboxEvents(ctx context.Context, limit int, now time.Time, lockedUntil time.Time) ([]entity.OutboxEvent, error)
	// MarkOutboxEventPublished marks a leased outbox event as published.
	MarkOutboxEventPublished(ctx context.Context, id uuid.UUID, attemptCount int, publishedAt time.Time) error
	// MarkOutboxEventFailed schedules a leased outbox event for retry.
	MarkOutboxEventFailed(ctx context.Context, id uuid.UUID, attemptCount int, nextAttemptAt time.Time, lastError string) error
	// MarkOutboxEventPermanentlyFailed moves a leased outbox event to terminal failure.
	MarkOutboxEventPermanentlyFailed(ctx context.Context, id uuid.UUID, attemptCount int, failedAt time.Time, lastError string) error
}

// Clock provides deterministic time for domain commands and tests.
type Clock interface {
	Now() time.Time
}

// IDGenerator provides aggregate and event identifiers for domain commands.
type IDGenerator interface {
	New() uuid.UUID
}
