import { Injectable } from '@nestjs/common';
import { InjectMetric } from '@willsoto/nestjs-prometheus';
import { Counter, Gauge, Histogram } from 'prom-client';
import {
  GOODGO_LISTINGS_CREATED_TOTAL,
  GOODGO_PAYMENTS_PROCESSED_TOTAL,
  GOODGO_ACTIVE_SUBSCRIPTIONS,
  GOODGO_SEARCH_QUERIES_TOTAL,
  GOODGO_API_REQUEST_DURATION,
  HTTP_REQUESTS_TOTAL,
  GOODGO_WS_CONNECTED_CLIENTS,
  GOODGO_WS_MESSAGES_TOTAL,
  WEB_VITALS_LCP,
  WEB_VITALS_FCP,
  WEB_VITALS_CLS,
  WEB_VITALS_TTFB,
  WEB_VITALS_INP,
  WEB_VITALS_TOTAL,
} from '../metrics.constants';

@Injectable()
export class MetricsService {
  constructor(
    @InjectMetric(GOODGO_LISTINGS_CREATED_TOTAL)
    private readonly listingsCreatedCounter: Counter,
    @InjectMetric(GOODGO_PAYMENTS_PROCESSED_TOTAL)
    private readonly paymentsProcessedCounter: Counter,
    @InjectMetric(GOODGO_ACTIVE_SUBSCRIPTIONS)
    private readonly activeSubscriptionsGauge: Gauge,
    @InjectMetric(GOODGO_SEARCH_QUERIES_TOTAL)
    private readonly searchQueriesCounter: Counter,
    @InjectMetric(GOODGO_API_REQUEST_DURATION)
    private readonly requestDurationHistogram: Histogram,
    @InjectMetric(HTTP_REQUESTS_TOTAL)
    private readonly httpRequestsCounter: Counter,
    @InjectMetric(GOODGO_WS_CONNECTED_CLIENTS)
    private readonly wsConnectedClientsGauge: Gauge,
    @InjectMetric(GOODGO_WS_MESSAGES_TOTAL)
    private readonly wsMessagesCounter: Counter,
    @InjectMetric(WEB_VITALS_LCP)
    private readonly lcpHistogram: Histogram,
    @InjectMetric(WEB_VITALS_FCP)
    private readonly fcpHistogram: Histogram,
    @InjectMetric(WEB_VITALS_CLS)
    private readonly clsHistogram: Histogram,
    @InjectMetric(WEB_VITALS_TTFB)
    private readonly ttfbHistogram: Histogram,
    @InjectMetric(WEB_VITALS_INP)
    private readonly inpHistogram: Histogram,
    @InjectMetric(WEB_VITALS_TOTAL)
    private readonly webVitalsCounter: Counter,
  ) {}

  /** Record a new listing creation. */
  recordListingCreated(category: string): void {
    this.listingsCreatedCounter.inc({ category });
  }

  /** Record a payment processing event. */
  recordPaymentProcessed(status: string, method: string): void {
    this.paymentsProcessedCounter.inc({ status, method });
  }

  /** Set the current number of active subscriptions for a plan tier. */
  setActiveSubscriptions(plan: string, count: number): void {
    this.activeSubscriptionsGauge.set({ plan }, count);
  }

  /** Record a search query. */
  recordSearchQuery(collection: string, type: string): void {
    this.searchQueriesCounter.inc({ collection, type });
  }

  /** Record HTTP request duration and count (used by interceptor). */
  recordHttpRequest(
    method: string,
    route: string,
    statusCode: number,
    durationSeconds: number,
  ): void {
    const labels = {
      method,
      route,
      status_code: String(statusCode),
    };
    this.requestDurationHistogram.observe(labels, durationSeconds);
    this.httpRequestsCounter.inc(labels);
  }

  /** Track a WebSocket client connection (++) or disconnection (--). */
  recordWsConnection(namespace: string, delta: 1 | -1): void {
    this.wsConnectedClientsGauge.inc({ namespace }, delta);
  }

  /** Reset the connected-clients gauge for a namespace (e.g. on shutdown). */
  setWsConnectedClients(namespace: string, count: number): void {
    this.wsConnectedClientsGauge.set({ namespace }, count);
  }

  /** Record a WebSocket message emitted/received on a given event. */
  recordWsMessage(
    namespace: string,
    event: string,
    direction: 'in' | 'out',
  ): void {
    this.wsMessagesCounter.inc({ namespace, event, direction });
  }

  /** Map metric name → the correct histogram. */
  private readonly vitalHistograms: Record<string, Histogram | undefined> = {};

  private getVitalHistogram(name: string): Histogram | undefined {
    // Lazy-init the lookup (cannot reference `this` in field initialiser)
    if (Object.keys(this.vitalHistograms).length === 0) {
      this.vitalHistograms['LCP'] = this.lcpHistogram;
      this.vitalHistograms['FCP'] = this.fcpHistogram;
      this.vitalHistograms['CLS'] = this.clsHistogram;
      this.vitalHistograms['TTFB'] = this.ttfbHistogram;
      this.vitalHistograms['INP'] = this.inpHistogram;
    }
    return this.vitalHistograms[name];
  }

  /** Record a single Core Web Vital measurement. */
  recordWebVital(
    name: string,
    value: number,
    rating: string,
    page: string,
  ): void {
    const histogram = this.getVitalHistogram(name);
    if (!histogram) return;

    // LCP, FID, TTFB, INP arrive in ms from the browser — convert to seconds.
    // CLS is unitless (no conversion).
    const observeValue = name === 'CLS' ? value : value / 1000;
    histogram.observe({ rating, page }, observeValue);
    this.webVitalsCounter.inc({ name, rating });
  }
}
