// Daraja (M-Pesa) STK Push helper. Server-only.
// Reads credentials from env; when not configured, returns a soft failure so
// the payment stays "pending" and staff can reconcile manually.

type StkPushResult =
  | { ok: true; checkout_request_id: string; merchant_request_id: string }
  | { ok: false; error: string; soft?: boolean };

function env(name: string): string | undefined {
  const v = process.env[name];
  return v && v.length > 0 ? v : undefined;
}

function baseUrl() {
  return env("MPESA_ENV") === "production"
    ? "https://api.safaricom.co.ke"
    : "https://sandbox.safaricom.co.ke";
}

async function getAccessToken(): Promise<string | null> {
  const key = env("MPESA_CONSUMER_KEY");
  const secret = env("MPESA_CONSUMER_SECRET");
  if (!key || !secret) return null;
  const auth = Buffer.from(`${key}:${secret}`).toString("base64");
  const res = await fetch(`${baseUrl()}/oauth/v1/generate?grant_type=client_credentials`, {
    headers: { Authorization: `Basic ${auth}` },
  });
  if (!res.ok) return null;
  const data = (await res.json()) as { access_token?: string };
  return data.access_token ?? null;
}

function timestamp(): string {
  const d = new Date();
  const p = (n: number) => String(n).padStart(2, "0");
  return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
}

export function isMpesaConfigured(): boolean {
  return !!(
    env("MPESA_CONSUMER_KEY") &&
    env("MPESA_CONSUMER_SECRET") &&
    env("MPESA_SHORTCODE") &&
    env("MPESA_PASSKEY")
  );
}

export async function stkPush(opts: {
  phone: string; // 2547XXXXXXXX
  amount: number;
  reference: string;
  description: string;
  callbackUrl: string;
}): Promise<StkPushResult> {
  if (!isMpesaConfigured()) {
    return {
      ok: false,
      soft: true,
      error: "M-Pesa not configured — payment will require manual reconciliation.",
    };
  }
  const shortcode = env("MPESA_SHORTCODE")!;
  const passkey = env("MPESA_PASSKEY")!;
  const ts = timestamp();
  const password = Buffer.from(`${shortcode}${passkey}${ts}`).toString("base64");

  const token = await getAccessToken();
  if (!token) return { ok: false, error: "Could not authenticate with M-Pesa" };

  const body = {
    BusinessShortCode: shortcode,
    Password: password,
    Timestamp: ts,
    TransactionType: "CustomerPayBillOnline",
    Amount: Math.max(1, Math.round(opts.amount)),
    PartyA: opts.phone,
    PartyB: shortcode,
    PhoneNumber: opts.phone,
    CallBackURL: opts.callbackUrl,
    AccountReference: opts.reference.slice(0, 12),
    TransactionDesc: opts.description.slice(0, 20),
  };

  const res = await fetch(`${baseUrl()}/mpesa/stkpush/v1/processrequest`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  const data = (await res.json().catch(() => ({}))) as {
    CheckoutRequestID?: string;
    MerchantRequestID?: string;
    errorMessage?: string;
    ResponseCode?: string;
  };
  if (!res.ok || data.ResponseCode !== "0" || !data.CheckoutRequestID) {
    return { ok: false, error: data.errorMessage || "M-Pesa request failed" };
  }
  return {
    ok: true,
    checkout_request_id: data.CheckoutRequestID,
    merchant_request_id: data.MerchantRequestID ?? "",
  };
}
