import { Controller, Get, Inject, Optional, UseGuards } from '@nestjs/common';
import { sql, type Db } from '@mosaicstack/db';
import { createQueue } from '@mosaicstack/queue';
import type { MosaicConfig } from '@mosaicstack/config';
import { DB } from '../database/database.module.js';
import { AgentService } from '../agent/agent.service.js';
import { ProviderService } from '../agent/provider.service.js';
import { MOSAIC_CONFIG } from '../config/config.module.js';
import { AdminGuard } from './admin.guard.js';
import type { HealthStatusDto, ServiceStatusDto } from './admin.dto.js';

@Controller('api/admin/health')
@UseGuards(AdminGuard)
export class AdminHealthController {
  constructor(
    @Inject(DB) private readonly db: Db,
    @Inject(AgentService) private readonly agentService: AgentService,
    @Inject(ProviderService) private readonly providerService: ProviderService,
    @Optional()
    @Inject(MOSAIC_CONFIG)
    private readonly mosaicConfig: MosaicConfig | null,
  ) {}

  @Get()
  async check(): Promise<HealthStatusDto> {
    const [database, cache] = await Promise.all([this.checkDatabase(), this.checkCache()]);

    const sessions = this.agentService.listAllSessionsForSystem();
    const providers = this.providerService.listProviders();

    const allOk = database.status === 'ok' && cache.status === 'ok';

    return {
      status: allOk ? 'ok' : 'degraded',
      database,
      cache,
      agentPool: { activeSessions: sessions.length },
      providers: providers.map((p) => ({
        id: p.id,
        name: p.name,
        available: p.available,
        modelCount: p.models.length,
      })),
      checkedAt: new Date().toISOString(),
    };
  }

  private async checkDatabase(): Promise<ServiceStatusDto> {
    const start = Date.now();
    try {
      await this.db.execute(sql`SELECT 1`);
      return { status: 'ok', latencyMs: Date.now() - start };
    } catch (err) {
      return {
        status: 'error',
        latencyMs: Date.now() - start,
        error: err instanceof Error ? err.message : String(err),
      };
    }
  }

  private async checkCache(): Promise<ServiceStatusDto> {
    // On Local tier there is no Redis. The cache is intentionally absent, which
    // is a healthy state for this tier — report 'ok' rather than opening a new
    // ioredis connection on every admin health check (which would spam
    // ECONNREFUSED and create/destroy a connection per request). latencyMs 0
    // signals "no cache backend to measure" for this tier.
    if (this.mosaicConfig?.queue?.type === 'local') {
      return { status: 'ok', latencyMs: 0 };
    }
    const start = Date.now();
    const handle = createQueue();
    try {
      await handle.redis.ping();
      return { status: 'ok', latencyMs: Date.now() - start };
    } catch (err) {
      return {
        status: 'error',
        latencyMs: Date.now() - start,
        error: err instanceof Error ? err.message : String(err),
      };
    } finally {
      await handle.close().catch(() => {});
    }
  }
}
