import { Logger } from "@nestjs/common";
import { HttpService } from "@nestjs/axios";
import { AxiosError } from "axios";
import { firstValueFrom } from "rxjs";
import type { CacSigninRequest, CacSigninResponse } from "./cac-bank.types";

interface TokenCache {
  accessToken: string;
  expiresAt: number;
}

export interface CacAuthConfig {
  baseUrl: string;
  username: string;
  password: string;
  tokenTtlMs: number;
}

/**
 * In-memory JWT cache for CAC Bank. Tokens are valid 24h per the API docs;
 * we refresh proactively before expiry.
 */
export class CacBankAuth {
  private readonly logger = new Logger(CacBankAuth.name);
  private cache: TokenCache | null = null;
  private signinInFlight: Promise<string> | null = null;

  constructor(
    private readonly http: HttpService,
    private readonly config: CacAuthConfig,
  ) {}

  async getAccessToken(): Promise<string> {
    if (this.cache && Date.now() < this.cache.expiresAt) {
      return this.cache.accessToken;
    }
    return this.signin();
  }

  invalidate(): void {
    this.cache = null;
  }

  private async signin(): Promise<string> {
    if (this.signinInFlight) return this.signinInFlight;

    this.signinInFlight = this.doSignin();
    try {
      return await this.signinInFlight;
    } finally {
      this.signinInFlight = null;
    }
  }

  private async doSignin(): Promise<string> {
    const body: CacSigninRequest = {
      username: this.config.username,
      password: this.config.password,
    };
    const url = `${this.config.baseUrl}/paymentapi/auth/signin`;

    try {
      const res = await firstValueFrom(
        this.http.post<CacSigninResponse>(url, body, {
          headers: { "Content-Type": "application/json" },
          timeout: 10_000,
        }),
      );
      const token = res.data.accessToken;
      if (!token) {
        throw new Error("CAC signin returned no accessToken");
      }
      this.cache = {
        accessToken: token,
        expiresAt: Date.now() + this.config.tokenTtlMs,
      };
      this.logger.debug("CAC signin succeeded; token cached");
      return token;
    } catch (err) {
      if (err instanceof AxiosError) {
        this.logger.error(
          `CAC signin failed: status=${err.response?.status} body=${JSON.stringify(err.response?.data)}`,
        );
      }
      throw err;
    }
  }
}
