import type { BackupConfig, MinioSyncAutomationConfig, MinioSyncTaskConfig } from '../config/schema.js';
import type { MemoryStore } from '../memory/store.js';
import type { OutboundMessage } from '../channels/types.js';
import { parseInterval } from './heartbeat.js';
import { createMinioSyncTool } from '../tools/builtin/minio-sync.js';
import { auditLogger } from '../audit/index.js';
import type { Tool } from '../tools/types.js';

interface ChannelLookup {
  get(name: string): { send(peerId: string, message: OutboundMessage): Promise<void> } | undefined;
}

export interface MinioSyncSchedulerDeps {
  config: MinioSyncAutomationConfig;
  backupConfig: BackupConfig;
  memoryStore?: MemoryStore;
  channelLookup: ChannelLookup;
  createSyncTool?: (backupConfig: BackupConfig, memoryStore: MemoryStore) => Tool;
}

export class MinioSyncScheduler {
  private readonly deps: MinioSyncSchedulerDeps;
  private timer: ReturnType<typeof setInterval> | undefined;
  private running = false;

  constructor(deps: MinioSyncSchedulerDeps) {
    this.deps = deps;
  }

  start(): void {
    if (!this.deps.config.enabled) {return;}
    if (!this.deps.memoryStore) {
      console.warn('MinioSyncScheduler: automation.minio_sync is enabled but memory is disabled; skipping scheduler');
      return;
    }
    if (this.deps.config.tasks.length === 0) {
      console.warn('MinioSyncScheduler: automation.minio_sync is enabled but no tasks are configured; skipping scheduler');
      return;
    }

    const intervalMs = parseInterval(this.deps.config.interval);
    console.log(`MinioSyncScheduler: starting (interval=${this.deps.config.interval}, tasks=${this.deps.config.tasks.length})`);
    auditLogger?.systemStart('MinioSyncScheduler', {
      interval: this.deps.config.interval,
      tasks: this.deps.config.tasks.length,
    });

    this.timer = setInterval(() => {
      this.runOnce().catch((err) => {
        console.error('MinioSyncScheduler: unexpected error during sync cycle:', err);
      });
    }, intervalMs);

    if (this.deps.config.run_on_start) {
      this.runOnce().catch((err) => {
        console.error('MinioSyncScheduler: unexpected error during startup sync:', err);
      });
    }
  }

  stop(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = undefined;
    }
    auditLogger?.systemStop('MinioSyncScheduler');
  }

  async runOnce(): Promise<{ succeeded: number; failed: number; details: string[] }> {
    if (!this.deps.memoryStore || this.running) {
      return { succeeded: 0, failed: 0, details: [] };
    }

    this.running = true;
    try {
      const toolFactory = this.deps.createSyncTool ?? createMinioSyncTool;
      const tool = toolFactory(this.deps.backupConfig, this.deps.memoryStore);
      let succeeded = 0;
      let failed = 0;
      const details: string[] = [];

      for (const task of this.deps.config.tasks) {
        const result = await tool.execute(this.toToolArgs(task));
        if (result.success) {
          succeeded++;
          details.push(`OK ${task.prefix}`);
        } else {
          failed++;
          details.push(`FAIL ${task.prefix}: ${result.error ?? 'Unknown error'}`);
        }
      }

      if (failed > 0) {
        await this.notify(`MinIO sync completed with failures.\n${details.join('\n')}`);
      } else if (this.deps.config.notify_on_success) {
        await this.notify(`MinIO sync completed successfully.\n${details.join('\n')}`);
      }

      return { succeeded, failed, details };
    } finally {
      this.running = false;
    }
  }

  private toToolArgs(task: MinioSyncTaskConfig): Record<string, unknown> {
    return {
      prefix: task.prefix,
      bucket: task.bucket,
      namespace_base: task.namespace_base,
      mode: task.mode,
      max_objects: task.max_objects,
      max_chars_per_object: task.max_chars_per_object,
      force: task.force,
    };
  }

  private async notify(text: string): Promise<void> {
    const notifyConfig = this.deps.config.notify;
    if (!notifyConfig) {return;}

    const channel = this.deps.channelLookup.get(notifyConfig.channel);
    if (!channel) {
      console.warn(`MinioSyncScheduler: notification channel '${notifyConfig.channel}' not found`);
      return;
    }

    try {
      await channel.send(notifyConfig.peer, { text });
    } catch (err) {
      console.error('MinioSyncScheduler: failed to send notification:', err);
    }
  }
}
