namespace Nexus.Api.Data;

/// <summary>
/// Durable, secret-free proposal for creating one OpenClaw-owned agent.
/// Nexus owns the approval workflow only; the resulting agent remains owned by
/// OpenClaw.
/// </summary>
public sealed class AgentProposal
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public required string Source { get; set; }
    public required string RequestedName { get; set; }
    public required string RequestedAgentId { get; set; }
    public string? Role { get; set; }
    public string? Description { get; set; }
    public string? Model { get; set; }
    public string? Emoji { get; set; }
    public string? Avatar { get; set; }
    public required string Workspace { get; set; }
    public required string StandardFilesJson { get; set; }
    public required string StandardFilesHash { get; set; }
    public string Status { get; set; } = AgentProposalStates.AwaitingApproval;
    public required string RequestedBy { get; set; }
    public string? ApprovedBy { get; set; }
    public string? RejectedBy { get; set; }
    public string? RejectionReason { get; set; }
    public string? OpenClawAgentId { get; set; }
    public string? OpenClawWorkspace { get; set; }
    public string? LastErrorCode { get; set; }
    public string? LastErrorMessage { get; set; }
    public int Revision { get; set; } = 1;
    public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset? ApprovedAt { get; set; }
    public DateTimeOffset? RejectedAt { get; set; }
    public DateTimeOffset? CompletedAt { get; set; }
    public ICollection<AgentProvisionRequest> ProvisionRequests { get; set; } =
        new List<AgentProvisionRequest>();
}

/// <summary>
/// One explicitly authorized provisioning attempt. A request that reached the
/// dispatch boundary is never silently re-queued after a restart.
/// </summary>
public sealed class AgentProvisionRequest
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public Guid ProposalId { get; set; }
    public AgentProposal Proposal { get; set; } = null!;
    public int Attempt { get; set; }
    public required string Stage { get; set; }
    public string Status { get; set; } = AgentProvisionRequestStates.Queued;
    public required string IdempotencyKeyHash { get; set; }
    public required string Actor { get; set; }
    public required string CorrelationId { get; set; }
    public string? TraceParent { get; set; }
    public string? OpenClawAgentId { get; set; }
    public string? LastErrorCode { get; set; }
    public string? LastErrorMessage { get; set; }
    public string? LeaseOwner { get; set; }
    public DateTimeOffset? LeaseUntil { get; set; }
    public int Revision { get; set; } = 1;
    public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset? DispatchStartedAt { get; set; }
    public DateTimeOffset? CompletedAt { get; set; }
}

/// <summary>
/// Hash-only idempotency claim. Raw idempotency keys and request content are
/// deliberately not persisted.
/// </summary>
public sealed class OperationClaim
{
    public Guid Id { get; init; } = Guid.NewGuid();
    public required string Operation { get; set; }
    public required string IdempotencyKeyHash { get; set; }
    public required string RequestHash { get; set; }
    public Guid? ResourceId { get; set; }
    public required string State { get; set; }
    public string? ResultCode { get; set; }
    public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset? CompletedAt { get; set; }
    public DateTimeOffset ExpiresAt { get; set; } =
        DateTimeOffset.UtcNow.AddDays(7);
}

/// <summary>
/// Transactional, content-minimized domain event. The payload may contain
/// identifiers and states, but never proposal markdown or credentials.
/// </summary>
public sealed class OutboxEvent
{
    public long Sequence { get; init; }
    public Guid EventId { get; init; } = Guid.NewGuid();
    public required string Type { get; set; }
    public required string AggregateType { get; set; }
    public required string AggregateId { get; set; }
    public int AggregateRevision { get; set; }
    public required string PayloadJson { get; set; }
    public DateTimeOffset OccurredAt { get; set; } = DateTimeOffset.UtcNow;
    public DateTimeOffset? PublishedAt { get; set; }
    public int PublishAttempts { get; set; }
    public string? LastErrorCode { get; set; }
}

public static class AgentProposalStates
{
    public const string Draft = "draft";
    public const string AwaitingApproval = "awaiting_approval";
    public const string Provisioning = "provisioning";
    public const string Ready = "ready";
    public const string Partial = "partial";
    public const string Failed = "failed";
    public const string InDoubt = "in_doubt";
    public const string Rejected = "rejected";
}

public static class AgentProvisionStages
{
    public const string CreateAgent = "create_agent";
    public const string ReconcileAgent = "reconcile_agent";
    public const string FinalizeFiles = "finalize_files";
}

public static class AgentProvisionRequestStates
{
    public const string Queued = "queued";
    public const string Dispatching = "dispatching";
    public const string Completed = "completed";
    public const string Failed = "failed";
    public const string Partial = "partial";
    public const string InDoubt = "in_doubt";
    public const string Blocked = "blocked";
}
