import axios, { type AxiosInstance, type AxiosRequestConfig, type Method } from 'axios';
import { env } from '../../config/env';
import { logger } from '../../config/logger';
import { AppError } from '../../utils/AppError';

interface BasicAuthCredentials {
  username: string;
  password: string;
}

interface FinanceRequestConfig extends AxiosRequestConfig {
  basicAuth?: BasicAuthCredentials;
}

export class FinanceApiClient {
  private readonly http: AxiosInstance;

  constructor() {
    this.http = axios.create({
      baseURL: env.FINANCE_API_URL,
      timeout: 15000,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    this.http.interceptors.request.use((config) => {
      const usesPrivateRoute = typeof config.url === 'string' && config.url.startsWith('/private/');
      const hasAuthorization = Boolean(config.headers.Authorization);

      if (usesPrivateRoute && !hasAuthorization) {
        throw new AppError('Finance API Basic Auth credentials are required for private routes.', 500, 'FINANCE_API_AUTH_NOT_CONFIGURED');
      }

      logger.info(
        {
          method: config.method,
          url: config.url,
          data: config.data,
        },
        'Calling finance API',
      );

      return config;
    });

    this.http.interceptors.response.use((response) => {
      logger.info(
        {
          status: response.status,
          url: response.config.url,
          method: response.config.method,
        },
        'Finance API response received',
      );

      return response;
    });
  }

  async request<T = unknown>(method: Method, path: string, data?: unknown, config?: FinanceRequestConfig): Promise<T> {
    const { basicAuth, headers, ...axiosConfig } = config ?? {};
    const authHeaders: Record<string, string> = {};

    if (basicAuth) {
      const credentials = Buffer.from(`${basicAuth.username}:${basicAuth.password}`).toString('base64');
      authHeaders.Authorization = `Basic ${credentials}`;
    }

    const response = await this.http.request<T>({
      method,
      url: path,
      data,
      headers: {
        ...headers,
        ...authHeaders,
      },
      ...axiosConfig,
    });

    return response.data;
  }

  get<T = unknown>(path: string, config?: FinanceRequestConfig): Promise<T> {
    return this.request<T>('GET', path, undefined, config);
  }

  post<T = unknown>(path: string, data?: unknown, config?: FinanceRequestConfig): Promise<T> {
    return this.request<T>('POST', path, data, config);
  }

  put<T = unknown>(path: string, data?: unknown, config?: FinanceRequestConfig): Promise<T> {
    return this.request<T>('PUT', path, data, config);
  }
}
