import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { SignJWT, jwtVerify } from "jose";
import { supabaseAdmin } from "@/integrations/supabase/client.server";
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";

const tokenSecret = () => new TextEncoder().encode(process.env.SUPABASE_SERVICE_ROLE_KEY!);

async function signToken(customerId: string) {
  return await new SignJWT({ cid: customerId, kind: "portal" })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime("7d")
    .sign(tokenSecret());
}

async function verifyToken(token: string): Promise<string> {
  try {
    const { payload } = await jwtVerify(token, tokenSecret());
    if (payload.kind !== "portal" || typeof payload.cid !== "string") throw new Error("Invalid token");
    return payload.cid;
  } catch (error: any) {
    const message = String(error?.message || "");
    if (/jwtexpired|exp.*claim|token.*expired/i.test(message)) {
      throw new Error("Portal session expired");
    }
    throw new Error("Invalid portal session");
  }
}

async function assertAdmin(supabase: any, userId: string) {
  const { data } = await supabase.from("user_roles").select("role").eq("user_id", userId).eq("role", "admin").maybeSingle();
  if (!data) throw new Error("Admin only");
}

export const customerPortalLogin = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    mobile: z.string().min(6).max(20),
    password: z.string().min(4).max(128),
  }).parse(d))
  .handler(async ({ data }) => {
    const fail = (msg: string) => ({ ok: false as const, error: msg, token: null, customer: null });
    try {
      const { data: c, error } = await supabaseAdmin
        .from("customers")
        .select("id, name, name_bn, mobile, portal_password_hash, portal_enabled, deleted_at, status, customer_type")
        .eq("mobile", data.mobile)
        .maybeSingle();
      if (error) { console.error("[portalLogin] db error", error); return fail("Invalid credentials"); }
      if (!c || c.deleted_at) return fail("Invalid credentials");
      if (c.status && c.status !== "active") return fail("Account is not active");
      if (!c.portal_enabled || !c.portal_password_hash) return fail("Portal access not enabled");
      const ok = await bcrypt.compare(data.password, c.portal_password_hash);
      if (!ok) return fail("Invalid credentials");
      await supabaseAdmin.from("customers").update({ portal_last_login_at: new Date().toISOString() } as any).eq("id", c.id);
      const token = await signToken(c.id);
      return { ok: true as const, error: null, token, customer: { id: c.id, name: c.name, name_bn: c.name_bn, mobile: c.mobile, customer_type: c.customer_type || "retail" } };
    } catch (e: any) {
      console.error("[portalLogin] exception", e?.message, e?.stack);
      return fail("Login failed");
    }
  });

export const customerPortalGetDashboard = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({ token: z.string().min(10) }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const [cust, wallet, walletTx, sales, withdrawals, allSales, dueAdj, refCount, refList, pendingRefs] = await Promise.all([
      supabaseAdmin.from("customers").select("id,name,name_bn,mobile,email,address,total_due,opening_balance,customer_type,image_url,reference_mobile,portal_last_login_at,status,created_at").eq("id", cid).single(),
      supabaseAdmin.from("referral_wallets").select("*").eq("customer_id", cid).maybeSingle(),
      supabaseAdmin.from("wallet_transactions").select("id,type,amount,note,transaction_date,balance_after").eq("customer_id", cid).is("deleted_at", null).order("transaction_date", { ascending: false }).limit(20),
      supabaseAdmin.from("sales").select("id,invoice_no,sale_date,total_amount,paid_amount,due_amount,payment_method,sale_status").eq("customer_id", cid).order("sale_date", { ascending: false }).limit(20),
      supabaseAdmin.from("withdrawal_requests").select("id,amount,method,account_number,status,created_at,processed_at").eq("customer_id", cid).order("created_at", { ascending: false }).limit(20),
      supabaseAdmin.from("sales").select("total_amount,paid_amount,due_amount,sale_status,sale_date").eq("customer_id", cid),
      supabaseAdmin.from("customer_due_adjustments").select("amount").eq("customer_id", cid),
      supabaseAdmin.from("customers").select("id", { count: "exact", head: true }).eq("reference_customer_id", cid).is("deleted_at", null),
      supabaseAdmin.from("customers").select("id,name,name_bn,mobile,created_at").eq("reference_customer_id", cid).is("deleted_at", null).order("created_at", { ascending: false }).limit(100),
      supabaseAdmin.from("pending_referrals" as any).select("id,mobile,note,status,created_at,matched_at").eq("referrer_id", cid).eq("status", "pending").order("created_at", { ascending: false }).limit(100),
    ]);

    const sAll = (allSales.data || []) as any[];
    const totalOrders = sAll.length;
    const completedOrders = sAll.filter((s) => ["completed", "delivered", "partial_delivered"].includes(s.sale_status)).length;
    const pendingOrders = sAll.filter((s) => ["pending", "confirmed", "processing", "ready_for_delivery"].includes(s.sale_status)).length;
    const returnedOrders = sAll.filter((s) => s.sale_status === "returned").length;
    const totalPurchase = sAll.reduce((a, s) => a + Number(s.total_amount || 0), 0);
    const totalPaid = sAll.reduce((a, s) => a + Number(s.paid_amount || 0), 0);
    const lastPurchaseDate = sAll.length ? sAll.map((s) => s.sale_date).filter(Boolean).sort().slice(-1)[0] : null;

    const wtx = (walletTx.data || []) as any[];
    const lastPaymentTx = wtx.find((t) => t.type === "withdraw" || t.type === "withdrawal_paid");
    const withdrawalsArr = (withdrawals.data || []) as any[];
    const withdrawPending = withdrawalsArr.filter((w) => w.status === "pending").reduce((a, w) => a + Number(w.amount || 0), 0);
    const totalWithdraw = withdrawalsArr.filter((w) => w.status === "approved" || w.status === "paid").reduce((a, w) => a + Number(w.amount || 0), 0);
    const dueAdjUsed = ((dueAdj.data || []) as any[]).reduce((a: number, r: any) => a + Number(r.amount || 0), 0);

    const created = new Date(cust.data?.created_at || Date.now()).getTime();
    const ageDays = (Date.now() - created) / 86400000;
    const lastDays = lastPurchaseDate ? (Date.now() - new Date(lastPurchaseDate).getTime()) / 86400000 : Infinity;
    let segment: "vip" | "regular" | "new" | "inactive" = "new";
    if (totalPurchase >= 100000) segment = "vip";
    else if (lastDays > 90 && totalPurchase > 0) segment = "inactive";
    else if (ageDays <= 30 && totalPurchase <= 0) segment = "new";
    else if (totalPurchase > 0) segment = "regular";

    return {
      customer: cust.data,
      wallet: wallet.data,
      walletTx: walletTx.data || [],
      sales: sales.data || [],
      withdrawals: withdrawals.data || [],
      referrals: refList.data || [],
      pendingReferrals: pendingRefs.data || [],
      summary: {
        totalOrders, completedOrders, pendingOrders, returnedOrders,
        totalPurchase, totalPaid,
        lastPurchaseDate,
        lastPaymentDate: lastPaymentTx?.transaction_date || null,
        withdrawPending, totalWithdraw,
        dueAdjUsed,
        segment,
        referralCount: refCount.count || 0,
      },
    };
  });

export const customerPortalRequestWithdraw = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    amount: z.number().positive().max(10000000),
    method: z.enum(["bkash", "nagad", "rocket", "bank", "cash"]),
    account_number: z.string().min(1).max(64),
    note: z.string().max(500).optional(),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: w } = await supabaseAdmin.from("referral_wallets").select("balance").eq("customer_id", cid).maybeSingle();
    const bal = Number(w?.balance || 0);
    if (data.amount > bal) throw new Error("Insufficient balance");
    const { data: ins, error } = await supabaseAdmin.from("withdrawal_requests").insert({
      customer_id: cid, amount: data.amount, method: data.method,
      account_number: data.account_number, note: data.note || "", status: "pending",
    }).select("id").single();
    if (error) throw error;
    return { ok: true, id: ins?.id as string };
  });

export const customerPortalCheckWithdraw = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    id: z.string().uuid(),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: row, error } = await supabaseAdmin
      .from("withdrawal_requests")
      .select("id,status,note,processed_at,amount,method,account_number")
      .eq("id", data.id)
      .eq("customer_id", cid)
      .maybeSingle();
    if (error) throw error;
    if (!row) throw new Error("Request not found");
    return row as { id: string; status: string; note: string | null; processed_at: string | null; amount: number; method: string; account_number: string };
  });

// ---------- Admin functions ----------

export const adminListPortalCustomers = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({ search: z.string().max(100).optional() }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    let q = supabaseAdmin.from("customers").select("id,name,name_bn,mobile,email,address,status,portal_enabled,portal_password_hash,portal_last_login_at").is("deleted_at", null).order("name").limit(200);
    if (data.search) q = q.or(`name.ilike.%${data.search}%,name_bn.ilike.%${data.search}%,mobile.ilike.%${data.search}%`);
    const { data: rows, error } = await q;
    if (error) throw error;
    return (rows || []).map((r: any) => ({
      id: r.id, name: r.name, name_bn: r.name_bn, mobile: r.mobile,
      email: r.email || "", address: r.address || "",
      status: r.status || "active",
      portal_last_login_at: r.portal_last_login_at || null,
      portal_enabled: r.portal_enabled, has_password: !!r.portal_password_hash,
    }));
  });

export const adminSetPortalPassword = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    customer_id: z.string().uuid(),
    password: z.string().min(4).max(128).optional(),
    enabled: z.boolean().optional(),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const update: Record<string, any> = {};
    if (typeof data.enabled === "boolean") update.portal_enabled = data.enabled;
    if (data.password) update.portal_password_hash = await bcrypt.hash(data.password, 10);
    if (Object.keys(update).length === 0) return { ok: true };
    const { error } = await supabaseAdmin.from("customers").update(update as any).eq("id", data.customer_id);
    if (error) throw error;
    return { ok: true };
  });

export const adminCreatePortalCustomer = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    name: z.string().trim().min(1).max(200),
    name_bn: z.string().trim().max(200).optional().default(""),
    mobile: z.string().trim().min(6).max(20),
    email: z.string().trim().email().max(255).optional().or(z.literal("")).default(""),
    address: z.string().trim().max(500).optional().default(""),
    password: z.string().min(4).max(128).optional(),
    portal_enabled: z.boolean().optional().default(true),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const { data: dup } = await supabaseAdmin.from("customers").select("id").eq("mobile", data.mobile).is("deleted_at", null).maybeSingle();
    if (dup) throw new Error("এই মোবাইল নম্বরে কাস্টমার আছে");
    const insert: Record<string, any> = {
      name: data.name, name_bn: data.name_bn || "", mobile: data.mobile,
      email: data.email || "", address: data.address || "",
      portal_enabled: data.portal_enabled,
    };
    if (data.password) insert.portal_password_hash = await bcrypt.hash(data.password, 10);
    const { data: row, error } = await supabaseAdmin.from("customers").insert(insert as any).select("id").single();
    if (error) throw error;
    return { ok: true, id: row.id };
  });

export const adminUpdatePortalCustomer = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    customer_id: z.string().uuid(),
    name: z.string().trim().min(1).max(200).optional(),
    name_bn: z.string().trim().max(200).optional(),
    mobile: z.string().trim().min(6).max(20).optional(),
    email: z.string().trim().max(255).optional(),
    address: z.string().trim().max(500).optional(),
    status: z.enum(["active", "inactive"]).optional(),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const { customer_id, ...rest } = data;
    const update: Record<string, any> = {};
    for (const [k, v] of Object.entries(rest)) if (v !== undefined) update[k] = v;
    if (Object.keys(update).length === 0) return { ok: true };
    if (update.mobile) {
      const { data: dup } = await supabaseAdmin.from("customers").select("id").eq("mobile", update.mobile).neq("id", customer_id).is("deleted_at", null).maybeSingle();
      if (dup) throw new Error("এই মোবাইল নম্বর অন্য কাস্টমারের");
    }
    const { error } = await supabaseAdmin.from("customers").update(update as any).eq("id", customer_id);
    if (error) throw error;
    return { ok: true };
  });

export const adminDeletePortalCustomer = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({ customer_id: z.string().uuid() }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const { error } = await supabaseAdmin.from("customers").update({ deleted_at: new Date().toISOString(), portal_enabled: false } as any).eq("id", data.customer_id);
    if (error) throw error;
    return { ok: true };
  });

export const adminListReferralMappings = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    search: z.string().trim().max(100).optional().default(""),
    filter: z.enum(["all", "linked", "unlinked", "orphan"]).optional().default("all"),
    limit: z.number().int().min(1).max(1000).optional().default(500),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    let q = supabaseAdmin
      .from("customers")
      .select("id,name,name_bn,mobile,reference_customer_id,reference_mobile,created_at,updated_at")
      .is("deleted_at", null)
      .order("updated_at", { ascending: false })
      .limit(data.limit);
    if (data.filter === "linked") q = q.not("reference_customer_id", "is", null);
    else if (data.filter === "unlinked") q = q.is("reference_customer_id", null);
    else if (data.filter === "orphan") q = q.is("reference_customer_id", null).not("reference_mobile", "is", null).neq("reference_mobile", "");
    if (data.search) q = q.or(`name.ilike.%${data.search}%,name_bn.ilike.%${data.search}%,mobile.ilike.%${data.search}%,reference_mobile.ilike.%${data.search}%`);
    let pendingQ = supabaseAdmin
      .from("pending_referrals" as any)
      .select("id,mobile,note,status,created_at,referrer_id")
      .eq("status", "pending")
      .order("created_at", { ascending: false })
      .limit(data.limit);
    if (data.search) pendingQ = pendingQ.or(`mobile.ilike.%${data.search}%,note.ilike.%${data.search}%`);

    const [{ data: rows, error }, { data: pendingRows, error: pendingError }] = await Promise.all([q, pendingQ]);
    if (error) throw error;
    if (pendingError) throw pendingError;
    const refIds = Array.from(new Set([
      ...(rows || []).map((r: any) => r.reference_customer_id).filter(Boolean),
      ...(pendingRows || []).map((r: any) => r.referrer_id).filter(Boolean),
    ]));
    let refMap = new Map<string, { id: string; name: string; name_bn: string; mobile: string }>();
    if (refIds.length) {
      const { data: refs } = await supabaseAdmin
        .from("customers")
        .select("id,name,name_bn,mobile")
        .in("id", refIds);
      refMap = new Map((refs || []).map((r: any) => [r.id, r]));
    }
    const all = (rows || []) as any[];
    const pendingAll = (pendingRows || []) as any[];
    const stats = {
      total: all.length,
      linked: all.filter((r) => !!r.reference_customer_id).length,
      orphan: all.filter((r) => !r.reference_customer_id && r.reference_mobile).length,
      none: all.filter((r) => !r.reference_customer_id && !r.reference_mobile).length,
      pending: pendingAll.length,
    };
    return {
      stats,
      rows: all.map((r: any) => ({
        id: r.id,
        name: r.name,
        name_bn: r.name_bn,
        mobile: r.mobile,
        reference_mobile: r.reference_mobile || "",
        reference_customer_id: r.reference_customer_id || null,
        referrer: r.reference_customer_id ? refMap.get(r.reference_customer_id) || null : null,
        created_at: r.created_at,
        updated_at: r.updated_at,
      })),
      pendingRows: pendingAll.map((r: any) => ({
        id: r.id,
        mobile: r.mobile,
        note: r.note || "",
        status: r.status,
        created_at: r.created_at,
        referrer: r.referrer_id ? refMap.get(r.referrer_id) || null : null,
      })),
    };
  });

export const adminSetReferralMapping = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    customer_id: z.string().uuid(),
    reference_mobile: z.string().trim().max(20).optional().default(""),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const ref = data.reference_mobile.trim();
    if (!ref) {
      const { error } = await supabaseAdmin.from("customers").update({ reference_mobile: null, reference_customer_id: null } as any).eq("id", data.customer_id);
      if (error) throw error;
      return { ok: true, cleared: true };
    }
    const { data: me } = await supabaseAdmin.from("customers").select("mobile").eq("id", data.customer_id).single();
    if (!me) throw new Error("Customer not found");
    if (ref === me.mobile) throw new Error("নিজের নাম্বার রেফারেল হতে পারবে না");
    const { data: refCust } = await supabaseAdmin.from("customers").select("id,name,mobile").eq("mobile", ref).is("deleted_at", null).maybeSingle();
    if (!refCust) throw new Error("এই নাম্বারে কোনো কাস্টমার পাওয়া যায়নি");
    const { error } = await supabaseAdmin.from("customers").update({
      reference_mobile: refCust.mobile,
      reference_customer_id: refCust.id,
    } as any).eq("id", data.customer_id);
    if (error) throw error;
    return { ok: true, referrer: refCust };
  });

// ---------- Portal self-service profile ----------

export const customerPortalUpdateProfile = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    name: z.string().trim().min(1).max(200).optional(),
    name_bn: z.string().trim().max(200).optional(),
    email: z.string().trim().max(255).optional(),
    address: z.string().trim().max(500).optional(),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const update: Record<string, any> = {};
    for (const k of ["name", "name_bn", "email", "address"] as const) {
      if (data[k] !== undefined) update[k] = data[k];
    }
    if (Object.keys(update).length === 0) return { ok: true };
    const { error } = await supabaseAdmin.from("customers").update(update as any).eq("id", cid);
    if (error) throw error;
    return { ok: true };
  });

export const customerPortalChangePassword = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    current_password: z.string().min(1).max(128),
    new_password: z.string().min(4).max(128),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: c } = await supabaseAdmin.from("customers").select("portal_password_hash").eq("id", cid).single();
    if (!c?.portal_password_hash) throw new Error("Password not set");
    const ok = await bcrypt.compare(data.current_password, c.portal_password_hash);
    if (!ok) throw new Error("Current password is incorrect");
    const hash = await bcrypt.hash(data.new_password, 10);
    const { error } = await supabaseAdmin.from("customers").update({ portal_password_hash: hash } as any).eq("id", cid);
    if (error) throw error;
    return { ok: true };
  });

export const customerPortalUploadProfileImage = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    data_url: z.string().min(20).max(8_000_000),
    content_type: z.enum(["image/jpeg", "image/png", "image/webp"]),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const m = data.data_url.match(/^data:([^;]+);base64,(.+)$/);
    if (!m) throw new Error("Invalid image data");
    const buf = Buffer.from(m[2], "base64");
    if (buf.length > 5 * 1024 * 1024) throw new Error("Image too large (max 5MB)");
    const ext = data.content_type === "image/png" ? "png" : data.content_type === "image/webp" ? "webp" : "jpg";
    const path = `portal/${cid}/${Date.now()}.${ext}`;
    const { error: upErr } = await supabaseAdmin.storage.from("customer-images").upload(path, buf, {
      contentType: data.content_type, upsert: true,
    });
    if (upErr) throw upErr;
    const { data: signed, error: signErr } = await supabaseAdmin.storage.from("customer-images").createSignedUrl(path, 60 * 60 * 24 * 365 * 10);
    if (signErr) throw signErr;
    const url = signed.signedUrl;
    const { error } = await supabaseAdmin.from("customers").update({ image_url: url } as any).eq("id", cid);
    if (error) throw error;
    return { ok: true, image_url: url };
  });

export const customerPortalSetReferral = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    reference_mobile: z.string().trim().min(6).max(20).regex(/^[0-9+\-\s]+$/, "Invalid mobile"),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: me } = await supabaseAdmin.from("customers").select("id,mobile,reference_customer_id,reference_mobile").eq("id", cid).single();
    if (!me) throw new Error("Customer not found");
    if (me.reference_customer_id || me.reference_mobile) throw new Error("রেফারেল ইতিমধ্যে সেট করা আছে");
    const ref = data.reference_mobile.trim();
    if (ref === me.mobile) throw new Error("নিজের নাম্বার রেফারেল হতে পারবে না");
    const { data: refCust } = await supabaseAdmin.from("customers").select("id,name,mobile").eq("mobile", ref).is("deleted_at", null).maybeSingle();
    if (!refCust) throw new Error("এই নাম্বারে কোনো রেজিস্টার্ড কাস্টমার পাওয়া যায়নি");
    const { error } = await supabaseAdmin.from("customers").update({
      reference_mobile: refCust.mobile,
      reference_customer_id: refCust.id,
    } as any).eq("id", cid);
    if (error) throw error;
    // Mark any matching pending_referral (referrer added this mobile earlier) as matched
    await supabaseAdmin
      .from("pending_referrals" as any)
      .update({ status: "matched", matched_customer_id: cid, matched_at: new Date().toISOString() } as any)
      .eq("referrer_id", refCust.id)
      .eq("mobile", me.mobile)
      .eq("status", "pending");
    return { ok: true, referrer: { id: refCust.id, name: refCust.name, mobile: refCust.mobile } };
  });

export const customerPortalRemoveProfileImage = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({ token: z.string().min(10) }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { error } = await supabaseAdmin.from("customers").update({ image_url: null } as any).eq("id", cid);
    if (error) throw error;
    return { ok: true };
  });

export const adminVerifyReferralAutobinding = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .handler(async ({ context }) => {
    await assertAdmin(context.supabase, context.userId);
    const { data, error } = await supabaseAdmin.rpc("verify_referral_autobinding" as any);
    if (error) throw new Error(error.message);
    return data as any;
  });

export const adminReferralLookup = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d) => z.object({
    mobile: z.string().trim().min(3).max(20),
  }).parse(d))
  .handler(async ({ data, context }) => {
    await assertAdmin(context.supabase, context.userId);
    const mob = data.mobile;

    // 1) The customer with this mobile (if registered) + their referrer
    const { data: self } = await supabaseAdmin
      .from("customers")
      .select("id,name,name_bn,mobile,reference_mobile,reference_customer_id,created_at,updated_at")
      .eq("mobile", mob)
      .is("deleted_at", null)
      .maybeSingle();

    let referrer: any = null;
    if (self?.reference_customer_id) {
      const { data: r } = await supabaseAdmin
        .from("customers")
        .select("id,name,name_bn,mobile,created_at")
        .eq("id", self.reference_customer_id)
        .maybeSingle();
      referrer = r || null;
    }

    // 2) All customers who used this mobile as their reference_mobile (downline)
    const { data: downline } = await supabaseAdmin
      .from("customers")
      .select("id,name,name_bn,mobile,reference_mobile,reference_customer_id,created_at,updated_at")
      .eq("reference_mobile", mob)
      .is("deleted_at", null)
      .order("created_at", { ascending: false })
      .limit(200);

    const downlineRows = (downline || []) as any[];
    const linkedCount = downlineRows.filter((r) => !!r.reference_customer_id).length;
    const orphanCount = downlineRows.filter((r) => !r.reference_customer_id).length;

    return {
      query_mobile: mob,
      self: self || null,
      referrer,
      downline: downlineRows,
      downline_stats: {
        total: downlineRows.length,
        linked: linkedCount,
        orphan: orphanCount,
      },
    };
  });

// Customer adds a phone number they want to refer (pre-binding)
export const customerPortalAddPendingReferral = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    mobile: z.string().trim().min(6).max(20).regex(/^[0-9+\-\s]+$/, "Invalid mobile"),
    note: z.string().trim().max(200).optional().default(""),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: me } = await supabaseAdmin.from("customers").select("id,mobile").eq("id", cid).single();
    if (!me) throw new Error("Customer not found");
    const mob = data.mobile.trim();
    if (mob === me.mobile) throw new Error("নিজের নাম্বার রেফার করা যাবে না");

    // If that mobile is already a registered customer → directly link them (skip pending)
    const { data: existing } = await supabaseAdmin.from("customers").select("id,name,mobile,reference_customer_id").eq("mobile", mob).is("deleted_at", null).maybeSingle();
    if (existing) {
      if (existing.reference_customer_id) throw new Error("এই কাস্টমারের ইতিমধ্যে রেফারার আছে");
      const { error } = await supabaseAdmin.from("customers").update({
        reference_customer_id: cid,
        reference_mobile: me.mobile,
      } as any).eq("id", existing.id);
      if (error) throw error;
      return { ok: true, linked_directly: true, customer: existing };
    }

    // Otherwise insert pending referral
    const { data: ins, error } = await supabaseAdmin
      .from("pending_referrals" as any)
      .insert({ referrer_id: cid, mobile: mob, note: data.note || null } as any)
      .select("id,mobile,note,status,created_at")
      .single();
    if (error) {
      if ((error as any).code === "23505") throw new Error("এই নাম্বার ইতিমধ্যে যোগ করা আছে");
      throw error;
    }
    return { ok: true, linked_directly: false, pending: ins };
  });

export const customerPortalDeletePendingReferral = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({ token: z.string().min(10), id: z.string().uuid() }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { error } = await supabaseAdmin.from("pending_referrals" as any).delete().eq("id", data.id).eq("referrer_id", cid).eq("status", "pending");
    if (error) throw error;
    return { ok: true };
  });

// ---------- Customer → Customer wallet transfer ----------

export const customerPortalLookupRecipient = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    mobile: z.string().trim().min(6).max(20).regex(/^[0-9+\-\s]+$/, "Invalid mobile"),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const { data: me } = await supabaseAdmin.from("customers").select("mobile").eq("id", cid).single();
    if (!me) throw new Error("Customer not found");
    const mob = data.mobile.trim();
    if (mob === me.mobile) throw new Error("নিজের নাম্বারে পাঠানো যাবে না");
    const { data: r } = await supabaseAdmin
      .from("customers")
      .select("id,name,name_bn,mobile,image_url,status")
      .eq("mobile", mob)
      .is("deleted_at", null)
      .maybeSingle();
    if (!r) throw new Error("এই নাম্বারে কোনো কাস্টমার পাওয়া যায়নি");
    if (r.status && r.status !== "active") throw new Error("রিসিপিয়েন্ট সক্রিয় নয়");
    return { id: r.id, name: r.name, name_bn: r.name_bn || "", mobile: r.mobile, image_url: r.image_url || "" };
  });

export const customerPortalSendMoney = createServerFn({ method: "POST" })
  .inputValidator((d) => z.object({
    token: z.string().min(10),
    recipient_mobile: z.string().trim().min(6).max(20).regex(/^[0-9+\-\s]+$/, "Invalid mobile"),
    amount: z.number().positive().max(10000000),
    note: z.string().trim().max(300).optional().default(""),
  }).parse(d))
  .handler(async ({ data }) => {
    const cid = await verifyToken(data.token);
    const amount = Math.round(Number(data.amount) * 100) / 100;
    if (!(amount > 0)) throw new Error("সঠিক পরিমাণ দিন");
    if (amount < 1) throw new Error("সর্বনিম্ন ৳১");

    const { data: me } = await supabaseAdmin.from("customers").select("id,name,name_bn,mobile").eq("id", cid).single();
    if (!me) throw new Error("Customer not found");
    if (data.recipient_mobile.trim() === me.mobile) throw new Error("নিজের নাম্বারে পাঠানো যাবে না");

    const { data: rc } = await supabaseAdmin
      .from("customers")
      .select("id,name,name_bn,mobile,status")
      .eq("mobile", data.recipient_mobile.trim())
      .is("deleted_at", null)
      .maybeSingle();
    if (!rc) throw new Error("এই নাম্বারে কোনো কাস্টমার পাওয়া যায়নি");
    if (rc.status && rc.status !== "active") throw new Error("রিসিপিয়েন্ট সক্রিয় নয়");

    const { data: sw } = await supabaseAdmin.from("referral_wallets").select("id,balance").eq("customer_id", cid).maybeSingle();
    if (!sw) throw new Error("ওয়ালেট পাওয়া যায়নি");

    const refId = `TRF-${Date.now()}-${Math.random().toString(36).slice(2, 8).toUpperCase()}`;

    // Atomic transfer via SECURITY DEFINER function (prevents race conditions)
    const { data: rpcRes, error: rpcErr } = await (supabaseAdmin as any).rpc("portal_transfer_wallet_balance", {
      p_sender_id: cid,
      p_recipient_id: rc.id,
      p_amount: amount,
    });
    if (rpcErr) {
      if (/insufficient/i.test(rpcErr.message || "")) throw new Error("ব্যালেন্স অপর্যাপ্ত");
      throw new Error("ট্রান্সফার ব্যর্থ");
    }
    const row = Array.isArray(rpcRes) ? rpcRes[0] : rpcRes;
    const newSenderBal = Number(row?.sender_new_balance ?? 0);
    const newRecipientBal = Number(row?.recipient_new_balance ?? 0);

    const noteOut = `Sent to ${rc.name} (${rc.mobile})${data.note ? ` — ${data.note}` : ""}`;
    const noteIn = `Received from ${me.name} (${me.mobile})${data.note ? ` — ${data.note}` : ""}`;
    await supabaseAdmin.from("wallet_transactions").insert([
      { customer_id: cid,   type: "transfer_out", amount: -amount, balance_after: newSenderBal,    reference_id: refId, note: noteOut },
      { customer_id: rc.id, type: "transfer_in",  amount:  amount, balance_after: newRecipientBal, reference_id: refId, note: noteIn  },
    ] as any);

    return {
      ok: true,
      reference: refId,
      amount,
      new_balance: newSenderBal,
      recipient: { id: rc.id, name: rc.name, name_bn: rc.name_bn || "", mobile: rc.mobile },
    };
  });
