/**
 * GoCardless API client using direct REST calls.
 * Supports Sandbox and Live environments.
 */

const GC_URLS = {
  sandbox: "https://api-sandbox.gocardless.com",
  live: "https://api.gocardless.com",
}

function getBaseUrl(): string {
  return process.env.GOCARDLESS_ENVIRONMENT === "live"
    ? GC_URLS.live
    : GC_URLS.sandbox
}

function getToken(): string | null {
  const token = process.env.GOCARDLESS_ACCESS_TOKEN
  if (!token || token === "sandbox_token" || token === "REPLACE_ME") return null
  return token
}

async function gcFetch(path: string, body: Record<string, unknown>): Promise<Record<string, unknown> | null> {
  const token = getToken()
  if (!token) return null

  const res = await fetch(`${getBaseUrl()}${path}`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "GoCardless-Version": "2015-07-06",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  })

  if (!res.ok) {
    const err = await res.text()
    console.error(`GoCardless API error [${res.status}] ${path}:`, err)
    return null
  }

  return res.json()
}

async function gcGet(path: string): Promise<Record<string, unknown> | null> {
  const token = getToken()
  if (!token) return null

  const res = await fetch(`${getBaseUrl()}${path}`, {
    headers: {
      "Authorization": `Bearer ${token}`,
      "GoCardless-Version": "2015-07-06",
    },
  })

  if (!res.ok) {
    const err = await res.text()
    console.error(`GoCardless API error [${res.status}] ${path}:`, err)
    return null
  }

  return res.json()
}

/**
 * Check if GoCardless is configured and accessible.
 */
export function isGoCardlessConfigured(): boolean {
  return getToken() !== null
}

/**
 * Create a Redirect Flow — the simplest way to set up a Direct Debit mandate.
 * The donor is redirected to GoCardless-hosted pages to enter bank details.
 */
export async function createRedirectFlow(opts: {
  description: string
  reference: string
  pledgeId: string
  successRedirectUrl: string
}): Promise<{ redirectUrl: string; redirectFlowId: string } | null> {
  const data = await gcFetch("/redirect_flows", {
    redirect_flows: {
      description: opts.description,
      session_token: opts.pledgeId,
      success_redirect_url: opts.successRedirectUrl,
      scheme: "bacs",
    },
  })

  if (!data) return null

  const flow = (data as { redirect_flows: { id: string; redirect_url: string } }).redirect_flows
  return {
    redirectUrl: flow.redirect_url,
    redirectFlowId: flow.id,
  }
}

/**
 * Complete a Redirect Flow after the donor returns from GoCardless.
 * This creates the mandate and returns its ID.
 */
export async function completeRedirectFlow(
  redirectFlowId: string,
  sessionToken: string
): Promise<{ mandateId: string; customerId: string } | null> {
  const data = await gcFetch(`/redirect_flows/${redirectFlowId}/actions/complete`, {
    data: {
      session_token: sessionToken,
    },
  })

  if (!data) return null

  const flow = (data as { redirect_flows: { links: { mandate: string; customer: string } } }).redirect_flows
  return {
    mandateId: flow.links.mandate,
    customerId: flow.links.customer,
  }
}

/**
 * Create a payment against an existing mandate.
 */
export async function createPayment(opts: {
  amountPence: number
  mandateId: string
  reference: string
  pledgeId: string
  description: string
}): Promise<{ paymentId: string; status: string; chargeDate: string } | null> {
  const data = await gcFetch("/payments", {
    payments: {
      amount: opts.amountPence,
      currency: "GBP",
      description: opts.description,
      reference: opts.reference,
      metadata: {
        pledge_id: opts.pledgeId,
      },
      links: {
        mandate: opts.mandateId,
      },
    },
  })

  if (!data) return null

  const payment = (data as { payments: { id: string; status: string; charge_date: string } }).payments
  return {
    paymentId: payment.id,
    status: payment.status,
    chargeDate: payment.charge_date,
  }
}

/**
 * Get a payment's current status.
 */
export async function getPayment(paymentId: string): Promise<{ status: string; chargeDate: string } | null> {
  const data = await gcGet(`/payments/${paymentId}`)
  if (!data) return null

  const payment = (data as { payments: { status: string; charge_date: string } }).payments
  return {
    status: payment.status,
    chargeDate: payment.charge_date,
  }
}
