/**
 * SentryAgentError hierarchy.
 * All custom errors extend SentryAgentError.
 * Error-to-HTTP-status mapping is handled exclusively in errorHandler.ts.
 */

/**
 * Base class for all SentryAgent.ai custom errors.
 * Carry a machine-readable `code`, HTTP status, and optional structured details.
 */
export class SentryAgentError extends Error {
  /**
   * @param message - Human-readable error description.
   * @param code - Machine-readable error code.
   * @param httpStatus - HTTP status code to return.
   * @param details - Optional structured detail map.
   */
  constructor(
    message: string,
    public readonly code: string,
    public readonly httpStatus: number,
    public readonly details?: Record<string, unknown>,
  ) {
    super(message);
    this.name = this.constructor.name;
    // Restore prototype chain for instanceof checks
    Object.setPrototypeOf(this, new.target.prototype);
  }
}

/** 400 — Request failed validation. */
export class ValidationError extends SentryAgentError {
  constructor(message: string, details?: Record<string, unknown>) {
    super(message, 'VALIDATION_ERROR', 400, details);
  }
}

/** 404 — Referenced agent was not found. */
export class AgentNotFoundError extends SentryAgentError {
  constructor(agentId?: string) {
    super(
      'Agent with the specified ID was not found.',
      'AGENT_NOT_FOUND',
      404,
      agentId ? { agentId } : undefined,
    );
  }
}

/** 409 — Agent with this email already exists. */
export class AgentAlreadyExistsError extends SentryAgentError {
  constructor(email: string) {
    super(
      'An agent with this email address is already registered.',
      'AGENT_ALREADY_EXISTS',
      409,
      { email },
    );
  }
}

/** 404 — Referenced credential was not found. */
export class CredentialNotFoundError extends SentryAgentError {
  constructor(credentialId?: string) {
    super(
      'Credential with the specified ID was not found.',
      'CREDENTIAL_NOT_FOUND',
      404,
      credentialId ? { credentialId } : undefined,
    );
  }
}

/** 409 — Credential is already revoked. */
export class CredentialAlreadyRevokedError extends SentryAgentError {
  constructor(credentialId: string, revokedAt: string) {
    super(
      'This credential has already been revoked.',
      'CREDENTIAL_ALREADY_REVOKED',
      409,
      { credentialId, revokedAt },
    );
  }
}

/** 409 — Agent is already decommissioned. */
export class AgentAlreadyDecommissionedError extends SentryAgentError {
  constructor(agentId: string) {
    super(
      'This agent has already been decommissioned.',
      'AGENT_ALREADY_DECOMMISSIONED',
      409,
      { agentId },
    );
  }
}

/** 400 — Credential operation error (e.g. agent not active). */
export class CredentialError extends SentryAgentError {
  constructor(message: string, code: string, details?: Record<string, unknown>) {
    super(message, code, 400, details);
  }
}

/** 401 — Authentication failed (missing or invalid token). */
export class AuthenticationError extends SentryAgentError {
  constructor(message = 'A valid Bearer token is required to access this resource.') {
    super(message, 'UNAUTHORIZED', 401);
  }
}

/** 403 — Authorisation failed (insufficient permissions). */
export class AuthorizationError extends SentryAgentError {
  constructor(message = 'You do not have permission to perform this action.') {
    super(message, 'FORBIDDEN', 403);
  }
}

/** 429 — Rate limit exceeded. */
export class RateLimitError extends SentryAgentError {
  constructor() {
    super(
      'Too many requests. Please retry after the rate limit window resets.',
      'RATE_LIMIT_EXCEEDED',
      429,
    );
  }
}

/** 403 — Free tier resource limit reached. */
export class FreeTierLimitError extends SentryAgentError {
  constructor(message: string, details?: Record<string, unknown>) {
    super(message, 'FREE_TIER_LIMIT_EXCEEDED', 403, details);
  }
}

/** 403 — Token does not have the required scope. */
export class InsufficientScopeError extends SentryAgentError {
  constructor(requiredScope: string) {
    super(
      `The '${requiredScope}' scope is required to access this resource.`,
      'INSUFFICIENT_SCOPE',
      403,
    );
  }
}

/** 404 — Audit event not found. */
export class AuditEventNotFoundError extends SentryAgentError {
  constructor(eventId?: string) {
    super(
      'Audit event with the specified ID was not found.',
      'AUDIT_EVENT_NOT_FOUND',
      404,
      eventId ? { eventId } : undefined,
    );
  }
}

/** 400 — Requested date range exceeds audit log retention window. */
export class RetentionWindowError extends SentryAgentError {
  constructor(retentionDays: number, earliestAvailable: string) {
    super(
      `Free tier audit log retention is ${retentionDays} days. Requested date is outside the retention window.`,
      'RETENTION_WINDOW_EXCEEDED',
      400,
      { retentionDays, earliestAvailable },
    );
  }
}

/** 404 — Organization not found. */
export class OrgNotFoundError extends SentryAgentError {
  constructor(orgId?: string) {
    super('Organization not found.', 'ORG_NOT_FOUND', 404, orgId ? { orgId } : undefined);
  }
}

/** 409 — Organization has active agents; cannot delete. */
export class OrgHasActiveAgentsError extends SentryAgentError {
  constructor(orgId: string, activeCount: number) {
    super(
      'Organization has active agents; decommission all agents before deleting.',
      'ORG_HAS_ACTIVE_AGENTS',
      409,
      { orgId, activeCount },
    );
  }
}

/** 409 — Agent is already a member of this organization. */
export class AlreadyMemberError extends SentryAgentError {
  constructor(agentId: string, orgId: string) {
    super(
      'Agent is already a member of this organization.',
      'ALREADY_MEMBER',
      409,
      { agentId, orgId },
    );
  }
}

/** 429 — Tenant has exceeded a tier-based resource limit (agents, API calls, or tokens). */
export class TierLimitError extends SentryAgentError {
  /**
   * @param limitType - Human-readable name of the limit that was exceeded (e.g. 'agent', 'API call').
   * @param limit - The numeric limit that was reached.
   * @param details - Optional extra structured detail.
   */
  constructor(limitType: string, limit: number, details?: Record<string, unknown>) {
    super(
      `You have reached your ${limitType} limit of ${limit}. Upgrade your plan to increase this limit.`,
      'tier_limit_exceeded',
      429,
      { limitType, limit, ...details },
    );
  }
}
