import type { ClickHouseSettings } from "@clickhouse/client";
export type { ClickHouseSettings };
import { ClickhouseClient } from "./client/client.js";
import type { ClickhouseReader, ClickhouseWriter } from "./client/types.js";
import { NoopClient } from "./client/noop.js";
import {
  insertTaskRunsCompactArrays,
  insertRawTaskRunPayloadsCompactArrays,
  getTaskRunsQueryBuilder,
  getTaskActivityQueryBuilder,
  getCurrentRunningStats,
  getChildRunStatusCounts,
  getAverageDurations,
  getTaskUsageByOrganization,
  getTaskRunsCountQueryBuilder,
  getTaskRunTagsQueryBuilder,
  getPendingVersionIdsQueryBuilder,
  getTaskRunExistsQueryBuilder,
} from "./taskRuns.js";
import {
  getSpanDetailsQueryBuilder,
  getSpanDetailsQueryBuilderV2,
  getTraceDetailedSummaryQueryBuilder,
  getTraceDetailedSummaryQueryBuilderV2,
  getTraceEventsForExportQueryBuilder,
  getTraceEventsForExportQueryBuilderV2,
  getTraceSummaryQueryBuilder,
  getTraceSummaryQueryBuilderV2,
  insertTaskEvents,
  insertTaskEventsV2,
  getLogDetailQueryBuilderV2,
  getLogsSearchListQueryBuilder,
} from "./taskEvents.js";
import { insertMetrics } from "./metrics.js";
import { insertLlmMetrics } from "./llmMetrics.js";
import {
  getSessionTagsQueryBuilder,
  getSessionsCountQueryBuilder,
  getSessionsQueryBuilder,
  insertSessionsCompactArrays,
} from "./sessions.js";
import {
  getGlobalModelMetrics,
  getGlobalModelComparison,
  getPopularModels,
} from "./llmModelAggregates.js";
import {
  getErrorGroups,
  getErrorInstances,
  getErrorGroupsListQueryBuilder,
  getErrorHourlyOccurrences,
  getErrorOccurrencesListQueryBuilder,
  createErrorOccurrencesQueryBuilder,
  createErrorOccurrencesByVersionQueryBuilder,
  getErrorAffectedVersionsQueryBuilder,
  getOccurrenceCountSinceQueryBuilder,
  getActiveErrorsSinceQueryBuilder,
  getOccurrenceCountsSinceQueryBuilder,
} from "./errors.js";
export { msToClickHouseInterval } from "./intervals.js";
import { Logger, type LogLevel } from "@trigger.dev/core/logger";
import type { Agent as HttpAgent } from "http";
import type { Agent as HttpsAgent } from "https";

export type * from "./taskRuns.js";
export type * from "./taskEvents.js";
export type * from "./metrics.js";
export type * from "./llmMetrics.js";
export type * from "./llmModelAggregates.js";
export type * from "./errors.js";
export type * from "./sessions.js";
export type * from "./client/queryBuilder.js";

// Re-export column constants, indices, and type-safe accessors
export {
  TASK_RUN_COLUMNS,
  TASK_RUN_INDEX,
  PAYLOAD_COLUMNS,
  PAYLOAD_INDEX,
  getTaskRunField,
  getPayloadField,
  composeTaskRunVersion,
} from "./taskRuns.js";

export { SESSION_COLUMNS, SESSION_INDEX, getSessionField } from "./sessions.js";

// TSQL query execution
export {
  executeTSQL,
  createTSQLExecutor,
  type ExecuteTSQLOptions,
  type TableSchema,
  type TSQLQueryResult,
  type TSQLQuerySuccess,
  type QueryStats,
  type FieldMappings,
  type WhereClauseCondition,
} from "./client/tsql.js";
export type { ColumnFormatType, OutputColumnMetadata } from "@internal/tsql";

// Errors
export { QueryError } from "./client/errors.js";

export type ClickhouseCommonConfig = {
  keepAlive?: {
    enabled?: boolean;
    idleSocketTtl?: number;
  };
  httpAgent?: HttpAgent | HttpsAgent;
  clickhouseSettings?: ClickHouseSettings;
  logger?: Logger;
  logLevel?: LogLevel;
  compression?: {
    request?: boolean;
    response?: boolean;
  };
  maxOpenConnections?: number;
};

export type ClickHouseConfig =
  | ({
      name?: string;
      url?: string;
      writerUrl?: never;
      readerUrl?: never;
    } & ClickhouseCommonConfig)
  | ({
      name?: never;
      url?: never;
      writerName?: string;
      writerUrl: string;
      readerName?: string;
      readerUrl: string;
    } & ClickhouseCommonConfig);

export class ClickHouse {
  public readonly reader: ClickhouseReader;
  public readonly writer: ClickhouseWriter;
  private readonly logger: Logger;
  private _splitClients: boolean;

  constructor(config: ClickHouseConfig) {
    this.logger = config.logger ?? new Logger("ClickHouse", config.logLevel ?? "debug");

    if (config.url) {
      const url = new URL(config.url);
      url.password = "redacted";

      this.logger.info("🏠 Initializing ClickHouse client with url", { url: url.toString() });

      const client = new ClickhouseClient({
        name: config.name ?? "clickhouse",
        url: config.url,
        clickhouseSettings: config.clickhouseSettings,
        logger: this.logger,
        logLevel: config.logLevel,
        keepAlive: config.keepAlive,
        httpAgent: config.httpAgent,
        maxOpenConnections: config.maxOpenConnections,
        compression: config.compression,
      });
      this.reader = client;
      this.writer = client;

      this._splitClients = false;
    } else if (config.writerUrl && config.readerUrl) {
      this.reader = new ClickhouseClient({
        name: config.readerName ?? "clickhouse-reader",
        url: config.readerUrl,
        clickhouseSettings: config.clickhouseSettings,
        logger: this.logger,
        logLevel: config.logLevel,
        keepAlive: config.keepAlive,
        httpAgent: config.httpAgent,
        maxOpenConnections: config.maxOpenConnections,
        compression: config.compression,
      });
      this.writer = new ClickhouseClient({
        name: config.writerName ?? "clickhouse-writer",
        url: config.writerUrl,
        clickhouseSettings: config.clickhouseSettings,
        logger: this.logger,
        logLevel: config.logLevel,
        keepAlive: config.keepAlive,
        httpAgent: config.httpAgent,
        maxOpenConnections: config.maxOpenConnections,
        compression: config.compression,
      });

      this._splitClients = true;
    } else {
      this.reader = new NoopClient();
      this.writer = new NoopClient();

      this._splitClients = true;
    }
  }

  static fromEnv(): ClickHouse {
    if (
      typeof process.env.CLICKHOUSE_WRITER_URL === "string" &&
      typeof process.env.CLICKHOUSE_READER_URL === "string"
    ) {
      return new ClickHouse({
        writerUrl: process.env.CLICKHOUSE_WRITER_URL,
        readerUrl: process.env.CLICKHOUSE_READER_URL,
        writerName: process.env.CLICKHOUSE_WRITER_NAME,
        readerName: process.env.CLICKHOUSE_READER_NAME,
      });
    }

    return new ClickHouse({
      url: process.env.CLICKHOUSE_URL,
      name: process.env.CLICKHOUSE_NAME,
    });
  }

  async close() {
    if (this._splitClients) {
      await Promise.all([this.reader.close(), this.writer.close()]);
    } else {
      await this.reader.close();
    }
  }

  get taskRuns() {
    return {
      insertCompactArrays: insertTaskRunsCompactArrays(this.writer),
      insertPayloadsCompactArrays: insertRawTaskRunPayloadsCompactArrays(this.writer),
      queryBuilder: getTaskRunsQueryBuilder(this.reader),
      countQueryBuilder: getTaskRunsCountQueryBuilder(this.reader),
      existsQueryBuilder: getTaskRunExistsQueryBuilder(this.reader, { max_execution_time: 10 }),
      tagQueryBuilder: getTaskRunTagsQueryBuilder(this.reader),
      pendingVersionIdsQueryBuilder: getPendingVersionIdsQueryBuilder(this.reader),
      getTaskActivity: getTaskActivityQueryBuilder(this.reader),
      getCurrentRunningStats: getCurrentRunningStats(this.reader),
      getChildRunStatusCounts: getChildRunStatusCounts(this.reader),
      getAverageDurations: getAverageDurations(this.reader),
      getTaskUsageByOrganization: getTaskUsageByOrganization(this.reader),
    };
  }

  get taskEvents() {
    return {
      insert: insertTaskEvents(this.writer),
      traceSummaryQueryBuilder: getTraceSummaryQueryBuilder(this.reader),
      traceDetailedSummaryQueryBuilder: getTraceDetailedSummaryQueryBuilder(this.reader),
      traceEventsForExportQueryBuilder: getTraceEventsForExportQueryBuilder(this.reader),
      spanDetailsQueryBuilder: getSpanDetailsQueryBuilder(this.reader),
    };
  }

  get metrics() {
    return {
      insert: insertMetrics(this.writer),
    };
  }

  get llmMetrics() {
    return {
      insert: insertLlmMetrics(this.writer),
    };
  }

  get llmModelAggregates() {
    return {
      globalMetrics: getGlobalModelMetrics(this.reader),
      comparison: getGlobalModelComparison(this.reader),
      popular: getPopularModels(this.reader),
    };
  }

  get sessions() {
    return {
      insertCompactArrays: insertSessionsCompactArrays(this.writer),
      queryBuilder: getSessionsQueryBuilder(this.reader),
      countQueryBuilder: getSessionsCountQueryBuilder(this.reader),
      tagQueryBuilder: getSessionTagsQueryBuilder(this.reader),
    };
  }

  get taskEventsV2() {
    return {
      insert: insertTaskEventsV2(this.writer),
      traceSummaryQueryBuilder: getTraceSummaryQueryBuilderV2(this.reader),
      traceDetailedSummaryQueryBuilder: getTraceDetailedSummaryQueryBuilderV2(this.reader),
      traceEventsForExportQueryBuilder: getTraceEventsForExportQueryBuilderV2(this.reader),
      spanDetailsQueryBuilder: getSpanDetailsQueryBuilderV2(this.reader),
      logDetailQueryBuilder: getLogDetailQueryBuilderV2(this.reader),
    };
  }

  get taskEventsSearch() {
    return {
      logsListQueryBuilder: getLogsSearchListQueryBuilder(this.reader),
    };
  }

  get errors() {
    return {
      getGroups: getErrorGroups(this.reader),
      getInstances: getErrorInstances(this.reader),
      getHourlyOccurrences: getErrorHourlyOccurrences(this.reader),
      affectedVersionsQueryBuilder: getErrorAffectedVersionsQueryBuilder(this.reader),
      listQueryBuilder: getErrorGroupsListQueryBuilder(this.reader),
      occurrencesListQueryBuilder: getErrorOccurrencesListQueryBuilder(this.reader),
      createOccurrencesQueryBuilder: (intervalExpr: string) =>
        createErrorOccurrencesQueryBuilder(this.reader, intervalExpr),
      createOccurrencesByVersionQueryBuilder: (intervalExpr: string) =>
        createErrorOccurrencesByVersionQueryBuilder(this.reader, intervalExpr),
      occurrenceCountSinceQueryBuilder: getOccurrenceCountSinceQueryBuilder(this.reader),
      activeErrorsSinceQueryBuilder: getActiveErrorsSinceQueryBuilder(this.reader),
      occurrenceCountsSinceQueryBuilder: getOccurrenceCountsSinceQueryBuilder(this.reader),
    };
  }
}
