import type { APIRoute } from 'astro';
import { env } from 'cloudflare:workers';
import type { Querier } from '@platform-modules/db';
import { sql } from 'drizzle-orm';
import {
  ingestWebhook,
  type LedgerSeam,
  type WebhookDispatch,
} from '@platform-modules/billing';
import { stripe } from '@platform-modules/billing/stripe';
import {
  settleCheckout,
  parseOrderId,
  type CheckoutDeps,
  type OrdersSchema,
} from '@platform-modules/commerce-checkout';
import { getOrderById } from '@platform-modules/commerce-orders';
import { appendEntry } from '@platform-modules/ledger';
import { createMail } from '@platform-modules/mail';
import { getTransactionalDb, type DbEnv } from '../../../lib/db.js';
import { buildMailAdapter, type MailEnv } from '../../../lib/mail.js';
import { createDbDedupStore } from '../../../lib/checkout-store.js';
import {
  createStorefrontFulfillmentPorts,
  createR2StorageAdapter,
  createNoOpStorageAdapter,
} from '../../../lib/fulfillment.js';
import { jsonError } from '../../../lib/http.js';

export const prerender = false;

const MAX_WEBHOOK_BODY_BYTES = 512 * 1024;

/** Stream-read the body with a byte cap — never trust Content-Length (chunked TE has none). */
async function readCappedWebhookBody(
  request: Request,
): Promise<{ ok: true; body: string } | { ok: false }> {
  const reader = request.body?.getReader();
  if (!reader) return { ok: true, body: '' };

  const chunks: Uint8Array[] = [];
  let total = 0;
  for (;;) {
    const { done, value } = await reader.read();
    if (done) break;
    if (!value) continue;
    total += value.byteLength;
    if (total > MAX_WEBHOOK_BODY_BYTES) {
      await reader.cancel();
      return { ok: false };
    }
    chunks.push(value);
  }

  const merged = new Uint8Array(total);
  let offset = 0;
  for (const chunk of chunks) {
    merged.set(chunk, offset);
    offset += chunk.byteLength;
  }
  return { ok: true, body: new TextDecoder().decode(merged) };
}

type WebhookEnv = DbEnv & MailEnv & {
  STRIPE_SECRET_KEY?: string;
  STRIPE_WEBHOOK_SECRET?: string;
  MEDIA?: R2Bucket;
};

async function lookupUserEmail(db: Querier, userId: string): Promise<string | null> {
  const result = await db.execute(sql`SELECT email FROM auth_users WHERE id = ${userId} LIMIT 1`);
  const rows = (Array.isArray(result) ? result : (result as { rows?: unknown[] }).rows ?? []) as Array<{ email?: unknown }>;
  const row = rows[0];
  return typeof row?.email === 'string' ? row.email : null;
}

export const POST: APIRoute = async ({ request }) => {
  const cfEnv = env as WebhookEnv | undefined;
  if (!cfEnv?.STRIPE_SECRET_KEY || !cfEnv?.STRIPE_WEBHOOK_SECRET) {
    return new Response('payment provider not configured', { status: 503 });
  }
  if (!cfEnv?.DB && !cfEnv?.DATABASE_URL) {
    return new Response('database not configured', { status: 503 });
  }

  const { db } = getTransactionalDb(cfEnv);
  const ledger: LedgerSeam = { appendEntry: appendEntry as LedgerSeam['appendEntry'] };
  const provider = stripe({ secretKey: cfEnv.STRIPE_SECRET_KEY, webhookSecret: cfEnv.STRIPE_WEBHOOK_SECRET });
  const dedupStore = createDbDedupStore(db as unknown as Querier);
  const storage = cfEnv.MEDIA ? createR2StorageAdapter(cfEnv.MEDIA) : createNoOpStorageAdapter();
  const fulfillment = createStorefrontFulfillmentPorts(
    db as unknown as import('@platform-modules/db').TransactionalDatabase<import('@platform-modules/commerce-fulfillment').FulfillmentDbSchema>,
    storage,
  );

  const dispatch: WebhookDispatch = async (event) => {
    if (event.kind !== 'settlement') return;
    let orderId: string;
    try {
      orderId = parseOrderId(event.chargeKey);
    } catch {
      return;
    }
    const settleDeps = { db, ledger, fulfillment } as unknown as CheckoutDeps<OrdersSchema>;
    await settleCheckout(settleDeps, orderId, event.providerRef, event.amount);

    try {
      const adapter = buildMailAdapter(cfEnv as MailEnv);
      if (adapter) {
        const order = await getOrderById(db as unknown as Querier<OrdersSchema>, orderId, { isAdmin: true });
        const buyerUserId = order && 'userId' in order.buyerRef ? order.buyerRef.userId : null;
        const email = buyerUserId ? await lookupUserEmail(db as unknown as Querier, buyerUserId) : null;
        if (email) {
          await createMail(adapter).send({
            from: (cfEnv as MailEnv).MAIL_FROM ?? 'noreply@mod-storefront.example',
            to: email,
            subject: `Order confirmed – #${orderId.slice(-6).toUpperCase()}`,
            text: `Your order has been confirmed. Order ID: ${orderId}`,
          });
        }
      }
    } catch {
      // mail failure must not fail webhook — Stripe re-delivers on 5xx
    }
  };

  const capped = await readCappedWebhookBody(request);
  if (!capped.ok) {
    return jsonError(413, 'payload_too_large', 'Request body exceeds size limit.');
  }

  const cappedRequest = new Request(request.url, {
    method: request.method,
    headers: request.headers,
    body: capped.body,
  });

  return ingestWebhook(cappedRequest, {
    provider,
    dedupStore,
    dispatch,
    ledger,
    db: db as unknown as Querier,
  });
};
