/**
 * V2 Task Claim Type Definitions
 *
 * Claims represent an agent's active hold on a WorkItem from the Task Pool.
 * The claim system uses lease + heartbeat to ensure liveness:
 * - Lease: 10 minutes default, agent must renew before expiry
 * - Heartbeat: 2 minutes, agent signals it's still working
 * - Grace period: 3 minutes after lease expiry before forced release
 *
 * @module types/v2/claim.types
 */
import { v4 as uuidv4 } from 'uuid';
/** All valid ClaimStatus values. */
export const CLAIM_STATUSES = [
    'active',
    'expiring',
    'released',
    'revoked',
];
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/** Default lease duration: 10 minutes. */
export const DEFAULT_LEASE_DURATION_MS = 600_000;
/** Default heartbeat interval: 2 minutes. */
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 120_000;
/** Default grace period after lease expiry: 3 minutes. */
export const DEFAULT_GRACE_PERIOD_MS = 180_000;
/** Default maximum lease extensions. */
export const DEFAULT_MAX_EXTENSIONS = 3;
// ---------------------------------------------------------------------------
// Type Guards
// ---------------------------------------------------------------------------
/**
 * Checks whether a string is a valid ClaimStatus.
 *
 * @param value - The string to check
 * @returns True if value is a valid ClaimStatus
 */
export function isValidClaimStatus(value) {
    return CLAIM_STATUSES.includes(value);
}
/**
 * Validates that an unknown value is structurally a valid TaskClaim.
 *
 * @param value - Unknown value to validate
 * @returns True if value conforms to the TaskClaim interface
 */
export function isTaskClaim(value) {
    if (typeof value !== 'object' || value === null)
        return false;
    const obj = value;
    return (typeof obj.id === 'string' &&
        typeof obj.workItemId === 'string' &&
        typeof obj.agentId === 'string' &&
        typeof obj.status === 'string' &&
        isValidClaimStatus(obj.status) &&
        typeof obj.claimedAt === 'string' &&
        typeof obj.leaseExpiresAt === 'string' &&
        typeof obj.lastHeartbeatAt === 'string' &&
        typeof obj.extensionCount === 'number' &&
        typeof obj.maxExtensions === 'number' &&
        typeof obj.leaseDurationMs === 'number');
}
/**
 * Checks whether a claim's lease has expired (not counting grace period).
 *
 * @param claim - The claim to check
 * @param now - Current time (defaults to Date.now())
 * @returns True if the lease has expired
 */
export function isLeaseExpired(claim, now = Date.now()) {
    return now > new Date(claim.leaseExpiresAt).getTime();
}
/**
 * Checks whether a claim is past its grace period and should be revoked.
 *
 * @param claim - The claim to check
 * @param gracePeriodMs - Grace period duration in ms
 * @param now - Current time (defaults to Date.now())
 * @returns True if grace period has exceeded
 */
export function isGracePeriodExceeded(claim, gracePeriodMs = DEFAULT_GRACE_PERIOD_MS, now = Date.now()) {
    const leaseExpiry = new Date(claim.leaseExpiresAt).getTime();
    return now > leaseExpiry + gracePeriodMs;
}
/**
 * Checks whether a claim can be extended (has not exhausted max extensions).
 *
 * @param claim - The claim to check
 * @returns True if another extension is allowed
 */
export function canExtendLease(claim) {
    return claim.status === 'active' && claim.extensionCount < claim.maxExtensions;
}
// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------
/**
 * Validates CreateClaimInput and returns an array of error messages.
 *
 * @param input - The creation input to validate
 * @returns Array of validation error strings (empty = valid)
 */
export function validateCreateClaimInput(input) {
    const errors = [];
    if (!input.workItemId || typeof input.workItemId !== 'string') {
        errors.push('workItemId is required and must be a string');
    }
    if (!input.agentId || typeof input.agentId !== 'string') {
        errors.push('agentId is required and must be a string');
    }
    if (input.leaseDurationMs !== undefined) {
        if (input.leaseDurationMs < 60_000) {
            errors.push('leaseDurationMs must be at least 60000 (1 minute)');
        }
        if (input.leaseDurationMs > 3_600_000) {
            errors.push('leaseDurationMs must not exceed 3600000 (1 hour)');
        }
    }
    if (input.maxExtensions !== undefined && (input.maxExtensions < 0 || !Number.isInteger(input.maxExtensions))) {
        errors.push('maxExtensions must be a non-negative integer');
    }
    return errors;
}
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
/**
 * Creates a new TaskClaim with sensible defaults.
 *
 * @param input - Required and optional creation fields
 * @returns A fully populated TaskClaim object
 *
 * @example
 * ```typescript
 * const claim = createTaskClaim({
 *   workItemId: 'wi-abc-123',
 *   agentId: 'crewly-product-leo-member-n',
 * });
 * // claim.leaseExpiresAt will be 10 minutes from now
 * ```
 */
export function createTaskClaim(input) {
    const now = new Date();
    const leaseDuration = input.leaseDurationMs ?? DEFAULT_LEASE_DURATION_MS;
    const leaseExpiry = new Date(now.getTime() + leaseDuration);
    return {
        id: uuidv4(),
        workItemId: input.workItemId,
        agentId: input.agentId,
        status: 'active',
        claimedAt: now.toISOString(),
        leaseExpiresAt: leaseExpiry.toISOString(),
        lastHeartbeatAt: now.toISOString(),
        extensionCount: 0,
        maxExtensions: input.maxExtensions ?? DEFAULT_MAX_EXTENSIONS,
        leaseDurationMs: leaseDuration,
    };
}
/**
 * Extends a claim's lease by the original lease duration.
 * Returns a new claim object (immutable pattern).
 *
 * @param claim - The claim to extend
 * @returns New TaskClaim with extended lease, or null if extension not allowed
 */
export function extendClaim(claim) {
    if (!canExtendLease(claim))
        return null;
    const now = new Date();
    const newExpiry = new Date(now.getTime() + claim.leaseDurationMs);
    return {
        ...claim,
        leaseExpiresAt: newExpiry.toISOString(),
        lastHeartbeatAt: now.toISOString(),
        extensionCount: claim.extensionCount + 1,
    };
}
//# sourceMappingURL=claim.types.js.map