/**
 * Health detailed check integration tests (L1)
 *
 * 验收（工单 #295）：
 * - 全部 up → HTTP 200 / status=healthy
 * - Redis down → HTTP 503 / services.redis.status=down
 * - Temporal down → HTTP 503 / services.temporal.status=down
 * - 两者都 down → HTTP 503 / 两个 services 都 down
 *
 * 响应结构（项目 TransformInterceptor + AllExceptionsFilter 双 wrap）：
 * - 200：res.body = { success:true, data:<DetailedHealthStatus>, ... }
 * - 503：res.body = { success:false, error:{ code, message, details:<DetailedHealthStatus 自定义字段> }, ... }
 *   filter 把 ServiceUnavailableException payload 里非保留字段（status/services/system/...）
 *   合并进 error.details。
 */

import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { createTestApp } from '../../helpers/app.helper';
import { RedisService } from '@/core/cache/redis/redis.service';
import { TemporalProbeService } from '@/core/system/health/temporal-probe.service';

describe('GET /api/v1/health/detailed (#295)', () => {
  let app: INestApplication;
  let redisService: RedisService;
  let probeService: TemporalProbeService;
  let pingSpy: jest.SpyInstance;
  let probeSpy: jest.SpyInstance;

  beforeAll(async () => {
    app = await createTestApp();
    redisService = app.get(RedisService);
    probeService = app.get(TemporalProbeService);
  });

  afterAll(async () => {
    await app.close();
  });

  afterEach(() => {
    pingSpy?.mockRestore();
    probeSpy?.mockRestore();
  });

  function stubRedis(up: boolean) {
    pingSpy = up
      ? jest.spyOn(redisService, 'ping').mockResolvedValue('PONG')
      : jest.spyOn(redisService, 'ping').mockRejectedValue(new Error('Connection refused'));
  }

  function stubTemporal(up: boolean) {
    probeSpy = jest.spyOn(probeService, 'probe').mockResolvedValue(
      up ? { ok: true, latencyMs: 12 } : { ok: false, error: 'Temporal probe timeout (2000ms)' },
    );
  }

  // 503 时 services 等字段在 error.details；200 时在 data
  function readPayload(body: any): any {
    return body.success === false ? body.error.details : body.data;
  }

  it('returns 200 with status=healthy when all dependencies are up', async () => {
    stubRedis(true);
    stubTemporal(true);

    const res = await request(app.getHttpServer()).get('/api/v1/health/detailed');

    expect(res.status).toBe(200);
    const payload = readPayload(res.body);
    expect(payload.status).toBe('healthy');
    expect(payload.services.database.status).toBe('up');
    expect(payload.services.redis.status).toBe('up');
    expect(payload.services.temporal.status).toBe('up');
  });

  it('returns 503 with services.redis.status=down when Redis is unreachable', async () => {
    stubRedis(false);
    stubTemporal(true);

    const res = await request(app.getHttpServer()).get('/api/v1/health/detailed');

    expect(res.status).toBe(503);
    const payload = readPayload(res.body);
    expect(payload.status).toBe('unhealthy');
    expect(payload.services.redis.status).toBe('down');
    expect(payload.services.temporal.status).toBe('up');
    expect(payload.services.database.status).toBe('up');
  });

  it('returns 503 with services.temporal.status=down when Temporal is unreachable', async () => {
    stubRedis(true);
    stubTemporal(false);

    const res = await request(app.getHttpServer()).get('/api/v1/health/detailed');

    expect(res.status).toBe(503);
    const payload = readPayload(res.body);
    expect(payload.status).toBe('unhealthy');
    expect(payload.services.redis.status).toBe('up');
    expect(payload.services.temporal.status).toBe('down');
    expect(payload.services.temporal.message).toMatch(/timeout/i);
  });

  it('returns 503 when both Redis and Temporal are down', async () => {
    stubRedis(false);
    stubTemporal(false);

    const res = await request(app.getHttpServer()).get('/api/v1/health/detailed');

    expect(res.status).toBe(503);
    const payload = readPayload(res.body);
    expect(payload.status).toBe('unhealthy');
    expect(payload.services.redis.status).toBe('down');
    expect(payload.services.temporal.status).toBe('down');
  });

  it('basic /api/v1/health still returns 200 regardless of dependency state', async () => {
    stubRedis(false);
    stubTemporal(false);

    const res = await request(app.getHttpServer()).get('/api/v1/health');

    expect(res.status).toBe(200);
    expect(res.body.data.status).toBe('healthy');
  });
});
