import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import {
  TelemetryClient,
  type TaskCompletionEvent,
  type PredictionQuery,
  type PredictionResponse,
  type EventBuilder,
} from "@mosaicstack/telemetry-client";
import {
  loadMosaicTelemetryConfig,
  toSdkConfig,
  type MosaicTelemetryModuleConfig,
} from "./mosaic-telemetry.config";

/**
 * NestJS service wrapping the @mosaicstack/telemetry-client SDK.
 *
 * Provides convenience methods for tracking task completions and reading
 * crowd-sourced predictions. When telemetry is disabled via
 * MOSAIC_TELEMETRY_ENABLED=false, all methods are safe no-ops.
 *
 * This service is provided globally by MosaicTelemetryModule — any service
 * can inject it without importing the module explicitly.
 *
 * @example
 * ```typescript
 * @Injectable()
 * export class TasksService {
 *   constructor(private readonly telemetry: MosaicTelemetryService) {}
 *
 *   async completeTask(taskId: string): Promise<void> {
 *     // ... complete the task ...
 *     const event = this.telemetry.eventBuilder.build({ ... });
 *     this.telemetry.trackTaskCompletion(event);
 *   }
 * }
 * ```
 */
@Injectable()
export class MosaicTelemetryService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(MosaicTelemetryService.name);
  private client: TelemetryClient | null = null;
  private config: MosaicTelemetryModuleConfig | null = null;

  constructor(private readonly configService: ConfigService) {}

  /**
   * Initialize the telemetry client on module startup.
   * Reads configuration from environment variables and starts background submission.
   */
  onModuleInit(): void {
    this.config = loadMosaicTelemetryConfig(this.configService);

    if (!this.config.enabled) {
      this.logger.log("Mosaic Telemetry is disabled");
      return;
    }

    if (!this.config.serverUrl || !this.config.apiKey || !this.config.instanceId) {
      this.logger.warn(
        "Mosaic Telemetry is enabled but missing configuration " +
          "(MOSAIC_TELEMETRY_SERVER_URL, MOSAIC_TELEMETRY_API_KEY, or MOSAIC_TELEMETRY_INSTANCE_ID). " +
          "Telemetry will remain disabled."
      );
      this.config = { ...this.config, enabled: false };
      return;
    }

    const sdkConfig = toSdkConfig(this.config, (error: Error) => {
      this.logger.error(`Telemetry client error: ${error.message}`, error.stack);
    });

    this.client = new TelemetryClient(sdkConfig);
    this.client.start();

    const mode = this.config.dryRun ? "dry-run" : "live";
    this.logger.log(`Mosaic Telemetry client started (${mode}) -> ${this.config.serverUrl}`);
  }

  /**
   * Stop the telemetry client on module shutdown.
   * Flushes any remaining queued events before stopping.
   */
  async onModuleDestroy(): Promise<void> {
    if (this.client) {
      this.logger.log("Stopping Mosaic Telemetry client...");
      await this.client.stop();
      this.client = null;
      this.logger.log("Mosaic Telemetry client stopped");
    }
  }

  /**
   * Queue a task completion event for batch submission.
   * No-op when telemetry is disabled.
   *
   * @param event - The task completion event to track
   */
  trackTaskCompletion(event: TaskCompletionEvent): void {
    if (!this.client) {
      return;
    }
    this.client.track(event);
  }

  /**
   * Get a cached prediction for the given query.
   * Returns null when telemetry is disabled or if not cached/expired.
   *
   * @param query - The prediction query parameters
   * @returns Cached prediction response, or null
   */
  getPrediction(query: PredictionQuery): PredictionResponse | null {
    if (!this.client) {
      return null;
    }
    return this.client.getPrediction(query);
  }

  /**
   * Force-refresh predictions from the telemetry server.
   * No-op when telemetry is disabled.
   *
   * @param queries - Array of prediction queries to refresh
   */
  async refreshPredictions(queries: PredictionQuery[]): Promise<void> {
    if (!this.client) {
      return;
    }
    await this.client.refreshPredictions(queries);
  }

  /**
   * Get the EventBuilder for constructing TaskCompletionEvent objects.
   * Returns null when telemetry is disabled.
   *
   * @returns EventBuilder instance, or null if disabled
   */
  get eventBuilder(): EventBuilder | null {
    if (!this.client) {
      return null;
    }
    return this.client.eventBuilder;
  }

  /**
   * Whether the telemetry client is currently active and running.
   */
  get isEnabled(): boolean {
    return this.client?.isRunning ?? false;
  }

  /**
   * Number of events currently queued for submission.
   * Returns 0 when telemetry is disabled.
   */
  get queueSize(): number {
    if (!this.client) {
      return 0;
    }
    return this.client.queueSize;
  }
}
