import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { FreebitAuthService } from "./freebit-auth.service";
import { FreebitError } from "./freebit-error.service";

interface FreebitResponseBase {
  resultCode?: string | number;
  status?: {
    message?: string;
    statusCode?: string | number;
  };
}

@Injectable()
export class FreebitClientService {
  constructor(
    private readonly authService: FreebitAuthService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Make an authenticated request to Freebit API with retry logic
   */
  async makeAuthenticatedRequest<TResponse extends FreebitResponseBase, TPayload extends object>(
    endpoint: string,
    payload: TPayload
  ): Promise<TResponse> {
    const authKey = await this.authService.getAuthKey();
    const config = this.authService.getConfig();

    const requestPayload = { ...payload, authKey };
    // Ensure proper URL construction - remove double slashes
    const baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
    const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
    const url = `${baseUrl}${cleanEndpoint}`;

    for (let attempt = 1; attempt <= config.retryAttempts; attempt++) {
      try {
        this.logger.debug(`Freebit API request (attempt ${attempt}/${config.retryAttempts})`, {
          url,
          payload: this.sanitizePayload(requestPayload),
        });

        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), config.timeout);

        const response = await fetch(url, {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          body: `json=${JSON.stringify(requestPayload)}`,
          signal: controller.signal,
        });

        clearTimeout(timeout);

        if (!response.ok) {
          const errorText = await response.text().catch(() => "Unable to read response body");
          this.logger.error(`Freebit API HTTP error`, {
            url,
            status: response.status,
            statusText: response.statusText,
            responseBody: errorText,
            attempt,
            payload: this.sanitizePayload(requestPayload),
          });
          throw new FreebitError(
            `HTTP ${response.status}: ${response.statusText}`,
            response.status.toString()
          );
        }

        const responseData = (await response.json()) as TResponse;

        const resultCode = this.normalizeResultCode(responseData.resultCode);
        const statusCode = this.normalizeResultCode(responseData.status?.statusCode);

        if (resultCode && resultCode !== "100") {
          this.logger.warn("Freebit API returned error response", {
            url,
            resultCode,
            statusCode,
            statusMessage: responseData.status?.message,
            fullResponse: responseData,
          });
          
          throw new FreebitError(
            `API Error: ${responseData.status?.message || "Unknown error"}`,
            resultCode,
            statusCode,
            responseData.status?.message
          );
        }

        this.logger.debug("Freebit API request successful", {
          url,
          resultCode,
        });

        return responseData;
      } catch (error: unknown) {
        if (error instanceof FreebitError) {
          if (error.isAuthError() && attempt === 1) {
            this.logger.warn("Auth error detected, clearing cache and retrying");
            this.authService.clearAuthCache();
            continue;
          }
          if (!error.isRetryable() || attempt === config.retryAttempts) {
            throw error;
          }
        }

        if (attempt === config.retryAttempts) {
          const message = getErrorMessage(error);
          this.logger.error(`Freebit API request failed after ${config.retryAttempts} attempts`, {
            url,
            error: message,
          });
          throw new FreebitError(`Request failed: ${message}`);
        }

        const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
        this.logger.warn(`Freebit API request failed, retrying in ${delay}ms`, {
          url,
          attempt,
          error: getErrorMessage(error),
        });
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }

    throw new FreebitError("Request failed after all retry attempts");
  }

  /**
   * Make an authenticated JSON request to Freebit API (for PA05-41)
   */
  async makeAuthenticatedJsonRequest<
    TResponse extends FreebitResponseBase,
    TPayload extends object,
  >(endpoint: string, payload: TPayload): Promise<TResponse> {
    const config = this.authService.getConfig();
    // Ensure proper URL construction - remove double slashes
    const baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
    const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
    const url = `${baseUrl}${cleanEndpoint}`;

    for (let attempt = 1; attempt <= config.retryAttempts; attempt++) {
      try {
        this.logger.debug(`Freebit JSON API request (attempt ${attempt}/${config.retryAttempts})`, {
          url,
          payload: this.sanitizePayload(payload as Record<string, unknown>),
        });

        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), config.timeout);

        const response = await fetch(url, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
          signal: controller.signal,
        });

        clearTimeout(timeout);

        if (!response.ok) {
          throw new FreebitError(
            `HTTP ${response.status}: ${response.statusText}`,
            response.status.toString()
          );
        }

        const responseData = (await response.json()) as TResponse;

        const resultCode = this.normalizeResultCode(responseData.resultCode);
        const statusCode = this.normalizeResultCode(responseData.status?.statusCode);

        if (resultCode && resultCode !== "100") {
          this.logger.error(`Freebit API returned error result code`, {
            url,
            resultCode,
            statusCode,
            message: responseData.status?.message,
            responseData: this.sanitizePayload(responseData as unknown as Record<string, unknown>),
            attempt,
          });
          throw new FreebitError(
            `API Error: ${responseData.status?.message || "Unknown error"}`,
            resultCode,
            statusCode,
            responseData.status?.message
          );
        }

        this.logger.debug("Freebit JSON API request successful", {
          url,
          resultCode,
        });

        return responseData;
      } catch (error: unknown) {
        if (error instanceof FreebitError) {
          if (error.isAuthError() && attempt === 1) {
            this.logger.warn("Auth error detected, clearing cache and retrying");
            this.authService.clearAuthCache();
            continue;
          }
          if (!error.isRetryable() || attempt === config.retryAttempts) {
            throw error;
          }
        }

        if (attempt === config.retryAttempts) {
          const message = getErrorMessage(error);
          this.logger.error(
            `Freebit JSON API request failed after ${config.retryAttempts} attempts`,
            {
              url,
              error: message,
            }
          );
          throw new FreebitError(`Request failed: ${message}`);
        }

        const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
        this.logger.warn(`Freebit JSON API request failed, retrying in ${delay}ms`, {
          url,
          attempt,
          error: getErrorMessage(error),
        });
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }

    throw new FreebitError("Request failed after all retry attempts");
  }

  /**
   * Make a simple request without authentication (for health checks)
   */
  async makeSimpleRequest(endpoint: string): Promise<boolean> {
    const config = this.authService.getConfig();
    // Ensure proper URL construction - remove double slashes
    const baseUrl = config.baseUrl.replace(/\/$/, ''); // Remove trailing slash
    const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
    const url = `${baseUrl}${cleanEndpoint}`;

    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), config.timeout);

      const response = await fetch(url, {
        method: "GET",
        signal: controller.signal,
      });

      clearTimeout(timeout);

      return response.ok;
    } catch (error) {
      this.logger.debug("Simple request failed", {
        url,
        error: getErrorMessage(error),
      });
      return false;
    }
  }

  /**
   * Sanitize payload for logging (remove sensitive data)
   */
  private sanitizePayload(payload: Record<string, unknown>): Record<string, unknown> {
    const sanitized = { ...payload };

    // Remove sensitive fields
    const sensitiveFields = ["authKey", "oemKey", "password", "secret"];
    for (const field of sensitiveFields) {
      if (sanitized[field]) {
        sanitized[field] = "[REDACTED]";
      }
    }

    return sanitized;
  }

  private normalizeResultCode(code?: string | number | null): string | undefined {
    if (code === undefined || code === null) {
      return undefined;
    }

    const normalized = String(code).trim();
    return normalized.length > 0 ? normalized : undefined;
  }
}
