import {
  BadGatewayException,
  ForbiddenException,
  Injectable,
} from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { ConfigService } from "@nestjs/config";
import { AxiosError, Method } from "axios";

interface ProxyRequest {
  method: string;
  originalUrl: string;
  query: Record<string, unknown>;
  body: unknown;
  acceptLanguage?: string | string[];
}

interface AllowedRoute {
  method: Method;
  pattern: RegExp;
}

const ALLOWED_AI_ROUTES: AllowedRoute[] = [
  { method: "GET", pattern: /^\/$/ },
  { method: "GET", pattern: /^\/health$/ },
  { method: "POST", pattern: /^\/v20plus\/analyze\/[^/]+$/ },
  { method: "GET", pattern: /^\/v20plus\/analyze-htms\/[^/]+$/ },
  { method: "GET", pattern: /^\/v20plus\/analyze-htft\/[^/]+$/ },
  { method: "POST", pattern: /^\/v20plus\/coupon$/ },
  { method: "GET", pattern: /^\/v20plus\/daily-banker$/ },
  { method: "GET", pattern: /^\/v20plus\/reversal-watchlist$/ },
  { method: "GET", pattern: /^\/v2\/health$/ },
  { method: "POST", pattern: /^\/v2\/analyze\/[^/]+$/ },
];

@Injectable()
export class AiProxyService {
  constructor(
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
  ) {}

  async proxy(request: ProxyRequest) {
    const path = this.extractProxyPath(request.originalUrl);
    const method = request.method.toUpperCase() as Method;

    if (!this.isAllowed(method, path)) {
      throw new ForbiddenException("AI_PROXY_ROUTE_NOT_ALLOWED");
    }

    const baseUrl = this.configService.getOrThrow<string>("AI_ENGINE_URL");
    const targetUrl = new URL(path, baseUrl);

    try {
      const response = await this.httpService.axiosRef.request({
        url: targetUrl.toString(),
        method,
        params: request.query,
        data: request.body,
        headers: {
          "content-type": "application/json",
          "accept-language": Array.isArray(request.acceptLanguage)
            ? request.acceptLanguage[0]
            : request.acceptLanguage,
        },
        timeout: 45000,
        maxRedirects: 0,
        validateStatus: (status) => status >= 200 && status < 500,
      });

      return response.data;
    } catch (error) {
      const axiosError = error as AxiosError;
      throw new BadGatewayException({
        message: "AI_PROXY_UPSTREAM_FAILED",
        status: axiosError.response?.status,
      });
    }
  }

  private extractProxyPath(originalUrl: string): string {
    const withoutQuery = originalUrl.split("?")[0] || "";
    const marker = "/ai-engine";
    const markerIndex = withoutQuery.indexOf(marker);
    if (markerIndex === -1) {
      return "/";
    }

    const path = withoutQuery.slice(markerIndex + marker.length);
    return path.length === 0 ? "/" : path;
  }

  private isAllowed(method: Method, path: string): boolean {
    return ALLOWED_AI_ROUTES.some(
      (route) => route.method === method && route.pattern.test(path),
    );
  }
}
