/**
 * QuestDB Client Test Setup
 *
 * Setup file specific to QuestDB client library tests.
 * Provides utilities and mocks for testing database operations.
 */

import { beforeAll, beforeEach, mock } from 'bun:test';
import { newDb } from 'pg-mem';

// Mock PostgreSQL database for unit tests
let pgMem: any;

beforeAll(() => {
  // Create in-memory PostgreSQL database
  pgMem = newDb();

  // Register QuestDB-specific functions
  pgMem.public.registerFunction({
    name: 'now',
    implementation: () => new Date().toISOString(),
  });

  pgMem.public.registerFunction({
    name: 'dateadd',
    args: [{ type: 'text' }, { type: 'int' }, { type: 'timestamp' }],
    returns: 'timestamp',
    implementation: (unit: string, amount: number, date: Date) => {
      const result = new Date(date);
      switch (unit) {
        case 'd':
        case 'day':
          result.setDate(result.getDate() + amount);
          break;
        case 'h':
        case 'hour':
          result.setHours(result.getHours() + amount);
          break;
        case 'm':
        case 'minute':
          result.setMinutes(result.getMinutes() + amount);
          break;
        default:
          throw new Error(`Unsupported date unit: ${unit}`);
      }
      return result;
    },
  }); // Mock QuestDB HTTP client
  // Mock fetch using Bun's built-in mock
  (global as any).fetch = mock(() => {});

  // Mock the logger module to avoid Pino configuration conflicts
  mock.module('@stock-bot/logger', () => ({
    Logger: mock(() => ({
      info: mock(() => {}),
      warn: mock(() => {}),
      error: mock(() => {}),
      debug: mock(() => {}),
      fatal: mock(() => {}),
      trace: mock(() => {}),
      child: mock(() => ({
        info: mock(() => {}),
        warn: mock(() => {}),
        error: mock(() => {}),
        debug: mock(() => {}),
        fatal: mock(() => {}),
        trace: mock(() => {}),
      })),
    })),
    getLogger: mock(() => ({
      info: mock(() => {}),
      warn: mock(() => {}),
      error: mock(() => {}),
      debug: mock(() => {}),
      fatal: mock(() => {}),
      trace: mock(() => {}),
      child: mock(() => ({
        info: mock(() => {}),
        warn: mock(() => {}),
        error: mock(() => {}),
        debug: mock(() => {}),
        fatal: mock(() => {}),
        trace: mock(() => {}),
      })),
    })),
  }));

  // Mock Pino and its transports to avoid configuration conflicts
  mock.module('pino', () => ({
    default: mock(() => ({
      info: mock(() => {}),
      warn: mock(() => {}),
      error: mock(() => {}),
      debug: mock(() => {}),
      fatal: mock(() => {}),
      trace: mock(() => {}),
      child: mock(() => ({
        info: mock(() => {}),
        warn: mock(() => {}),
        error: mock(() => {}),
        debug: mock(() => {}),
        fatal: mock(() => {}),
        trace: mock(() => {}),
      })),
    })),
  }));

  mock.module('pino-pretty', () => ({
    default: mock(() => ({})),
  }));

  mock.module('pino-loki', () => ({
    default: mock(() => ({})),
  }));
});

beforeEach(() => {
  // Reset database state
  if (pgMem) {
    try {
      pgMem.public.none('DROP TABLE IF EXISTS ohlcv CASCADE');
      pgMem.public.none('DROP TABLE IF EXISTS trades CASCADE');
      pgMem.public.none('DROP TABLE IF EXISTS quotes CASCADE');
      pgMem.public.none('DROP TABLE IF EXISTS indicators CASCADE');
      pgMem.public.none('DROP TABLE IF EXISTS performance CASCADE');
      pgMem.public.none('DROP TABLE IF EXISTS risk_metrics CASCADE');
    } catch {
      // Tables might not exist, ignore errors
    }
  } // Reset fetch mock
  if ((global as any).fetch) {
    ((global as any).fetch as any).mockClear?.();
  }
});

/**
 * QuestDB-specific test utilities
 */
export const questdbTestHelpers = {
  /**
   * Get mock PostgreSQL adapter
   */
  getMockPgAdapter: () => pgMem?.adapters?.createPg?.(),

  /**
   * Execute SQL in mock database
   */
  executeMockSQL: (sql: string, params?: any[]) => {
    return pgMem?.public?.query(sql, params);
  },
  /**
   * Mock successful QuestDB HTTP response
   */ mockQuestDBHttpSuccess: (data: any) => {
    ((global as any).fetch as any).mockResolvedValue?.({
      ok: true,
      status: 200,
      json: async () => data,
      text: async () => JSON.stringify(data),
    });
  },

  /**
   * Mock QuestDB HTTP error
   */ mockQuestDBHttpError: (status: number, message: string) => {
    ((global as any).fetch as any).mockResolvedValue?.({
      ok: false,
      status,
      json: async () => ({ error: message }),
      text: async () => message,
    });
  },

  /**
   * Mock InfluxDB line protocol response
   */ mockInfluxDBSuccess: () => {
    ((global as any).fetch as any).mockResolvedValue?.({
      ok: true,
      status: 204,
      text: async () => '',
    });
  },

  /**
   * Create test OHLCV table
   */
  createTestOHLCVTable: () => {
    const sql = `
      CREATE TABLE ohlcv (
        symbol VARCHAR(10),
        timestamp TIMESTAMP,
        open DECIMAL(10,2),
        high DECIMAL(10,2),
        low DECIMAL(10,2),
        close DECIMAL(10,2),
        volume BIGINT,
        source VARCHAR(50)
      )
    `;
    return pgMem?.public?.none(sql);
  },

  /**
   * Insert test OHLCV data
   */
  insertTestOHLCVData: (data: any[]) => {
    const sql = `
      INSERT INTO ohlcv (symbol, timestamp, open, high, low, close, volume, source)
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
    `;

    return Promise.all(
      data.map(row =>
        pgMem?.public?.none(sql, [
          row.symbol,
          row.timestamp,
          row.open,
          row.high,
          row.low,
          row.close,
          row.volume,
          row.source || 'test',
        ])
      )
    );
  },

  /**
   * Generate InfluxDB line protocol test data
   */
  generateInfluxDBLines: (count: number = 5) => {
    const lines: string[] = [];
    const baseTime = Date.now() * 1000000; // Convert to nanoseconds

    for (let i = 0; i < count; i++) {
      const time = baseTime + i * 60000000000; // 1 minute intervals
      const price = 150 + Math.random() * 10;

      lines.push(
        `ohlcv,symbol=TEST open=${price},high=${price + 1},low=${price - 1},close=${price + 0.5},volume=1000i ${time}`
      );
    }

    return lines;
  },

  /**
   * Validate QuestDB query syntax
   */
  validateQuestDBQuery: (query: string): boolean => {
    // Basic validation for QuestDB-specific syntax
    const _questdbKeywords = ['SAMPLE BY', 'LATEST BY', 'ASOF JOIN', 'SPLICE JOIN', 'LT JOIN'];

    // Check for valid SQL structure
    const hasSelect = /SELECT\s+/i.test(query);
    const hasFrom = /FROM\s+/i.test(query);

    return hasSelect && hasFrom;
  },

  /**
   * Mock connection pool
   */ createMockPool: () => {
    const mockQuery = () => Promise.resolve({ rows: [], rowCount: 0 });
    const mockRelease = () => {};
    const mockConnect = () =>
      Promise.resolve({
        query: mockQuery,
        release: mockRelease,
      });
    const mockEnd = () => Promise.resolve(undefined);

    return {
      connect: mockConnect,
      end: mockEnd,
      totalCount: 0,
      idleCount: 0,
      waitingCount: 0,
    };
  },
};
