// ============================================================
// Lexware Office API - Rate-limited HTTP Client
// ============================================================

import {
  Injectable,
  Logger,
  OnModuleInit,
  ServiceUnavailableException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpService } from '@nestjs/axios';
import { AxiosError } from 'axios';
import { firstValueFrom } from 'rxjs';
import { RateLimiter } from './utils/rate-limiter';

@Injectable()
export class LexwareClientService implements OnModuleInit {
  private readonly logger = new Logger(LexwareClientService.name);
  private readonly rateLimiter: RateLimiter;
  private enabled = false;

  constructor(
    private readonly httpService: HttpService,
    private readonly config: ConfigService,
  ) {
    const rateLimit = this.config.get<number>('LEXWARE_RATE_LIMIT', 2);
    this.rateLimiter = new RateLimiter(rateLimit, rateLimit);
  }

  async onModuleInit(): Promise<void> {
    const apiKey = this.config.get<string>('LEXWARE_API_KEY');
    if (!apiKey) {
      this.logger.warn(
        'LEXWARE_API_KEY nicht konfiguriert. Lexware-Integration deaktiviert.',
      );
      this.enabled = false;
      return;
    }

    this.enabled = true;
    this.logger.log('Lexware Office Integration aktiviert.');
  }

  /**
   * Prueft ob die Lexware-Integration konfiguriert und erreichbar ist.
   */
  isEnabled(): boolean {
    return this.enabled;
  }

  /**
   * Health-Check: Prueft Lexware API Erreichbarkeit.
   */
  async isHealthy(): Promise<'up' | 'down' | 'unconfigured'> {
    if (!this.enabled) return 'unconfigured';

    try {
      await this.get<unknown>('/v1/profile');
      return 'up';
    } catch {
      return 'down';
    }
  }

  /**
   * GET Request an Lexware API (rate-limited).
   */
  async get<T>(
    path: string,
    params?: Record<string, string | number | boolean>,
  ): Promise<T> {
    this.ensureEnabled();
    return this.executeWithRetry<T>('GET', path, undefined, params);
  }

  /**
   * POST Request an Lexware API (rate-limited).
   */
  async post<T>(path: string, body: unknown): Promise<T> {
    this.ensureEnabled();
    return this.executeWithRetry<T>('POST', path, body);
  }

  /**
   * PUT Request an Lexware API (rate-limited).
   */
  async put<T>(path: string, body: unknown): Promise<T> {
    this.ensureEnabled();
    return this.executeWithRetry<T>('PUT', path, body);
  }

  /**
   * DELETE Request an Lexware API (rate-limited).
   */
  async delete(path: string): Promise<void> {
    this.ensureEnabled();
    await this.executeWithRetry<void>('DELETE', path);
  }

  // --------------------------------------------------------
  // Internes: Retry-Logik mit Rate Limiting
  // --------------------------------------------------------

  private async executeWithRetry<T>(
    method: string,
    path: string,
    body?: unknown,
    params?: Record<string, string | number | boolean>,
    retries = 3,
  ): Promise<T> {
    for (let attempt = 1; attempt <= retries; attempt++) {
      try {
        await this.rateLimiter.acquire();

        const response = await firstValueFrom(
          method === 'GET'
            ? this.httpService.get<T>(path, { params })
            : method === 'POST'
              ? this.httpService.post<T>(path, body)
              : method === 'PUT'
                ? this.httpService.put<T>(path, body)
                : this.httpService.delete<T>(path),
        );

        return response.data;
      } catch (error) {
        const axiosError = error as AxiosError;
        const status = axiosError.response?.status;

        // 429 Too Many Requests -> Backoff und Retry
        if (status === 429 && attempt < retries) {
          const backoffMs = Math.pow(2, attempt) * 1000; // 2s, 4s
          this.logger.warn(
            `Lexware Rate Limit erreicht (429). Warte ${backoffMs}ms (Versuch ${attempt}/${retries})`,
          );
          await new Promise((resolve) => setTimeout(resolve, backoffMs));
          continue;
        }

        // 401/403 -> API Key Problem
        if (status === 401 || status === 403) {
          this.logger.error(
            `Lexware API Authentifizierungsfehler (${status}). API Key pruefen.`,
          );
          throw new ServiceUnavailableException(
            'Lexware Office API nicht erreichbar (Authentifizierung fehlgeschlagen)',
          );
        }

        // Andere Fehler oder letzter Retry
        if (attempt === retries) {
          const msg =
            axiosError.response?.statusText ||
            axiosError.message ||
            'Unbekannter Fehler';
          const responseBody = axiosError.response?.data
            ? JSON.stringify(axiosError.response.data).substring(0, 500)
            : '';
          this.logger.error(
            `Lexware API Fehler: ${method} ${path} -> ${status || 'NETWORK'}: ${msg}${responseBody ? ` | Body: ${responseBody}` : ''}`,
          );
          throw error;
        }

        // Netzwerk-Fehler -> kurzer Backoff
        const backoffMs = attempt * 1000;
        this.logger.warn(
          `Lexware API Fehler (${status || 'NETWORK'}). Retry in ${backoffMs}ms (${attempt}/${retries})`,
        );
        await new Promise((resolve) => setTimeout(resolve, backoffMs));
      }
    }

    // Sollte nicht erreicht werden
    throw new ServiceUnavailableException('Lexware API nicht erreichbar');
  }

  private ensureEnabled(): void {
    if (!this.enabled) {
      throw new ServiceUnavailableException(
        'Lexware Office Integration ist nicht konfiguriert. LEXWARE_API_KEY fehlt.',
      );
    }
  }
}
