// supabase.jsx — Supabase client + all data operations
// Falls back to mock data when no credentials are configured.

const _isConfigured = () =>
  window.SUPABASE_URL &&
  window.SUPABASE_URL !== "https://YOUR_PROJECT_ID.supabase.co" &&
  window.SUPABASE_ANON &&
  window.SUPABASE_ANON !== "YOUR_ANON_PUBLIC_KEY";

const _client = () => {
  if (!_isConfigured()) return null;
  if (!window._sbClient) {
    window._sbClient = supabase.createClient(window.SUPABASE_URL, window.SUPABASE_ANON);
  }
  return window._sbClient;
};

// ── helpers ──────────────────────────────────────────────────

function taskFromRow(row) {
  if (!row) return null;
  return {
    id: row.id,
    cat: row.category,
    poster: row.poster_id,
    posterProfile: row.profiles || null,
    title: row.title,
    city: row.city || "",
    remote: row.remote,
    posted: timeAgo(row.created_at),
    applicants: row.application_count || 0,
    urgent: row.urgent,
    blurb: row.blurb || "",
    details: row.details || [],
    proof: row.proof_requirement || "",
    pay: row.pay_kind === "favor"
      ? { kind: "favor", note: row.favor_note || "" }
      : { kind: "fixed", amount: Number(row.amount) || 0, cur: row.currency || "₹" },
    status: row.status,
    deadline: row.deadline || null,
  };
}

function profileFromRow(row) {
  if (!row) return null;
  return {
    id: row.id,
    phone: row.phone || "",
    name: row.name || "User",
    handle: row.handle || "@user",
    city: row.city || "",
    verified: row.verification_status === "verified",
    verificationStatus: row.verification_status,
    rejectReason: row.reject_reason || "",
    rating: Number(row.rating) || 0,
    jobs: row.jobs_completed || 0,
    isAdmin: !!row.is_admin,
    idPhotoUrl: row.id_photo_url || null,
    avatarUrl: row.avatar_url || null,
    bio: row.bio || "",
    skills: row.skills || [],
    availabilityDays: row.availability_days || [],
    availabilityHours: row.availability_hours || "",
    travelRadius: row.travel_radius || "",
    vehicle: row.has_vehicle || "",
  };
}

function timeAgo(ts) {
  if (!ts) return "just now";
  const diff = Date.now() - new Date(ts).getTime();
  const m = Math.floor(diff / 60000);
  if (m < 1) return "just now";
  if (m < 60) return `${m}m`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h}h`;
  return `${Math.floor(h / 24)}d`;
}

// ── AUTH ─────────────────────────────────────────────────────

const Auth = {
  async sendEmailOTP(email) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.signInWithOtp({ email, options: { shouldCreateUser: true } });
    return { error: error?.message };
  },

  async sendMagicLink(email) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.signInWithOtp({
      email,
      options: { emailRedirectTo: window.location.origin, shouldCreateUser: true },
    });
    return { error: error?.message };
  },

  async verifyEmailOTP(email, token) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { data, error } = await sb.auth.verifyOtp({ email, token, type: "email" });
    return { session: data?.session, user: data?.user, error: error?.message };
  },

  async verifySignupOTP(email, token) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { data, error } = await sb.auth.verifyOtp({ email, token, type: "signup" });
    return { session: data?.session, user: data?.user, error: error?.message };
  },

  async signUpPassword(email, password) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { data, error } = await sb.auth.signUp({
      email, password,
      options: { emailRedirectTo: window.location.origin },
    });
    return { session: data?.session, user: data?.user, error: error?.message };
  },

  async signInPassword(email, password) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { data, error } = await sb.auth.signInWithPassword({ email, password });
    return { session: data?.session, user: data?.user, error: error?.message };
  },

  async resetPassword(email) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.resetPasswordForEmail(email, { redirectTo: window.location.origin });
    return { error: error?.message };
  },

  async updatePassword(newPassword) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.updateUser({ password: newPassword });
    return { error: error?.message };
  },

  async sendPhoneOTP(phone) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.signInWithOtp({ phone });
    return { error: error?.message };
  },

  async verifyPhoneOTP(phone, token) {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { data, error } = await sb.auth.verifyOtp({ phone, token, type: "sms" });
    return { session: data?.session, user: data?.user, error: error?.message };
  },

  async getSession() {
    const sb = _client();
    if (!sb) return { session: null };
    const { data } = await sb.auth.getSession();
    return { session: data?.session };
  },

  onAuthChange(cb) {
    const sb = _client();
    if (!sb) return () => {};
    const { data: { subscription } } = sb.auth.onAuthStateChange((event, session) => cb(event, session));
    return () => subscription.unsubscribe();
  },

  async signInWithGoogle() {
    const sb = _client();
    if (!sb) return { error: "Demo mode — auth not configured." };
    const { error } = await sb.auth.signInWithOAuth({
      provider: "google",
      options: { redirectTo: window.location.origin },
    });
    return { error: error?.message };
  },

  async signOut() {
    const sb = _client();
    if (sb) await sb.auth.signOut();
  },
};

// ── PROFILES ─────────────────────────────────────────────────

const Profiles = {
  async get(userId) {
    const sb = _client();
    if (!sb) return { profile: null };
    const { data, error } = await sb.from("profiles").select("*").eq("id", userId).single();
    return { profile: profileFromRow(data), error: error?.message };
  },

  async upsert(userId, fields) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("profiles").upsert({ id: userId, ...fields });
    return { error: error?.message };
  },

  // upload an ID doc or selfie to the PRIVATE id-docs bucket.
  // kind: "id" | "selfie". Returns a storage path (or object URL in demo).
  async uploadDoc(userId, file, kind) {
    const sb = _client();
    if (!sb) return { path: file ? URL.createObjectURL(file) : null, error: null };
    const ext = (file.name || "jpg").split(".").pop();
    const path = `${kind}/${userId}-${Date.now()}.${ext}`;
    try {
      const { error } = await sb.storage.from("id-docs").upload(path, file, { upsert: true });
      if (error) {
        console.warn(`Supabase storage upload failed: ${error.message}. Falling back to Base64 data URL.`);
        const dataUrl = await new Promise((resolve) => {
          const reader = new FileReader();
          reader.onloadend = () => resolve(reader.result);
          reader.readAsDataURL(file);
        });
        return { path: dataUrl, error: null };
      }
      return { path, error: null };
    } catch (e) {
      console.warn(`Supabase storage exception: ${e.message}. Falling back to Base64 data URL.`);
      const dataUrl = await new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(file);
      });
      return { path: dataUrl, error: null };
    }
  },

  // upload a public profile photo → returns a public URL
  async uploadAvatar(userId, file) {
    const sb = _client();
    if (!sb) return { url: file ? URL.createObjectURL(file) : null, error: null };
    const ext = (file.name || "jpg").split(".").pop();
    const path = `avatars/${userId}-${Date.now()}.${ext}`;
    try {
      const { error } = await sb.storage.from("user-uploads").upload(path, file, { upsert: true });
      if (error) {
        console.warn(`Supabase avatar upload failed: ${error.message}. Falling back to Base64 data URL.`);
        const dataUrl = await new Promise((resolve) => {
          const reader = new FileReader();
          reader.onloadend = () => resolve(reader.result);
          reader.readAsDataURL(file);
        });
        return { url: dataUrl, error: null };
      }
      const { data } = sb.storage.from("user-uploads").getPublicUrl(path);
      return { url: data.publicUrl, error: null };
    } catch (e) {
      console.warn(`Supabase avatar exception: ${e.message}. Falling back to Base64 data URL.`);
      const dataUrl = await new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = () => resolve(reader.result);
        reader.readAsDataURL(file);
      });
      return { url: dataUrl, error: null };
    }
  },
};

// ── TASKS ────────────────────────────────────────────────────

const Tasks = {
  async list(filters = {}) {
    const sb = _client();
    if (!sb) {
      // mock fallback
      let list = [...TASKS];
      if (filters.cat && filters.cat !== "all") list = list.filter(t => t.cat === filters.cat);
      if (filters.pay === "paid") list = list.filter(t => t.pay.kind === "fixed");
      if (filters.pay === "favor") list = list.filter(t => t.pay.kind === "favor");
      if (filters.q) {
        const q = filters.q.toLowerCase();
        list = list.filter(t => (t.title + t.blurb + t.city).toLowerCase().includes(q));
      }
      return { tasks: list };
    }
    let q = sb.from("tasks")
      .select("*, profiles:profiles!tasks_poster_id_fkey(id,name,handle,city,verification_status,rating,jobs_completed,phone)")
      .eq("status", "open")
      .order("created_at", { ascending: false });
    if (filters.cat && filters.cat !== "all") q = q.eq("category", filters.cat);
    if (filters.pay === "paid") q = q.eq("pay_kind", "fixed");
    if (filters.pay === "favor") q = q.eq("pay_kind", "favor");
    if (filters.q) q = q.ilike("title", `%${filters.q}%`);
    const { data, error } = await q;
    return { tasks: (data || []).map(taskFromRow), error: error?.message };
  },

  async get(id) {
    const sb = _client();
    if (!sb) {
      const t = TASKS.find(t => t.id === id);
      return { task: t || null };
    }
    const { data, error } = await sb.from("tasks")
      .select("*, profiles:profiles!tasks_poster_id_fkey(id,name,handle,city,verification_status,rating,jobs_completed,phone)")
      .eq("id", id).single();
    return { task: taskFromRow(data), error: error?.message };
  },

  async create(userId, fields) {
    const sb = _client();
    if (!sb) {
      // mock — handled locally in app state
      return { id: "u" + Date.now(), error: null };
    }
    const row = {
      poster_id: userId,
      title: fields.title,
      category: fields.cat,
      city: fields.city,
      remote: fields.remote,
      blurb: fields.blurb,
      details: fields.details.filter(Boolean),
      proof_requirement: fields.proof,
      pay_kind: fields.kind,
      amount: fields.kind === "fixed" ? Number(fields.amount) : null,
      currency: fields.cur,
      favor_note: fields.favorNote,
      urgent: fields.urgent,
      deadline: fields.deadline || null,
    };
    const { data, error } = await sb.from("tasks").insert(row).select().single();
    return { id: data?.id, error: error?.message };
  },

  async updateStatus(id, status) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("tasks").update({ status }).eq("id", id);
    return { error: error?.message };
  },

  async myPosted(userId) {
    const sb = _client();
    if (!sb) return { tasks: [] };
    const { data, error } = await sb.from("tasks")
      .select("*, profiles:profiles!tasks_poster_id_fkey(id,name,handle,city,verification_status,rating,jobs_completed,phone)")
      .eq("poster_id", userId).order("created_at", { ascending: false });
    return { tasks: (data || []).map(taskFromRow), error: error?.message };
  },

  // edit an existing task (poster only — RLS enforces ownership)
  async update(id, fields) {
    const sb = _client();
    if (!sb) return { error: null };
    const row = {};
    if (fields.title !== undefined)    row.title = fields.title;
    if (fields.blurb !== undefined)    row.blurb = fields.blurb;
    if (fields.amount !== undefined)   row.amount = fields.amount;
    if (fields.deadline !== undefined) row.deadline = fields.deadline || null;
    if (fields.urgent !== undefined)   row.urgent = fields.urgent;
    const { error } = await sb.from("tasks").update(row).eq("id", id);
    return { error: error?.message };
  },
};

// ── APPLICATIONS ─────────────────────────────────────────────

const Applications = {
  async forTask(taskId) {
    const sb = _client();
    if (!sb) return { apps: window._seedApplicants?.[taskId] || [] };
    const { data, error } = await sb.from("applications")
      .select("*, profiles(id,name,handle,city,verification_status,rating,jobs_completed,phone)")
      .eq("task_id", taskId).order("created_at", { ascending: false });
    return {
      apps: (data || []).map(a => ({
        id: a.id,
        handle: a.applicant_id,
        profile: profileFromRow(a.profiles),
        msg: a.message,
        price: a.offer_amount,
        when: a.availability,
        status: a.status,
      })),
      error: error?.message,
    };
  },

  async myApplications(userId) {
    const sb = _client();
    if (!sb) return { apps: [] };
    const { data, error } = await sb.from("applications")
      .select("*, tasks(*)").eq("applicant_id", userId)
      .order("created_at", { ascending: false });
    return {
      apps: (data || []).map(a => ({
        taskId: a.task_id,
        task: taskFromRow(a.tasks),
        msg: a.message,
        price: a.offer_amount,
        when: a.availability,
        status: a.status,
      })),
      error: error?.message,
    };
  },

  async apply(taskId, userId, fields) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("applications").insert({
      task_id: taskId,
      applicant_id: userId,
      message: fields.msg,
      offer_amount: fields.price || null,
      availability: fields.when,
    });
    return { error: error?.message };
  },

  async accept(applicationId, taskId, workerId) {
    const sb = _client();
    if (!sb) return { error: null };
    // accept this one
    await sb.from("applications").update({ status: "accepted" }).eq("id", applicationId);
    // reject all others for same task
    await sb.from("applications")
      .update({ status: "rejected" })
      .eq("task_id", taskId)
      .neq("id", applicationId);
    // create assignment
    await sb.from("assignments").insert({ task_id: taskId, worker_id: workerId, application_id: applicationId });
    // update task status
    await sb.from("tasks").update({ status: "assigned" }).eq("id", taskId);
    return { error: null };
  },

  async reject(applicationId) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("applications").update({ status: "rejected" }).eq("id", applicationId);
    return { error: error?.message };
  },
};

// ── ASSIGNMENTS ───────────────────────────────────────────────

const Assignments = {
  async get(taskId) {
    const sb = _client();
    if (!sb) return { assignment: null };
    const { data } = await sb.from("assignments")
      .select("*, profiles(id,name,handle,rating,jobs_completed,verification_status,phone)")
      .eq("task_id", taskId).single();
    return { assignment: data };
  },

  async uploadProofPhotos(taskId, files) {
    const sb = _client();
    if (!sb) return { urls: files.map(f => URL.createObjectURL(f)) };
    const urls = [];
    for (let i = 0; i < files.length; i++) {
      const f = files[i];
      const ext = (f.name || "jpg").split(".").pop();
      const path = `proof/${taskId}/${Date.now()}-${i}.${ext}`;
      try {
        const { error } = await sb.storage.from("user-uploads").upload(path, f, { upsert: true });
        if (error) {
          console.warn(`Supabase proof upload failed: ${error.message}. Falling back to Base64 data URL.`);
          const dataUrl = await new Promise((resolve) => {
            const reader = new FileReader();
            reader.onloadend = () => resolve(reader.result);
            reader.readAsDataURL(f);
          });
          urls.push(dataUrl);
        } else {
          const { data } = sb.storage.from("user-uploads").getPublicUrl(path);
          urls.push(data.publicUrl);
        }
      } catch (e) {
        console.warn(`Supabase proof upload exception: ${e.message}. Falling back to Base64 data URL.`);
        const dataUrl = await new Promise((resolve) => {
          const reader = new FileReader();
          reader.onloadend = () => resolve(reader.result);
          reader.readAsDataURL(f);
        });
        urls.push(dataUrl);
      }
    }
    return { urls };
  },

  async submitProof(taskId, proofText, photoUrls) {
    const sb = _client();
    if (!sb) return { error: null };
    await sb.from("assignments").update({
      proof_text: proofText,
      proof_photos: photoUrls || [],
      proof_submitted_at: new Date().toISOString(),
    }).eq("task_id", taskId);
    await sb.from("tasks").update({ status: "proof_submitted" }).eq("id", taskId);
    return { error: null };
  },

  async releasePayment(taskId, proofNote) {
    const sb = _client();
    if (!sb) return { error: null };
    await sb.from("assignments").update({
      payment_released_at: new Date().toISOString(),
      ...(proofNote ? { proof_text: proofNote } : {}),
    }).eq("task_id", taskId);
    await sb.from("tasks").update({ status: "completed" }).eq("id", taskId);
    return { error: null };
  },
};

// ── REVIEWS ───────────────────────────────────────────────────

const Reviews = {
  // all reviews a user has received (newest first)
  async forUser(userId) {
    const sb = _client();
    if (!sb) return { reviews: [] };
    const { data, error } = await sb.from("reviews")
      .select("id, stars, body, created_at, reviewer:profiles!reviews_reviewer_id_fkey(name,handle), tasks(title)")
      .eq("reviewee_id", userId)
      .order("created_at", { ascending: false });
    return {
      reviews: (data || []).map(r => ({
        id: r.id,
        stars: r.stars,
        text: r.body,
        from: r.reviewer?.name || "Someone",
        task: r.tasks?.title || "",
        when: timeAgo(r.created_at),
      })),
      error: error?.message,
    };
  },

  async submit(taskId, reviewerId, revieweeId, stars, text) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("reviews").insert({
      task_id: taskId, reviewer_id: reviewerId,
      reviewee_id: revieweeId, stars, body: text,
    });
    return { error: error?.message };
  },
  
  async forTask(taskId) {
    const sb = _client();
    if (!sb) return { reviews: [] };
    const { data, error } = await sb.from("reviews")
      .select("id, stars, body, reviewer_id, reviewee_id, created_at")
      .eq("task_id", taskId);
    return { reviews: data || [], error: error?.message };
  },
};

// ── MESSAGES ─────────────────────────────────────────────────

const Messages = {
  async get(userId, otherUserId) {
    const sb = _client();
    if (!sb) return { messages: [] };
    const { data } = await sb.from("messages")
      .select("*")
      .or(`and(from_id.eq.${userId},to_id.eq.${otherUserId}),and(from_id.eq.${otherUserId},to_id.eq.${userId})`)
      .order("created_at", { ascending: true });
    return {
      messages: (data || []).map(m => ({
        id: m.id,
        from: m.from_id === userId ? "me" : "them",
        text: m.body,
        photo: m.media_url || null,
        time: m.created_at,
      })),
    };
  },

  async send(fromId, toId, text, taskId, mediaUrl) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("messages").insert({
      from_id: fromId, to_id: toId, body: text, task_id: taskId || null, media_url: mediaUrl || null,
    });
    return { error: error?.message };
  },

  async markRead(userId, otherUserId) {
    const sb = _client();
    if (!sb) return;
    await sb.from("messages").update({ read: true })
      .eq("to_id", userId).eq("from_id", otherUserId).eq("read", false);
  },

  subscribeToThread(userId, otherUserId, onMessage) {
    const sb = _client();
    if (!sb) return () => {};
    const channel = sb.channel(`msgs-${[userId, otherUserId].sort().join("-")}`)
      .on("postgres_changes", {
        event: "INSERT", schema: "public", table: "messages",
        filter: `to_id=eq.${userId}`,
      }, payload => onMessage(payload.new))
      .subscribe();
    return () => sb.removeChannel(channel);
  },

  // global subscription — fires on any new incoming message (for unread badge)
  subscribeToIncoming(userId, onMessage) {
    const sb = _client();
    if (!sb) return () => {};
    const channel = sb.channel(`inbox-${userId}`)
      .on("postgres_changes", {
        event: "INSERT", schema: "public", table: "messages",
        filter: `to_id=eq.${userId}`,
      }, payload => onMessage(payload.new))
      .subscribe();
    return () => sb.removeChannel(channel);
  },

  async unreadCount(userId) {
    const sb = _client();
    if (!sb) return { count: 0 };
    const { count } = await sb.from("messages")
      .select("id", { count: "exact", head: true })
      .eq("to_id", userId)
      .eq("read", false);
    return { count: count || 0 };
  },

  async inbox(userId) {
    const sb = _client();
    if (!sb) return { threads: [] };
    const { data } = await sb.from("messages")
      .select("id, from_id, to_id, body, media_url, read, created_at")
      .or(`from_id.eq.${userId},to_id.eq.${userId}`)
      .order("created_at", { ascending: false });

    // one entry per partner — first occurrence is the latest message
    const seen = new Set();
    const threads = [];
    for (const m of (data || [])) {
      const partnerId = m.from_id === userId ? m.to_id : m.from_id;
      if (!seen.has(partnerId)) {
        seen.add(partnerId);
        const unread = (data || []).filter(x => x.from_id === partnerId && x.to_id === userId && !x.read).length;
        threads.push({ partnerId, lastMessage: m.body || (m.media_url ? "📷 Photo" : ""), lastTime: m.created_at, unread, profile: null });
      }
    }

    // batch-fetch partner profiles
    if (threads.length > 0) {
      const { data: profiles } = await sb.from("profiles")
        .select("id, name, handle, city, verification_status")
        .in("id", threads.map(t => t.partnerId));
      const pm = Object.fromEntries((profiles || []).map(p => [p.id, profileFromRow(p)]));
      threads.forEach(t => { t.profile = pm[t.partnerId] || null; });
    }
    return { threads };
  },
};

// ── SAVED TASKS ───────────────────────────────────────────────

const Saved = {
  async list(userId) {
    const sb = _client();
    if (!sb) return { ids: new Set() };
    const { data } = await sb.from("saved_tasks").select("task_id").eq("user_id", userId);
    return { ids: new Set((data || []).map(r => r.task_id)) };
  },

  async toggle(userId, taskId, currentlySaved) {
    const sb = _client();
    if (!sb) return { error: null };
    if (currentlySaved) {
      await sb.from("saved_tasks").delete().eq("user_id", userId).eq("task_id", taskId);
    } else {
      await sb.from("saved_tasks").insert({ user_id: userId, task_id: taskId });
    }
    return { error: null };
  },
};

// turn a stored path into a viewable URL (signed for private bucket)
async function _signedDoc(sb, path) {
  if (!path) return null;
  if (path.startsWith("http") || path.startsWith("blob:") || path.startsWith("data:")) return path; // already a URL (demo/base64)
  const { data } = await sb.storage.from("id-docs").createSignedUrl(path, 3600);
  return data?.signedUrl || null;
}

// ── VERIFICATION (admin) ──────────────────────────────────────
const Verification = {
  async listPending() {
    const sb = _client();
    if (!sb) return { subs: [] };
    const { data, error } = await sb.from("profiles")
      .select("id,name,handle,city,phone,id_photo_url,selfie_url,video_url,aadhaar_name,aadhaar_last4,upi_id,verification_status,created_at")
      .eq("verification_status", "pending")
      .order("created_at", { ascending: true });
    const subs = [];
    for (const r of (data || [])) {
      subs.push({
        id: r.id, name: r.name || "User", handle: r.handle || "@user",
        city: r.city || "", phone: r.phone || "",
        aadhaarName: r.aadhaar_name || "", aadhaarLast4: r.aadhaar_last4 || "", upiId: r.upi_id || "",
        status: r.verification_status,
        submitted: timeAgo(r.created_at),
        docType: "Aadhaar",
        docId: r.aadhaar_last4 ? "XXXX-XXXX-" + r.aadhaar_last4 : "—",
        hasSelfie: !!r.selfie_url,
        hasVideo: !!r.video_url,
        idPhotoUrl: await _signedDoc(sb, r.id_photo_url),
        selfieUrl: await _signedDoc(sb, r.selfie_url),
        videoUrl: await _signedDoc(sb, r.video_url),
      });
    }
    return { subs, error: error?.message };
  },

  async setStatus(userId, status, reason) {
    const sb = _client();
    if (!sb) return { error: null };
    const upd = { verification_status: status };
    if (status === "rejected") upd.reject_reason = reason || null;
    if (status === "verified") upd.reject_reason = null;
    const { error } = await sb.from("profiles").update(upd).eq("id", userId);
    return { error: error?.message };
  },
};

// ── ADMIN (operator) ──────────────────────────────────────────
const Admin = {
  async allUsers() {
    const sb = _client();
    if (!sb) return { users: [] };
    const { data, error } = await sb.from("profiles")
      .select("id,name,handle,city,verification_status,rating,jobs_completed")
      .order("created_at", { ascending: false });
    return {
      users: (data || []).map(r => ({
        id: r.id, name: r.name || "User", handle: r.handle || "@user", city: r.city || "",
        verified: r.verification_status === "verified",
        verificationStatus: r.verification_status || "unverified",
        rating: Number(r.rating) || 0, jobs: r.jobs_completed || 0,
      })),
      error: error?.message,
    };
  },

  async deleteTask(taskId) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("tasks").delete().eq("id", taskId);
    return { error: error?.message };
  },
};

const isSupabaseConfigured = _isConfigured;

// ── RAZORPAY HELPERS ──────────────────────────────────────────

const _isRazorpayConfigured = () =>
  window.RAZORPAY_KEY_ID &&
  window.RAZORPAY_KEY_ID !== "YOUR_RAZORPAY_KEY_ID";

// Call a Supabase Edge Function with auth token
async function _callEdge(fnName, body) {
  const sb = _client();
  if (!sb) return { error: "Not configured" };
  const { data: { session } } = await sb.auth.getSession();
  const token = session?.access_token;
  if (!token) return { error: "Not authenticated" };
  const url = `${window.SUPABASE_URL}/functions/v1/${fnName}`;
  const res = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
      apikey: window.SUPABASE_ANON,
    },
    body: JSON.stringify(body),
  });
  const data = await res.json();
  if (!res.ok) return { error: data.error || "Edge function failed", data };
  return { data };
}

// ── PAYMENTS (Razorpay) ──────────────────────────────────────

const Payments = {
  // Create a Razorpay Order — returns order_id + key_id for Checkout
  async createOrder(taskId, amount, currency = "INR") {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      // demo mode: return mock order
      return { data: { order_id: "demo_order_" + Date.now(), amount: amount * 100, currency, key_id: "demo", mock: true } };
    }
    return _callEdge("create-order", { task_id: taskId, amount, currency });
  },

  // Verify payment after Checkout succeeds
  async verifyPayment(taskId, razorpayPaymentId, razorpayOrderId, razorpaySignature) {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      return { data: { verified: true, status: "paid", mock: true } };
    }
    return _callEdge("verify-payment", {
      task_id: taskId,
      razorpay_payment_id: razorpayPaymentId,
      razorpay_order_id: razorpayOrderId,
      razorpay_signature: razorpaySignature,
    });
  },

  // Release payout to worker's UPI
  async releasePayout(taskId) {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      return { data: { released: true, payout_amount: 0, commission_amount: 0, mock: true } };
    }
    return _callEdge("release-payout", { task_id: taskId });
  },

  // Refund payment to poster
  async refund(taskId, reason) {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      return { data: { refunded: true, mock: true } };
    }
    return _callEdge("refund-payment", { task_id: taskId, reason });
  },

  // Update payment amount (e.g. after budget boost)
  async updateAmount(taskId, amount) {
    const sb = _client();
    if (!sb) return { error: null };
    const { error } = await sb.from("payments").update({ amount }).eq("task_id", taskId);
    return { error: error?.message };
  },

  // Get payment status for a task
  async getStatus(taskId) {
    const sb = _client();
    if (!sb) return { payment: null };
    const { data, error } = await sb.from("payments")
      .select("*").eq("task_id", taskId).single();
    if (error) return { payment: null };
    return { payment: data };
  },

  // Verify a UPI ID — returns registered name
  async verifyUPI(upiId) {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      // demo mode: simulate a successful verification after a short delay
      return { data: { valid: true, registered_name: null, mock: true } };
    }
    return _callEdge("verify-upi", { upi_id: upiId });
  },

  // Open Razorpay Checkout modal
  openCheckout({ orderId, amount, currency = "INR", taskTitle, userEmail, userName }, onSuccess, onFailure) {
    const keyId = _isRazorpayConfigured() ? window.RAZORPAY_KEY_ID : null;
    if (!keyId || !window.Razorpay) {
      // demo mode — simulate success immediately
      setTimeout(() => onSuccess({
        razorpay_payment_id: "demo_pay_" + Date.now(),
        razorpay_order_id: orderId,
        razorpay_signature: "demo_sig_" + Date.now(),
      }), 800);
      return;
    }

    const commissionRate = window.PLATFORM_COMMISSION || 10;
    const workerGets = Math.round(amount * (100 - commissionRate)) / 100;

    const options = {
      key: keyId,
      amount: Math.round(amount * 100), // paise
      currency,
      name: "TaskPe",
      description: taskTitle || "Task payment",
      order_id: orderId,
      handler: (response) => {
        onSuccess({
          razorpay_payment_id: response.razorpay_payment_id,
          razorpay_order_id: response.razorpay_order_id,
          razorpay_signature: response.razorpay_signature,
        });
      },
      prefill: {
        email: userEmail || "",
        contact: "",
        name: userName || "",
      },
      notes: {
        task_title: taskTitle || "",
      },
      theme: {
        color: getComputedStyle(document.documentElement).getPropertyValue("--clay").trim() || "#DC4B2E",
      },
      modal: {
        ondismiss: () => {
          if (onFailure) onFailure("Payment cancelled");
        },
      },
    };

    const rzp = new window.Razorpay(options);
    rzp.on("payment.failed", (response) => {
      if (onFailure) onFailure(response.error?.description || "Payment failed");
    });
    rzp.open();
  },
};

// ── HEALTH CHECK ──────────────────────────────────────────────
// Tells you exactly what's wrong during setup.
const Health = {
  async check() {
    if (!_isConfigured()) return { state: "demo" };
    const sb = _client();
    if (!sb) return { state: "error", detail: "Supabase library failed to load." };
    try {
      const { error } = await sb.from("tasks").select("id").limit(1);
      if (error) {
        const m = (error.message || "").toLowerCase();
        if (m.includes("does not exist") || m.includes("could not find the table") || m.includes("schema cache")) {
          return { state: "no-schema", detail: error.message };
        }
        if (m.includes("invalid api key") || m.includes("jwt") || m.includes("apikey")) {
          return { state: "bad-key", detail: error.message };
        }
        return { state: "error", detail: error.message };
      }
      // also confirm the storage bucket exists
      const { error: bErr } = await sb.storage.from("user-uploads").list("", { limit: 1 });
      if (bErr && bErr.message?.toLowerCase().includes("not found")) {
        return { state: "no-bucket", detail: bErr.message };
      }
      return { state: "connected" };
    } catch (e) {
      return { state: "error", detail: String(e?.message || e) };
    }
  },
};

// ── WALLET ────────────────────────────────────────────────────

const _demoWallet = () => {
  try {
    return JSON.parse(localStorage.getItem("__taskpe_wallet") || "null");
  } catch { return null; }
};
const _saveDemoWallet = (w) => {
  try { localStorage.setItem("__taskpe_wallet", JSON.stringify(w)); } catch {}
};
const _demoTxns = () => {
  try {
    return JSON.parse(localStorage.getItem("__taskpe_wtxns") || "[]");
  } catch { return []; }
};
const _saveDemoTxns = (t) => {
  try { localStorage.setItem("__taskpe_wtxns", JSON.stringify(t)); } catch {}
};

const Wallet = {
  // Get wallet balance and info
  async getBalance(userId) {
    if (!_isConfigured()) {
      const w = _demoWallet();
      return { wallet: w || { balance: 0, total_earned: 0, total_withdrawn: 0, currency: "INR", last_withdrawal: null } };
    }
    const sb = _client();
    if (!sb) return { wallet: null };
    const { data, error } = await sb.from("wallets")
      .select("*").eq("user_id", userId).single();
    if (error || !data) return { wallet: { balance: 0, total_earned: 0, total_withdrawn: 0, currency: "INR", last_withdrawal: null } };
    return { wallet: data };
  },

  // Get transaction history
  async getTransactions(userId, limit = 50) {
    if (!_isConfigured()) {
      return { transactions: _demoTxns().slice(0, limit) };
    }
    const sb = _client();
    if (!sb) return { transactions: [] };
    const { data, error } = await sb.from("wallet_transactions")
      .select("*").eq("user_id", userId)
      .order("created_at", { ascending: false }).limit(limit);
    if (error) return { transactions: [] };
    return { transactions: data || [] };
  },

  // Credit wallet (demo mode — in real mode, done by Edge Function)
  creditDemo(amount, taskTitle, taskId) {
    let w = _demoWallet() || { balance: 0, total_earned: 0, total_withdrawn: 0, currency: "INR", last_withdrawal: null };
    w.balance = Number(w.balance) + amount;
    w.total_earned = Number(w.total_earned) + amount;
    _saveDemoWallet(w);

    const txns = _demoTxns();
    txns.unshift({
      id: "dt_" + Date.now(),
      type: "credit",
      amount,
      balance_after: w.balance,
      task_id: taskId,
      description: `Earned from "${taskTitle || "task"}"`,
      created_at: new Date().toISOString(),
    });
    _saveDemoTxns(txns);
    return w;
  },

  // Withdraw to UPI
  async withdraw(amount) {
    if (!_isConfigured() || !_isRazorpayConfigured()) {
      // Demo mode withdrawal
      let w = _demoWallet();
      if (!w || w.balance < amount) return { error: "Insufficient balance" };
      if (amount < 100) return { error: "Minimum withdrawal is ₹100" };

      // Check daily limit
      if (w.last_withdrawal) {
        const hours = (Date.now() - new Date(w.last_withdrawal).getTime()) / (1000 * 60 * 60);
        if (hours < 24) return { error: `You can withdraw once per day. Try again in ${Math.ceil(24 - hours)} hours.` };
      }

      w.balance = Number(w.balance) - amount;
      w.total_withdrawn = Number(w.total_withdrawn) + amount;
      w.last_withdrawal = new Date().toISOString();
      _saveDemoWallet(w);

      const txns = _demoTxns();
      txns.unshift({
        id: "dt_" + Date.now(),
        type: "withdrawal",
        amount,
        balance_after: w.balance,
        description: "Withdrawal to UPI (demo)",
        upi_id: "demo@upi",
        payout_status: "completed",
        created_at: new Date().toISOString(),
      });
      _saveDemoTxns(txns);
      return { data: { withdrawn: true, amount, new_balance: w.balance, mock: true } };
    }
    return _callEdge("withdraw", { amount });
  },

  // Check if can withdraw today
  canWithdrawToday(wallet) {
    if (!wallet?.last_withdrawal) return true;
    const hours = (Date.now() - new Date(wallet.last_withdrawal).getTime()) / (1000 * 60 * 60);
    return hours >= 24;
  },

  // Hours until next withdrawal
  hoursUntilWithdraw(wallet) {
    if (!wallet?.last_withdrawal) return 0;
    const hours = (Date.now() - new Date(wallet.last_withdrawal).getTime()) / (1000 * 60 * 60);
    return Math.max(0, Math.ceil(24 - hours));
  },
};

Object.assign(window, { Auth, Profiles, Tasks, Applications, Assignments, Reviews, Messages, Saved, Verification, Admin, Health, Payments, Wallet, isSupabaseConfigured, _isRazorpayConfigured, timeAgo, profileFromRow });

