// Cron types and interfaces

import type { SessionStore } from '../session/store.js';
import type {
  WorkflowRunInputEnvelope,
  WorkflowRunSource,
} from '../workflows/domain/index.js';
import type {
  StartWorkflowRunServiceParams,
  WorkflowRunServiceResult,
} from '../workflows/service/workflow-run-service.types.js';

// ============================================================================
// Delivery Types
// ============================================================================

export type CronDeliveryMode = 'none' | 'announce' | 'direct';

export interface CronDelivery {
  mode: CronDeliveryMode;
  channel?: string;  // 'telegram' | 'cli' | 'local'
  to?: string;       // recipient chat id (omit for `local`)
  bestEffort?: boolean;
}

// ============================================================================
// Payload Types
// ============================================================================

export type CronPayload =
  | { kind: 'systemEvent'; text: string }
  | { kind: 'agentTurn'; message: string; model?: string; timeoutSeconds?: number }
  | CronWorkflowRunPayload;

export interface CronWorkflowRunPayload {
  kind: 'workflowRun';
  definitionId: string;
  input?: unknown;
  inputEnvelope?: WorkflowRunInputEnvelope;
  goal?: string;
  agentId?: string;
  sessionKey?: string;
  source?: Partial<Extract<WorkflowRunSource, { kind: 'cron' }>>;
}

// ============================================================================
// Session Target
// ============================================================================

export type CronSessionTarget = 'main' | 'isolated';

// ============================================================================
// Job Data
// ============================================================================

export interface JobData {
  id: string;
  name?: string;
  schedule: string;
  enabled: boolean;
  timezone?: string;
  maxRetries: number;
  timeout: number;
  created_at: string;
  updated_at: string;
  sessionTarget?: CronSessionTarget;
  /** When set, isolated agent runs use this agent id in the session key (multi-agent). */
  agentId?: string;
  /**
   * Optional absolute workspace root for isolated agent runs (same semantics as chat working directory).
   * Omit to use the effective agent profile default workspace.
   */
  workingDirectory?: string;
  payload: CronPayload;
  delivery?: CronDelivery;
  model?: string;
  // Internal state
  state?: JobState;
}

export interface JobState {
  nextRunAtMs?: number;
  runningAtMs?: number;
  lastRunAtMs?: number;
  lastStatus?: 'ok' | 'error' | 'skipped';
  lastError?: string;
  lastDurationMs?: number;
  consecutiveErrors?: number;
  scheduleErrorCount?: number;
}

// ============================================================================
// Job Execution
// ============================================================================

export interface JobExecution {
  id: string;
  jobId: string;
  status: 'running' | 'success' | 'failed' | 'cancelled' | 'skipped';
  startedAt: string;
  endedAt?: string;
  duration?: number; // milliseconds
  error?: string;
  output?: string;
  retryCount: number;
  summary?: string;
  sessionId?: string;
  sessionKey?: string;
  /** Persisted for local run logs (e.g. `cron` for isolated agent jobs). */
  sessionType?: string;
  model?: string;
  provider?: string;
  usage?: CronUsageSummary;
  workflowRunId?: string;
}

export interface CronUsageSummary {
  input_tokens?: number;
  output_tokens?: number;
  total_tokens?: number;
}

/** One row for GET /api/cron/runs/history (optional display name from job definition). */
export interface CronRunHistoryRow extends JobExecution {
  jobName?: string;
}

export type CronRunStatus = 'ok' | 'error' | 'skipped';

export interface CronRunOutcome {
  status: CronRunStatus;
  error?: string;
  summary?: string;
  sessionId?: string;
  sessionKey?: string;
  sessionType?: string;
  model?: string;
  provider?: string;
  usage?: CronUsageSummary;
  workflowRunId?: string;
}

// ============================================================================
// Executor Interface
// ============================================================================

export interface CronWorkflowRunStarter {
  startWorkflowRun(params: StartWorkflowRunServiceParams): Promise<WorkflowRunServiceResult>;
}

/** Optional hook after a successful cron run (e.g. wake gateway heartbeat). */
export interface HeartbeatWakeSink {
  requestNow(opts?: { reason?: string }): void;
}

export interface JobExecutorDeps {
  agentService?: any;
  messageBus?: any;
  heartbeatService?: HeartbeatWakeSink;
  /** When set, weixin cron `delivery.to` may be a bare ilink user id; accountId is inferred from sessions. */
  sessionStore?: SessionStore;
  /**
   * When a job has no `agentId`, isolated cron runs use this id for the session key
   * (same as {@link JobData.agentId} set to the default agent). Typically `getDefaultAgentId(config)`.
   */
  getDefaultCronAgentId?: () => string;
  workflowRunService?: CronWorkflowRunStarter;
}

export interface JobExecutor {
  execute(job: JobData, signal: AbortSignal, deps?: JobExecutorDeps): Promise<void>;
}

// ============================================================================
// Metrics & Health
// ============================================================================

export interface CronMetrics {
  totalJobs: number;
  runningJobs: number;
  enabledJobs: number;
  failedLastHour: number;
  avgExecutionTime: number;
  nextScheduledJob?: {
    id: string;
    name?: string;
    runAt: Date;
  };
}

export interface CronHealth {
  status: 'healthy' | 'degraded' | 'unhealthy';
  issues: string[];
  lastError?: string;
}

// ============================================================================
// API Options
// ============================================================================

export interface AddJobOptions {
  name?: string;
  timezone?: string;
  maxRetries?: number;
  timeout?: number;
  sessionTarget?: CronSessionTarget;
  agentId?: string;
  workingDirectory?: string;
  payload: CronPayload;
  delivery?: CronDelivery;
  model?: string;
}

export interface JobWithNextRun extends Omit<JobData, 'created_at' | 'updated_at' | 'state'> {
  next_run?: string;
}

export interface JobHistoryQuery {
  jobId: string;
  limit?: number;
  before?: Date;
}
