/**
 * Coinbase Onramp Provider
 *
 * Generates session tokens for the Coinbase-hosted Onramp widget.
 * Uses Ed25519 JWT auth against the CDP API to get a session token,
 * then constructs a one-click-buy URL for the iframe.
 *
 * Docs: https://docs.cdp.coinbase.com/onramp/docs/welcome
 */

import { SignJWT } from 'jose';
import { randomBytes, createPrivateKey } from 'crypto';
import type { OnrampProvider, OnrampSessionRequest, OnrampSessionResult } from './onramp-provider';

const CDP_API_HOST = 'api.developer.coinbase.com';
const TOKEN_PATH = '/onramp/v1/token';
const ONRAMP_BASE_URL = 'https://pay.coinbase.com/buy';

export interface CoinbaseOnrampConfig {
  apiKeyId: string;
  apiKeySecret: string; // Ed25519 private key (PEM or base64)
  projectId: string;
  environment?: 'sandbox' | 'production';
}

export interface CoinbaseSessionOpts {
  walletAddress: string;
  fiatAmount: number;
  fiatCurrency: string;
  network?: string;
  asset?: string;
  partnerUserRef?: string;
  redirectUrl?: string;
}

export interface CoinbaseSessionResult {
  sessionToken: string;
  onrampUrl: string;
}

export class CoinbaseOnrampProvider {
  private config: CoinbaseOnrampConfig;

  constructor(config: CoinbaseOnrampConfig) {
    this.config = config;
  }

  /**
   * Generate a CDP JWT for authenticating against the onramp token endpoint.
   * Uses EdDSA (Ed25519) signing per CDP docs.
   */
  private async generateJwt(): Promise<string> {
    const now = Math.floor(Date.now() / 1000);
    const nonce = randomBytes(16).toString('hex');

    const keySecret = this.config.apiKeySecret.replace(/\\n/g, '\n').trim();

    let privateKey: import('crypto').KeyObject;
    if (keySecret.includes('-----BEGIN')) {
      privateKey = createPrivateKey({ key: keySecret, format: 'pem' });
    } else {
      // Raw base64 key from CDP portal (64 bytes: 32-byte seed + 32-byte pubkey)
      const rawBytes = Buffer.from(keySecret, 'base64');
      const seed = rawBytes.subarray(0, 32);
      // Wrap seed in PKCS8 DER envelope for Ed25519
      const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex');
      const pkcs8Der = Buffer.concat([pkcs8Header, seed]);
      privateKey = createPrivateKey({ key: pkcs8Der, format: 'der', type: 'pkcs8' });
    }

    const jwt = await new SignJWT({
      sub: this.config.apiKeyId,
      iss: 'cdp',
      aud: ['cdp_service'],
      uri: `POST ${CDP_API_HOST}${TOKEN_PATH}`,
    })
      .setProtectedHeader({
        alg: 'EdDSA',
        typ: 'JWT',
        kid: this.config.apiKeyId,
        nonce,
      })
      .setIssuedAt(now)
      .setNotBefore(now)
      .setExpirationTime(now + 120)
      .sign(privateKey);

    return jwt;
  }

  /**
   * Request a session token from the CDP Onramp API.
   * Returns a short-lived token used to construct the widget URL.
   */
  async createSession(opts: CoinbaseSessionOpts): Promise<CoinbaseSessionResult> {
    const network = opts.network ?? 'base';
    const asset = opts.asset ?? 'USDC';

    // Build partnerUserRef — prefix with "sandbox-" for test mode
    let partnerUserRef = opts.partnerUserRef ?? '';
    if (this.config.environment === 'sandbox' && !partnerUserRef.startsWith('sandbox-')) {
      partnerUserRef = `sandbox-${partnerUserRef}`;
    }

    const jwt = await this.generateJwt();

    const body = {
      addresses: [{
        address: opts.walletAddress,
        blockchains: [network],
      }],
      assets: [asset],
    };

    console.log(`[coinbase-onramp] Creating session: wallet=${opts.walletAddress} network=${network} asset=${asset} fiat=${opts.fiatAmount}`);

    const res = await fetch(`https://${CDP_API_HOST}${TOKEN_PATH}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${jwt}`,
      },
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      const errBody = await res.text();
      console.error(`[coinbase-onramp] CDP session token request failed: ${res.status} ${errBody}`);
      throw new Error(`Coinbase Onramp session failed (${res.status}): ${errBody}`);
    }

    const data = await res.json() as { token: string; channel_id?: string };
    const sessionToken = data.token;

    // Construct the one-click-buy URL
    const url = new URL(ONRAMP_BASE_URL);
    url.searchParams.set('sessionToken', sessionToken);
    url.searchParams.set('appId', this.config.projectId);
    url.searchParams.set('defaultAsset', asset);
    url.searchParams.set('defaultNetwork', network);
    url.searchParams.set('defaultPaymentMethod', 'CARD');
    url.searchParams.set('fiatCurrency', opts.fiatCurrency.toUpperCase());
    url.searchParams.set('presetFiatAmount', opts.fiatAmount.toString());

    if (opts.redirectUrl) {
      url.searchParams.set('redirectUrl', opts.redirectUrl);
    }
    if (partnerUserRef) {
      url.searchParams.set('partnerUserId', partnerUserRef);
    }

    const onrampUrl = url.toString();
    console.log(`[coinbase-onramp] URL generated: ${onrampUrl.slice(0, 80)}...`);

    return { sessionToken, onrampUrl };
  }
}

/**
 * Adapter wrapping CoinbaseOnrampProvider behind the generic OnrampProvider interface.
 */
export class CoinbaseOnrampAdapter implements OnrampProvider {
  id = 'coinbase' as const;
  name = 'Coinbase';

  private provider: CoinbaseOnrampProvider | null = null;

  isAvailable(): boolean {
    return !!(process.env.COINBASE_CDP_KEY_ID && process.env.COINBASE_CDP_KEY_SECRET && process.env.COINBASE_CDP_PROJECT_ID);
  }

  private getProvider(): CoinbaseOnrampProvider {
    if (!this.provider) {
      this.provider = new CoinbaseOnrampProvider({
        apiKeyId: process.env.COINBASE_CDP_KEY_ID!,
        apiKeySecret: process.env.COINBASE_CDP_KEY_SECRET!,
        projectId: process.env.COINBASE_CDP_PROJECT_ID!,
      });
    }
    return this.provider;
  }

  async createSession(req: OnrampSessionRequest): Promise<OnrampSessionResult> {
    const result = await this.getProvider().createSession({
      walletAddress: req.walletAddress,
      fiatAmount: req.fiatAmount,
      fiatCurrency: req.fiatCurrency,
      partnerUserRef: req.sessionId,
      redirectUrl: req.returnUrl,
    });

    return { widgetUrl: result.onrampUrl, provider: 'coinbase' };
  }
}
