import Redis from 'ioredis';

let client: Redis | null = null;
let currentUrl: string = '';

export function getRedis(): Redis | null {
  return client;
}

export function getRedisClient(): Redis {
  if (client) return client;
  return createClient();
}

function createClient(url?: string): Redis {
  const redisUrl = url || process.env.REDIS_URL || currentUrl || getSystemConfigRedisUrl();
  currentUrl = redisUrl;

  if (client) {
    try { client.disconnect(); } catch {}
    client = null;
  }

  client = new Redis(redisUrl, {
    maxRetriesPerRequest: 3,
    retryStrategy(times: number) {
      if (times > 3) return null;
      return Math.min(times * 200, 2000);
    },
    lazyConnect: true,
  });

  client.on('error', (err: Error) => {
    console.error('[Redis] Error:', err.message);
  });

  client.on('connect', () => {
    console.log('[Redis] Connected to', currentUrl);
  });

  return client;
}

function getSystemConfigRedisUrl(): string {
  try {
    const { getSystemConfig } = require('./admin/system-config.service');
    return getSystemConfig('redis_url') || process.env.REDIS_URL || 'redis://redis:6379';
  } catch {
    return 'redis://redis:6379';
  }
}

export async function connectRedis(): Promise<void> {
  const redis = createClient();
  try {
    await redis.connect();
  } catch (err) {
    console.warn('[Redis] Connection failed, running without cache');
  }
}

export async function reconnectRedis(url: string): Promise<boolean> {
  try {
    if (client) {
      await client.quit().catch(() => {});
      client = null;
    }
    const redis = createClient(url);
    await redis.connect();
    console.log('[Redis] Reconnected with new URL:', url);
    return true;
  } catch (err) {
    console.error('[Redis] Reconnect failed:', err);
    return false;
  }
}

export async function disconnectRedis(): Promise<void> {
  if (client) {
    await client.quit();
    client = null;
    currentUrl = '';
    console.log('[Redis] Disconnected');
  }
}

/**
 * Test a Redis URL without affecting the running client.
 * @returns { ok: boolean, latency: number, info?: string }
 */
export async function testRedisConnection(url: string): Promise<{ ok: boolean; latency: number; info?: string }> {
  const start = Date.now();
  const testClient = new Redis(url, {
    maxRetriesPerRequest: 1,
    retryStrategy() { return null; },
    lazyConnect: true,
    connectTimeout: 5000,
  });
  try {
    await testClient.connect();
    const pong = await testClient.ping();
    const latency = Date.now() - start;
    await testClient.quit();
    return { ok: pong === 'PONG', latency, info: `响应时间 ${latency}ms` };
  } catch (err: any) {
    try { await testClient.disconnect(); } catch {}
    const latency = Date.now() - start;
    return { ok: false, latency, info: err.message || '连接失败' };
  }
}

export class RedisClient {
  private redis: Redis;

  constructor() {
    this.redis = getRedisClient();
  }

  async get(key: string): Promise<string | null> {
    try {
      return await this.redis.get(key);
    } catch {
      return null;
    }
  }

  async set(key: string, value: string): Promise<void> {
    try {
      await this.redis.set(key, value);
    } catch {
      // silently fail
    }
  }

  async setEx(key: string, ttl: number, value: string): Promise<void> {
    try {
      await this.redis.setex(key, ttl, value);
    } catch {
      // silently fail
    }
  }

  async del(key: string): Promise<void> {
    try {
      await this.redis.del(key);
    } catch {
      // silently fail
    }
  }

  async exists(key: string): Promise<boolean> {
    try {
      const result = await this.redis.exists(key);
      return result === 1;
    } catch {
      return false;
    }
  }
}

export default RedisClient;
