import type { Order, PositionSnapshot } from "../../domain/src/types";
import { emitRiskMetrics } from "./riskMetrics";

export interface RiskLimits {
  maxBaseAbs: number;
  maxNotionalAbs: number;
  maxOrderSz: number;
}

export type KillSwitchTriggerType =
  | "pnl_drawdown"
  | "delta_abs"
  | "hedge_failure_count"
  | "data_gap_sec";

export interface KillSwitchTrigger {
  type: KillSwitchTriggerType;
  threshold: number;
}

export interface KillSwitchConfig {
  drawdownPct: number;
  triggers?: KillSwitchTrigger[];
}

export interface RiskStatus {
  realizedPnL: number;
  peakEquity: number;
  currentEquity: number;
  deltaAbs: number;
  hedgeFailures: number;
  dataGapSeconds: number;
  halted: boolean;
  haltReason?: string;
}

export class RiskEngine {
  private realizedPnL = 0;
  private currentEquity = 0;
  private peakEquity = 0;
  private deltaAbs = 0;
  private hedgeFailures = 0;
  private dataGapSeconds = 0;
  private halted = false;
  private haltReason?: string;

  constructor(
    private readonly limits: RiskLimits,
    private readonly killSwitch?: KillSwitchConfig
  ) {
    this.emitMetrics();
  }

  preCheck(order: Order, position: PositionSnapshot, midPrice: number): void {
    if (this.halted) {
      throw new Error(`kill-switch active: ${this.haltReason ?? "unknown"}`);
    }
    if (order.sz > this.limits.maxOrderSz) {
      throw new Error("order size exceeds limit");
    }
    const nextBase = position.base + (order.side === "buy" ? order.sz : -order.sz);
    if (Math.abs(nextBase) > this.limits.maxBaseAbs) {
      throw new Error("inventory limit exceeded");
    }
    const nextNotional = Math.abs(nextBase * midPrice);
    if (nextNotional > this.limits.maxNotionalAbs) {
      throw new Error("notional limit exceeded");
    }
  }

  reportFill(pnlDelta: number): void {
    this.realizedPnL += pnlDelta;
    this.emitMetrics();
  }

  updateEquity(equity: number): void {
    this.currentEquity = equity;
    if (equity > this.peakEquity) {
      this.peakEquity = equity;
    }
    this.evaluateKillSwitch();
    this.emitMetrics();
  }

  updateDeltaAbs(deltaAbs: number): void {
    this.deltaAbs = Math.abs(deltaAbs);
    this.evaluateKillSwitch();
    this.emitMetrics();
  }

  recordHedgeFailure(): void {
    this.hedgeFailures += 1;
    this.evaluateKillSwitch();
    this.emitMetrics();
  }

  recordHedgeSuccess(): void {
    if (this.hedgeFailures > 0) {
      this.hedgeFailures -= 1;
    }
    this.emitMetrics();
  }

  setDataGap(seconds: number): void {
    this.dataGapSeconds = seconds;
    this.evaluateKillSwitch();
    this.emitMetrics();
  }

  shouldHalt(): boolean {
    return this.halted;
  }

  getStatus(): RiskStatus {
    return {
      realizedPnL: this.realizedPnL,
      peakEquity: this.peakEquity,
      currentEquity: this.currentEquity,
      deltaAbs: this.deltaAbs,
      hedgeFailures: this.hedgeFailures,
      dataGapSeconds: this.dataGapSeconds,
      halted: this.halted,
      haltReason: this.haltReason
    };
  }

  resetKillSwitch(): void {
    this.halted = false;
    this.haltReason = undefined;
    this.emitMetrics();
  }

  private evaluateKillSwitch(): void {
    if (!this.killSwitch || this.halted) return;

    if (this.killSwitch.drawdownPct > 0 && this.peakEquity > 0) {
      const drawdown = (this.currentEquity - this.peakEquity) / this.peakEquity;
      if (drawdown <= -Math.abs(this.killSwitch.drawdownPct)) {
        this.triggerHalt("drawdown threshold");
      }
    }

    if (!this.killSwitch.triggers) return;

    for (const trigger of this.killSwitch.triggers) {
      if (this.halted) break;
      switch (trigger.type) {
        case "delta_abs":
          if (this.deltaAbs > trigger.threshold) {
            this.triggerHalt("delta_abs limit");
          }
          break;
        case "hedge_failure_count":
          if (this.hedgeFailures >= trigger.threshold) {
            this.triggerHalt("hedge failures");
          }
          break;
        case "data_gap_sec":
          if (this.dataGapSeconds >= trigger.threshold) {
            this.triggerHalt("data gap");
          }
          break;
        case "pnl_drawdown":
          if (this.peakEquity > 0) {
            const drop = (this.currentEquity - this.peakEquity) / this.peakEquity;
            if (drop <= -Math.abs(trigger.threshold)) {
              this.triggerHalt("pnl drawdown");
            }
          }
          break;
      }
    }
  }

  private triggerHalt(reason: string): void {
    this.halted = true;
    this.haltReason = reason;
    this.emitMetrics();
  }

  private emitMetrics(): void {
    const drawdownPct = this.peakEquity > 0
      ? (this.currentEquity - this.peakEquity) / this.peakEquity
      : 0;

    emitRiskMetrics({
      deltaAbs: this.deltaAbs,
      hedgeFailures: this.hedgeFailures,
      dataGapSeconds: this.dataGapSeconds,
      drawdownPct,
      halted: this.halted,
      haltReason: this.haltReason
    });
  }
}
