import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

import type { CreateRedisStringsHandlerOptions } from './index';
import RedisStringsHandler from './RedisStringsHandler';

vi.mock('redis', () => {
  const createClient = () => {
    return {
      isReady: true,
      on: vi.fn(),
      connect: vi.fn(async () => undefined),
      disconnect: vi.fn(),
      quit: vi.fn(),
      duplicate: vi.fn(() => ({
        connect: vi.fn(async () => undefined),
        subscribe: vi.fn(async () => undefined),
        on: vi.fn(),
        quit: vi.fn(async () => undefined),
        configGet: vi.fn(async () => ({ 'notify-keyspace-events': 'Exe' })),
      })),
      get: vi.fn(async () => {
        const err = new Error('timeout') as Error & { name: string };
        err.name = 'AbortError';
        throw err;
      }),
      hScan: vi.fn(async () => ({ cursor: 0, tuples: [] })),
      scan: vi.fn(async () => ({ cursor: 0, keys: [] })),
      hSet: vi.fn(async () => 1),
      hDel: vi.fn(async () => 1),
      publish: vi.fn(async () => 1),
      unlink: vi.fn(async () => 1),
      set: vi.fn(async () => 'OK'),
    };
  };
  return {
    createClient,
    commandOptions: vi.fn((opts) => opts),
  };
});

vi.mock('./SyncedMap', () => {
  class SyncedMap {
    waitUntilReady = vi.fn(async () => undefined);
    get = vi.fn(() => undefined);
    set = vi.fn(async () => undefined);
    delete = vi.fn(async () => undefined);
    entries = vi.fn(function* () {
      return;
    });

    constructor() {}
  }

  return { SyncedMap };
});

describe('RedisStringsHandler', () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  afterEach(() => {
    vi.unstubAllEnvs();
  });

  it('treats aborted GET (timeout) as cache miss without console.error', async () => {
    const consoleErrorSpy = vi
      .spyOn(console, 'error')
      .mockImplementation(() => undefined);

    const handler = new RedisStringsHandler({
      redisUrl: 'redis://localhost:6379',
      keyPrefix: 'test:',
      database: 0,
      getTimeoutMs: 1,
      redisGetDeduplication: false,
    });

    const res = await handler.get('missing-key', {
      kind: 'APP_PAGE',
      isRoutePPREnabled: false,
      isFallback: false,
    });

    expect(res).toBeNull();
    expect(consoleErrorSpy).not.toHaveBeenCalled();
  });
});

describe('Public exports', () => {
  it('exports CreateRedisStringsHandlerOptions type', () => {
    const _typeCheck: CreateRedisStringsHandlerOptions = {
      keyPrefix: 'test',
    };

    expect(_typeCheck.keyPrefix).toBe('test');
  });
});
