import {
  BadGatewayException,
  Injectable,
  Logger,
  ServiceUnavailableException,
} from "@nestjs/common";
import { ContainerLifecycleService } from "../container-lifecycle/container-lifecycle.service";
import { PrismaService } from "../prisma/prisma.service";
import type { ChatMessage } from "./chat-proxy.dto";

const DEFAULT_OPENCLAW_MODEL = "openclaw:default";

interface ContainerConnection {
  url: string;
  token: string;
}

@Injectable()
export class ChatProxyService {
  private readonly logger = new Logger(ChatProxyService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly containerLifecycle: ContainerLifecycleService
  ) {}

  // Get the user's OpenClaw container URL and mark it active.
  async getContainerUrl(userId: string): Promise<string> {
    const { url } = await this.getContainerConnection(userId);
    return url;
  }

  // Proxy chat request to OpenClaw.
  async proxyChat(
    userId: string,
    messages: ChatMessage[],
    signal?: AbortSignal
  ): Promise<Response> {
    const { url: containerUrl, token: gatewayToken } = await this.getContainerConnection(userId);
    const model = await this.getPreferredModel(userId);
    const requestInit: RequestInit = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${gatewayToken}`,
      },
      body: JSON.stringify({
        messages,
        model,
        stream: true,
      }),
    };

    if (signal) {
      requestInit.signal = signal;
    }

    try {
      const response = await fetch(`${containerUrl}/v1/chat/completions`, requestInit);

      if (!response.ok) {
        const detail = await this.readResponseText(response);
        const status = `${String(response.status)} ${response.statusText}`.trim();
        this.logger.warn(
          detail ? `OpenClaw returned ${status}: ${detail}` : `OpenClaw returned ${status}`
        );
        throw new BadGatewayException(`OpenClaw returned ${status}`);
      }

      return response;
    } catch (error: unknown) {
      if (error instanceof BadGatewayException) {
        throw error;
      }

      const message = error instanceof Error ? error.message : String(error);
      this.logger.warn(`Failed to proxy chat request: ${message}`);
      throw new ServiceUnavailableException("Failed to proxy chat to OpenClaw");
    }
  }

  private async getContainerConnection(userId: string): Promise<ContainerConnection> {
    const connection = await this.containerLifecycle.ensureRunning(userId);
    await this.containerLifecycle.touch(userId);
    return connection;
  }

  private async getPreferredModel(userId: string): Promise<string> {
    const config = await this.prisma.userAgentConfig.findUnique({
      where: { userId },
      select: { primaryModel: true },
    });

    const primaryModel = config?.primaryModel?.trim();
    if (!primaryModel) {
      return DEFAULT_OPENCLAW_MODEL;
    }

    return primaryModel;
  }

  private async readResponseText(response: Response): Promise<string | null> {
    try {
      const text = (await response.text()).trim();
      return text.length > 0 ? text : null;
    } catch {
      return null;
    }
  }
}
