import { BadRequestException, Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { MerchantProviderConfig, Provider, ProviderMode } from "@prisma/client";
import { EncryptedPayload, EncryptionService } from "../common/crypto/encryption.service";
import { PrismaService } from "../prisma/prisma.service";
import { DlocalAdapter, DlocalCredentials } from "./dlocal/dlocal.adapter";
import { DpagoAdapter, DpagoCredentials } from "./dpago/dpago.adapter";
import { ProviderAdapter } from "./provider-adapter.interface";

interface RouteInput {
  merchantId: string;
  country: string;
  currency: string;
  requestedProvider?: Provider | null;
  mode?: ProviderMode;
}

interface RouteResult {
  config: MerchantProviderConfig;
  adapter: ProviderAdapter;
}

@Injectable()
export class ProviderRouterService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly encryption: EncryptionService,
    private readonly config: ConfigService,
  ) {}

  /** Selecciona el provider config apto y devuelve adapter listo para usar. */
  async route(input: RouteInput): Promise<RouteResult> {
    const candidates = await this.prisma.merchantProviderConfig.findMany({
      where: {
        merchantId: input.merchantId,
        country: input.country,
        active: true,
        ...(input.mode && { mode: input.mode }),
        ...(input.requestedProvider && { provider: input.requestedProvider }),
      },
      orderBy: [{ priority: "desc" }, { createdAt: "desc" }],
    });

    if (candidates.length === 0) {
      throw new BadRequestException(
        `No active provider config for country=${input.country}${input.requestedProvider ? ` provider=${input.requestedProvider}` : ""}`,
      );
    }

    const config = candidates[0];
    const adapter = this.buildAdapter(config);
    return { config, adapter };
  }

  async getById(merchantId: string, providerConfigId: string): Promise<RouteResult> {
    const config = await this.prisma.merchantProviderConfig.findFirst({
      where: { id: providerConfigId, merchantId },
    });
    if (!config) throw new BadRequestException("Provider config not found");
    return { config, adapter: this.buildAdapter(config) };
  }

  private buildAdapter(config: MerchantProviderConfig): ProviderAdapter {
    const credentials = this.encryption.decryptJson<Record<string, unknown>>(
      config.credentialsEncrypted as unknown as EncryptedPayload,
    );

    switch (config.provider) {
      case Provider.dlocal: {
        const baseUrl =
          this.config.get<string>("DLOCAL_GO_BASE_URL") ?? "https://api-sbx.dlocalgo.com";
        return new DlocalAdapter({
          baseUrl,
          mode: config.mode === ProviderMode.live ? "live" : "sandbox",
          credentials: credentials as unknown as DlocalCredentials,
        });
      }
      case Provider.dpago: {
        const baseUrl = this.config.get<string>("DPAGO_API_BASE_URL") ?? "https://api.dpago.com";
        return new DpagoAdapter({
          baseUrl,
          mode: config.mode === ProviderMode.live ? "live" : "sandbox",
          credentials: credentials as unknown as DpagoCredentials,
        });
      }
      default:
        throw new BadRequestException(`Provider ${config.provider} not implemented yet`);
    }
  }
}
