import { endpoints } from './constants/endpoints';
import { httpRequest, HttpClientOptions } from './httpClient';
import { PixCreateImmediateChargeBody, PixCreateImmediateChargeResponse } from './types/pix';
import axios from 'axios';
import https from 'https';
import fs from 'fs';

export interface SdkOptions {
  sandbox: boolean;
  client_id: string;
  client_secret: string;
  certificate?: string;
  pemKey?: string;
  cert_base64?: boolean;
  partner_token?: string;
  validateMtls?: boolean;
  idempotencyKey?: string;
}

interface AuthData {
  access_token: string;
  expires_in: number;
  token_type: string;
  scope?: string;
  authDate: number; // timestamp em segundos
}

type ApiType = 'PIX' | 'DEFAULT';

function getApiConfig(api: ApiType, sandbox: boolean) {
  const apiConfig = endpoints.APIS[api];
  return {
    baseUrl: sandbox ? apiConfig.URL.SANDBOX : apiConfig.URL.PRODUCTION,
    authRoute: apiConfig.ENDPOINTS.authorize.route,
  };
}

export class EfiSdk {
  private options: SdkOptions;
  private tokenMap: Record<ApiType, AuthData | null> = { PIX: null, DEFAULT: null };

  constructor(options: SdkOptions) {
    if (!options.client_id || !options.client_secret) {
      throw new Error('client_id e client_secret são obrigatórios');
    }
    this.options = options;
  }

  private isTokenExpired(token: AuthData | null): boolean {
    if (!token) return true;
    const now = Math.floor(Date.now() / 1000);
    if (typeof token.expires_in !== 'number' || typeof token.authDate !== 'number') return true;
    return now > (token.authDate + token.expires_in - 60); // 60s de margem
  }

  private buildAgent(): https.Agent | undefined {
    if (!this.options.certificate) return undefined;
    try {
      if (!this.options.cert_base64) {
        if (this.options.pemKey) {
          return new https.Agent({
            cert: fs.readFileSync(this.options.certificate),
            key: fs.readFileSync(this.options.pemKey),
            passphrase: '',
          });
        } else {
          return new https.Agent({
            pfx: fs.readFileSync(this.options.certificate),
            passphrase: '',
          });
        }
      } else {
        if (this.options.pemKey) {
          return new https.Agent({
            cert: Buffer.from(this.options.certificate, 'base64'),
            key: Buffer.from(this.options.pemKey, 'base64'),
            passphrase: '',
          });
        } else {
          return new https.Agent({
            pfx: Buffer.from(this.options.certificate, 'base64'),
            passphrase: '',
          });
        }
      }
    } catch (error) {
      throw new Error('Erro ao ler o certificado ou chave: ' + (error as Error).message);
    }
  }

  private async authenticate(api: ApiType): Promise<string> {
    const { baseUrl, authRoute } = getApiConfig(api, this.options.sandbox);
    const currentToken = this.tokenMap[api];
    if (currentToken && !this.isTokenExpired(currentToken)) {
      return currentToken.access_token;
    }
    const agent = this.buildAgent();
    const isDefault = api === 'DEFAULT';
    let authParams: any = {
      method: 'POST',
      url: baseUrl + authRoute,
      headers: {
        'api-sdk': 'efi-node-typescript',
      },
      data: {
        grant_type: 'client_credentials',
      },
      httpsAgent: agent,
    };
    if (isDefault) {
      authParams.auth = {
        username: this.options.client_id,
        password: this.options.client_secret,
      };
    } else {
      const token = Buffer.from(this.options.client_id + ':' + this.options.client_secret).toString('base64');
      authParams.headers['Authorization'] = 'Basic ' + token;
      authParams.headers['Content-Type'] = 'application/json';
    }
    const res = await axios(authParams);
    this.tokenMap[api] = {
      ...res.data,
      authDate: Math.floor(Date.now() / 1000),
    };
    return res.data.access_token;
  }

  private async authorizedRequest<T = any>(api: ApiType, opts: Omit<HttpClientOptions, 'authorization' | 'baseUrl'>): Promise<T> {
    const { baseUrl } = getApiConfig(api, this.options.sandbox);
    const access_token = await this.authenticate(api);
    return httpRequest<T>({
      ...opts,
      baseUrl,
      authorization: `Bearer ${access_token}`,
    });
  }

  async pixCreateImmediateCharge(body: PixCreateImmediateChargeBody, headers?: Record<string, string>): Promise<PixCreateImmediateChargeResponse> {
    return this.authorizedRequest<PixCreateImmediateChargeResponse>('PIX', {
      endpoint: endpoints.APIS.PIX.ENDPOINTS.pixCreateImmediateCharge,
      body,
      headers,
      certificate: this.options.certificate,
      pemKey: this.options.pemKey,
      cert_base64: this.options.cert_base64,
      partner_token: this.options.partner_token,
      validateMtls: this.options.validateMtls,
      idempotencyKey: this.options.idempotencyKey,
    });
  }

  // Exemplo de método para DEFAULT (ex: listCharges)
  async listCharges(params?: Record<string, any>, headers?: Record<string, string>): Promise<any> {
    return this.authorizedRequest<any>('DEFAULT', {
      endpoint: endpoints.APIS.DEFAULT.ENDPOINTS.listCharges,
      params,
      headers,
      certificate: this.options.certificate,
      pemKey: this.options.pemKey,
      cert_base64: this.options.cert_base64,
      partner_token: this.options.partner_token,
      validateMtls: this.options.validateMtls,
      idempotencyKey: this.options.idempotencyKey,
    });
  }

  // Adicione outros métodos do SDK aqui, seguindo o mesmo padrão
} 