/**
 * Authorize.Net Accept Hosted Payment Integration
 *
 * Uses the Accept Hosted payment form (iframe/redirect) for PCI-compliant checkout.
 * API reference: https://developer.authorize.net/api/reference/index.html
 *
 * Flow:
 * 1. Backend creates a hosted payment page token via getHostedPaymentPageRequest
 * 2. Frontend redirects user to Authorize.Net hosted form
 * 3. On payment completion, Authorize.Net posts a Silent Post webhook
 * 4. Backend also has a verify endpoint as fallback (getTransactionDetails)
 */

import crypto from 'crypto';
import pool from '../db';

const AUTHNET_API_LOGIN_ID = process.env.AUTHNET_API_LOGIN_ID || '';
const AUTHNET_TRANSACTION_KEY = process.env.AUTHNET_TRANSACTION_KEY || '';
const AUTHNET_SIGNATURE_KEY = process.env.AUTHNET_SIGNATURE_KEY || '';
const AUTHNET_SANDBOX = process.env.AUTHNET_SANDBOX === 'true';

const API_URL = AUTHNET_SANDBOX
  ? 'https://apitest.authorize.net/xml/v1/request.api'
  : 'https://api.authorize.net/xml/v1/request.api';

const HOSTED_FORM_URL = AUTHNET_SANDBOX
  ? 'https://test.authorize.net/payment/payment'
  : 'https://accept.authorize.net/payment/payment';

export const PRODUCTS: Record<string, {
  name: string;
  amount: number; // cents
  amountDollars: string; // "9.99"
  picks: number;
  recurring?: boolean;
  intervalMonths?: number;
}> = {
  single_pick: {
    name: 'Single Forecast',
    amount: 100,
    amountDollars: '1.00',
    picks: 1,
  },
  daily_pass: {
    name: 'Today Only Pass',
    amount: 499,
    amountDollars: '4.99',
    picks: 999,
  },
  monthly_pass: {
    name: 'The Rain Man — 30 Day Unlimited',
    amount: 1999,
    amountDollars: '19.99',
    picks: 999999,
  },
};

/** Make an Authorize.Net API call */
async function authnetRequest(requestType: string, payload: any): Promise<any> {
  const body = {
    [requestType]: {
      merchantAuthentication: {
        name: AUTHNET_API_LOGIN_ID,
        transactionKey: AUTHNET_TRANSACTION_KEY,
      },
      ...payload,
    },
  };

  const res = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });

  // Authorize.Net returns JSON with a BOM sometimes — strip it
  const text = await res.text();
  const clean = text.replace(/^\uFEFF/, '');
  return JSON.parse(clean);
}

/**
 * Create a hosted payment page token for one-time payments.
 * Returns a token that the frontend uses to redirect to Accept Hosted.
 */
export async function createCheckoutSession(data: {
  userId: string;
  email: string;
  productType: 'single_pick' | 'daily_pass' | 'monthly_pass';
  successUrl: string;
  cancelUrl: string;
  clientTimezone?: string;
  purchaseIp?: string;
  attribution?: any;
}): Promise<{ token: string; formUrl: string }> {
  const product = PRODUCTS[data.productType];
  if (!product) throw new Error('Invalid product type');

  const appendQueryParam = (urlString: string, key: string, value: string): string => {
    try {
      const url = new URL(urlString);
      url.searchParams.set(key, value);
      return url.toString();
    } catch {
      const separator = urlString.includes('?') ? '&' : '?';
      return `${urlString}${separator}${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
    }
  };

  // Store pending order in DB so we can look it up on callback
  // Authorize.Net invoiceNumber max length is tight; keep this short and unique.
  const invoiceNumber = `RM${Date.now().toString().slice(-8)}${Math.random().toString(36).slice(2, 6).toUpperCase()}`;
  const successUrl = appendQueryParam(data.successUrl, 'invoiceNumber', invoiceNumber);

  await pool.query(
    `INSERT INTO rm_pending_orders (invoice_number, user_id, product_type, amount_cents, picks_granted, client_timezone, purchase_ip, status, attribution)
     VALUES ($1, $2, $3, $4, $5, $6, $7, 'pending', $8)`,
    [
      invoiceNumber,
      data.userId,
      data.productType,
      product.amount,
      product.picks,
      data.clientTimezone || '',
      data.purchaseIp || '',
      data.attribution ? JSON.stringify(data.attribution) : null,
    ]
  );

  // One-time payment
  const response = await authnetRequest('getHostedPaymentPageRequest', {
    transactionRequest: {
      transactionType: 'authCaptureTransaction',
      amount: product.amountDollars,
      order: {
        invoiceNumber,
        description: product.name,
      },
      customer: {
        email: data.email,
      },
      userFields: {
        userField: [
          { name: 'userId', value: data.userId },
          { name: 'productType', value: data.productType },
          { name: 'picksGranted', value: String(product.picks) },
          { name: 'clientTimezone', value: data.clientTimezone || '' },
          { name: 'purchaseIp', value: data.purchaseIp || '' },
          { name: 'invoiceNumber', value: invoiceNumber },
        ],
      },
    },
    hostedPaymentSettings: {
      setting: [
        {
          settingName: 'hostedPaymentReturnOptions',
          settingValue: JSON.stringify({
            showReceipt: false,
            url: successUrl,
            urlText: 'Return to Rainmaker',
            cancelUrl: data.cancelUrl,
            cancelUrlText: 'Cancel',
          }),
        },
        {
          settingName: 'hostedPaymentButtonOptions',
          settingValue: JSON.stringify({ text: `Pay $${product.amountDollars}` }),
        },
        {
          settingName: 'hostedPaymentStyleOptions',
          settingValue: JSON.stringify({ bgColor: '0a0c12' }),
        },
        {
          settingName: 'hostedPaymentOrderOptions',
          settingValue: JSON.stringify({ show: true, merchantName: 'Rainmaker Sports' }),
        },
        {
          settingName: 'hostedPaymentPaymentOptions',
          settingValue: JSON.stringify({ cardCodeRequired: true, showCreditCard: true, showBankAccount: false }),
        },
        {
          settingName: 'hostedPaymentSecurityOptions',
          settingValue: JSON.stringify({ captcha: false }),
        },
        {
          settingName: 'hostedPaymentCustomerOptions',
          settingValue: JSON.stringify({ showEmail: false, requiredEmail: false }),
        },
      ],
    },
  });

  if (response.messages?.resultCode !== 'Ok') {
    const errMsg = response.messages?.message?.[0]?.text || 'Unknown error';
    console.error('[authnet] Failed to create hosted page:', errMsg);
    throw new Error(`Authorize.Net error: ${errMsg}`);
  }

  return { token: response.token, formUrl: HOSTED_FORM_URL };
}

/**
 * Verify a transaction by ID using getTransactionDetailsRequest.
 * Called from the /verify endpoint after user returns from checkout.
 */
export async function verifyTransaction(transactionId: string): Promise<{
  paid: boolean;
  transactionId: string;
  invoiceNumber: string;
  amountCents: number;
  responseCode: string;
} | null> {
  const response = await authnetRequest('getTransactionDetailsRequest', {
    transId: transactionId,
  });

  if (response.messages?.resultCode !== 'Ok') {
    console.error('[authnet] Transaction lookup failed:', response.messages?.message?.[0]?.text);
    return null;
  }

  const txn = response.transaction;
  if (!txn) return null;

  // responseCode 1 = Approved
  const paid = txn.responseCode === 1 || txn.transactionStatus === 'settledSuccessfully' || txn.transactionStatus === 'capturedPendingSettlement';

  return {
    paid,
    transactionId: txn.transId,
    invoiceNumber: txn.order?.invoiceNumber || '',
    amountCents: Math.round(parseFloat(txn.settleAmount || txn.authAmount || '0') * 100),
    responseCode: String(txn.responseCode),
  };
}

/**
 * Look up a pending order by invoice number.
 */
export async function getPendingOrder(invoiceNumber: string): Promise<{
  user_id: string;
  product_type: string;
  amount_cents: number;
  picks_granted: number;
  client_timezone: string;
  purchase_ip: string;
  status: string;
  attribution: any;
} | null> {
  const { rows } = await pool.query(
    'SELECT * FROM rm_pending_orders WHERE invoice_number = $1',
    [invoiceNumber]
  );
  return rows[0] || null;
}

/**
 * Mark a pending order as completed.
 */
export async function completePendingOrder(invoiceNumber: string, transactionId: string): Promise<void> {
  await pool.query(
    `UPDATE rm_pending_orders SET status = 'completed', transaction_id = $1, completed_at = NOW() WHERE invoice_number = $2`,
    [transactionId, invoiceNumber]
  );
}

/**
 * Validate Authorize.Net webhook signature (SHA-512 HMAC).
 * Authorize.Net signs webhooks with: SHA512(SIGNATURE_KEY + LOGIN_ID + transId + amount)
 */
export function validateWebhookSignature(
  xSignature: string,
  transId: string,
  amount: string
): boolean {
  if (!AUTHNET_SIGNATURE_KEY) {
    console.error('[authnet] AUTHNET_SIGNATURE_KEY is not configured — rejecting webhook');
    return false;
  }

  if (!/^[0-9a-fA-F]+$/.test(AUTHNET_SIGNATURE_KEY) || AUTHNET_SIGNATURE_KEY.length % 2 !== 0) {
    console.error('[authnet] AUTHNET_SIGNATURE_KEY is not valid hex — rejecting webhook');
    return false;
  }

  const hashValue = `^${AUTHNET_API_LOGIN_ID}^${transId}^${amount}^`;
  const expectedHash = crypto
    .createHmac('sha512', Buffer.from(AUTHNET_SIGNATURE_KEY, 'hex'))
    .update(hashValue)
    .digest('hex')
    .toUpperCase();

  return expectedHash === xSignature.toUpperCase();
}

/**
 * Parse Authorize.Net Silent Post / Webhook notification body.
 * Silent Post sends form-encoded data; webhooks send JSON.
 */
export function parseWebhookPayload(body: any): {
  transactionId: string;
  invoiceNumber: string;
  responseCode: string;
  amount: string;
  userId: string;
  productType: string;
  picksGranted: number;
  clientTimezone: string;
  purchaseIp: string;
} | null {
  // JSON webhook format (Authorize.Net Webhooks v2)
  if (body.payload) {
    const p = body.payload;
    return {
      transactionId: p.id || '',
      invoiceNumber: p.invoiceNumber || '',
      responseCode: String(p.responseCode || ''),
      amount: String(p.authAmount || '0'),
      userId: '', // Will be looked up from pending_orders
      productType: '',
      picksGranted: 0,
      clientTimezone: '',
      purchaseIp: '',
    };
  }

  // Form-encoded Silent Post format
  if (body.x_trans_id) {
    // Extract user fields
    const userFields: Record<string, string> = {};
    // Authorize.Net sends userfields as x_user_field_N
    for (const [key, val] of Object.entries(body)) {
      if (key.startsWith('x_')) {
        userFields[key] = String(val);
      }
    }

    return {
      transactionId: body.x_trans_id || '',
      invoiceNumber: body.x_invoice_num || '',
      responseCode: body.x_response_code || '',
      amount: body.x_amount || '0',
      userId: body.userId || '',
      productType: body.productType || '',
      picksGranted: parseInt(body.picksGranted || '0', 10),
      clientTimezone: body.clientTimezone || '',
      purchaseIp: body.purchaseIp || '',
    };
  }

  return null;
}

export { AUTHNET_SANDBOX, HOSTED_FORM_URL };
