/* ClientBase — Couche de données Supabase (étape 3/6)

   Expose window.cbCloud avec :
   - loadAll()                          → {clients, appointments, invoices, stock, notifications,
                                            business, bookingSettings, vacations, fideliteRules}
   - upsertClient / removeClient
   - upsertAppointment / removeAppointment
   - upsertInvoice / removeInvoice / togglePaid
   - upsertStockItem / setStockQty / removeStockItem
   - upsertBusiness (update uniquement — la ligne est créée par le trigger)
   - upsertBookingSettings
   - addVacation / removeVacation
   - setFideliteRules
   - markAllNotificationsRead / removeNotification

   Les fonctions de lecture renvoient l'état converti en camelCase (forme
   attendue par l'UI historique localStorage). Les fonctions d'écriture
   renvoient une promise — l'UI met à jour optimistiquement et montre
   un toast warn si l'appel échoue.
*/

const _sb = () => window.cbSupabase;
const _uid = () => window.cbAuth && window.cbAuth.getCurrentUser && window.cbAuth.getCurrentUser().id;

/* ---------- Mappers DB ↔ UI ---------- */

const fromBusiness = (r) => r && ({
  name: r.name, owner: r.owner, initials: r.initials, hue: r.hue,
  phone: r.phone, contactEmail: r.contact_email,
  address: r.address, postalCode: r.postal_code, city: r.city,
  siret: r.siret, legalForm: r.legal_form,
  vatStatus: r.vat_status, vatNumber: r.vat_number, vatRate: Number(r.vat_rate || 0),
  iban: r.iban, paymentTermsDays: r.payment_terms_days, paymentMethods: r.payment_methods,
  bookingSlug: r.booking_slug,
  invoiceCounter: r.invoice_counter,
  avatar: r.avatar_data_url || "",
  rcsRm: r.rcs_rm || "",
  mediatorName: r.mediator_name || "",
  mediatorUrl: r.mediator_url || "",
});
const toBusiness = (u) => {
  const p = {};
  if (u.name !== undefined)             p.name = u.name;
  if (u.owner !== undefined)            p.owner = u.owner;
  if (u.initials !== undefined)         p.initials = u.initials;
  if (u.hue !== undefined)              p.hue = u.hue;
  if (u.phone !== undefined)            p.phone = u.phone;
  if (u.contactEmail !== undefined)     p.contact_email = u.contactEmail;
  if (u.address !== undefined)          p.address = u.address;
  if (u.postalCode !== undefined)       p.postal_code = u.postalCode;
  if (u.city !== undefined)             p.city = u.city;
  if (u.siret !== undefined)            p.siret = u.siret;
  if (u.legalForm !== undefined)        p.legal_form = u.legalForm;
  if (u.vatStatus !== undefined)        p.vat_status = u.vatStatus;
  if (u.vatNumber !== undefined)        p.vat_number = u.vatNumber;
  if (u.vatRate !== undefined)          p.vat_rate = u.vatRate;
  if (u.iban !== undefined)             p.iban = u.iban;
  if (u.paymentTermsDays !== undefined) p.payment_terms_days = u.paymentTermsDays;
  if (u.paymentMethods !== undefined)   p.payment_methods = u.paymentMethods;
  if (u.bookingSlug !== undefined)      p.booking_slug = u.bookingSlug;
  if (u.invoiceCounter !== undefined)   p.invoice_counter = u.invoiceCounter;
  if (u.avatar !== undefined)           p.avatar_data_url = u.avatar;
  if (u.rcsRm !== undefined)            p.rcs_rm = u.rcsRm;
  if (u.mediatorName !== undefined)     p.mediator_name = u.mediatorName;
  if (u.mediatorUrl !== undefined)      p.mediator_url = u.mediatorUrl;
  return p;
};

const fromClient = (r) => ({
  id: r.id, name: r.name, email: r.email || "", phone: r.phone || "",
  notes: r.notes || "", birthday: r.birthday || "", social: r.social || "",
  visits: r.visits || 0, spent: Number(r.spent || 0), fid: r.fid || 0,
  last: r.last_visit || "—", tags: r.tags || [], hue: r.hue || 30,
});
const toClient = (c) => {
  const p = {};
  if (c.name !== undefined)     p.name = c.name;
  if (c.email !== undefined)    p.email = c.email;
  if (c.phone !== undefined)    p.phone = c.phone;
  if (c.notes !== undefined)    p.notes = c.notes;
  if (c.birthday !== undefined) p.birthday = c.birthday;
  if (c.social !== undefined)   p.social = c.social;
  if (c.visits !== undefined)   p.visits = c.visits;
  if (c.spent !== undefined)    p.spent = c.spent;
  if (c.fid !== undefined)      p.fid = c.fid;
  if (c.last !== undefined)     p.last_visit = c.last;
  if (c.tags !== undefined)     p.tags = c.tags;
  if (c.hue !== undefined)      p.hue = c.hue;
  return p;
};

const fromAppointment = (r) => ({
  id: r.id,
  clientId: r.client_id, serviceId: r.service_id,
  day: r.day, h: Number(r.h), d: Number(r.d),
  color: r.color || "accent",
  done: !!r.done, invoiceId: r.invoice_id,
  completedAt: r.completed_at ? Date.parse(r.completed_at) : null,
});
const toAppointment = (a) => {
  const p = {};
  if (a.clientId !== undefined)    p.client_id = a.clientId;
  if (a.serviceId !== undefined)   p.service_id = a.serviceId;
  if (a.day !== undefined)         p.day = a.day;
  if (a.h !== undefined)           p.h = a.h;
  if (a.d !== undefined)           p.d = a.d;
  if (a.color !== undefined)       p.color = a.color;
  if (a.done !== undefined)        p.done = a.done;
  if (a.invoiceId !== undefined)   p.invoice_id = a.invoiceId;
  if (a.completedAt !== undefined) p.completed_at = a.completedAt ? new Date(a.completedAt).toISOString() : null;
  return p;
};

const fromInvoice = (r) => ({
  id: r.id, number: r.number, clientId: r.client_id,
  date: r.issued_at ? _fmtDateFR(r.issued_at) : "",
  amount: Number(r.amount || 0), paid: !!r.paid, vatRate: Number(r.vat_rate || 0),
  designation: r.designation || "",
  prestationDate: r.prestation_date ? _fmtDateFR(r.prestation_date) : "",
  dueDate: r.due_date ? _fmtDateFR(r.due_date) : "",
  creditOf: r.credit_of || "",
});
const toInvoice = (i) => {
  const p = {};
  if (i.number !== undefined)         p.number = i.number;
  if (i.clientId !== undefined)       p.client_id = i.clientId;
  if (i.date !== undefined)           p.issued_at = _dateToISO(i.date);
  if (i.amount !== undefined)         p.amount = i.amount;
  if (i.paid !== undefined)           p.paid = i.paid;
  if (i.vatRate !== undefined)        p.vat_rate = i.vatRate;
  if (i.designation !== undefined)    p.designation = i.designation;
  if (i.prestationDate !== undefined) p.prestation_date = _dateToISO(i.prestationDate);
  if (i.dueDate !== undefined)        p.due_date = _dateToISO(i.dueDate);
  if (i.creditOf !== undefined)       p.credit_of = i.creditOf;
  return p;
};

const fromStock = (r) => ({
  id: r.id, name: r.name, qty: r.qty, min: r.min_qty, price: Number(r.price || 0),
});
const toStock = (s) => {
  const p = {};
  if (s.name !== undefined)  p.name = s.name;
  if (s.qty !== undefined)   p.qty = s.qty;
  if (s.min !== undefined)   p.min_qty = s.min;
  if (s.price !== undefined) p.price = s.price;
  return p;
};

/* ----- Services (prestations) ----- */
const fromService = (r) => ({
  id: r.id, name: r.name,
  price: Number(r.price || 0),
  duration: Number(r.duration || 1),
  description: r.description || "",
  active: r.active === false ? false : true, // default true
});
const toService = (s) => {
  const p = {};
  if (s.name !== undefined)        p.name = s.name;
  if (s.price !== undefined)       p.price = s.price;
  if (s.duration !== undefined)    p.duration = s.duration;
  if (s.description !== undefined) p.description = s.description;
  if (s.active !== undefined)      p.active = s.active;
  return p;
};

const fromNotif = (r) => ({
  id: r.id, type: r.type, title: r.title, message: r.message,
  createdAt: r.created_at ? Date.parse(r.created_at) : Date.now(),
  read: !!r.read, clientId: r.client_id, appointmentId: r.appointment_id,
});

const fromVacation = (r) => ({
  id: r.id, start: r.starts_on, end: r.ends_on, reason: r.reason || "Fermeture",
});

const fromBookingSettings = (r) => r && ({
  enabled: r.enabled,
  slotDuration: r.slot_duration,
  leadTimeMinutes: r.lead_time_minutes,
  schedule: r.schedule || {},
  preferredSocial: r.preferred_social || "instagram",
});

const _fmtDateFR = (isoDate) => {
  try {
    const d = new Date(isoDate + "T00:00:00");
    return d.toLocaleDateString("fr-FR", { day: "numeric", month: "long", year: "numeric" });
  } catch { return isoDate; }
};
const _dateToISO = (d) => {
  // Already ISO ?
  if (/^\d{4}-\d{2}-\d{2}/.test(d)) return d.slice(0, 10);
  // Else keep today
  return new Date().toISOString().slice(0, 10);
};

/* ---------- API publique ---------- */

const cbCloud = {
  /** true si on a un client Supabase + un user connecté */
  isActive() { return !!(_sb() && _uid()); },

  async loadAll() {
    if (!cbCloud.isActive()) return null;
    const sb = _sb();
    const [
      biz, cl, ap, inv, st, nt, bs, vac, sv,
    ] = await Promise.all([
      sb.from("businesses").select("*").single(),
      sb.from("clients").select("*").order("created_at", { ascending: false }),
      sb.from("appointments").select("*"),
      sb.from("invoices").select("*").order("created_at", { ascending: false }),
      sb.from("stock_items").select("*").order("created_at", { ascending: true }),
      sb.from("notifications").select("*").order("created_at", { ascending: false }).limit(50),
      sb.from("booking_settings").select("*").single(),
      sb.from("vacations").select("*").order("starts_on"),
      sb.from("services").select("*").order("created_at", { ascending: false }),
    ]);

    if (biz.error) console.warn("[cbCloud] businesses:", biz.error.message);
    if (cl.error)  console.warn("[cbCloud] clients:", cl.error.message);
    if (ap.error)  console.warn("[cbCloud] appointments:", ap.error.message);
    if (inv.error) console.warn("[cbCloud] invoices:", inv.error.message);
    if (st.error)  console.warn("[cbCloud] stock:", st.error.message);
    if (nt.error)  console.warn("[cbCloud] notifications:", nt.error.message);
    if (bs.error)  console.warn("[cbCloud] booking_settings:", bs.error.message);
    if (vac.error) console.warn("[cbCloud] vacations:", vac.error.message);
    if (sv.error)  console.warn("[cbCloud] services:", sv.error.message);

    const business = fromBusiness(biz.data) || {};
    const bookingSettings = {
      ...(fromBookingSettings(bs.data) || { enabled: true, slotDuration: 30, leadTimeMinutes: 120, schedule: {} }),
      vacations: (vac.data || []).map(fromVacation),
      activeServiceIds: [], // legacy
      autoConfirm: true,
    };

    return {
      business,
      clients:       (cl.data  || []).map(fromClient),
      appointments:  (ap.data  || []).map(fromAppointment),
      invoices:      (inv.data || []).map(fromInvoice),
      stock:         (st.data  || []).map(fromStock),
      notifications: (nt.data  || []).map(fromNotif),
      services:      (sv.data  || []).map(fromService),
      bookingSettings,
      fideliteRules: {
        visits:      biz.data ? biz.data.fidelity_visits : 10,
        rewardLabel: biz.data ? biz.data.fidelity_reward : "1 prestation offerte",
      },
      // Local-only (plus tard)
      messages: {}, conversations: [], activeConv: null, promos: [],
      invoiceCounter: biz.data ? (biz.data.invoice_counter || 1) : 1,
    };
  },

  /* ----- Realtime — push instantané des changements DB → UI ----- */
  /* Renvoie une fonction de désabonnement. Les handlers reçoivent soit
     { row: <objet déjà mappé> } pour INSERT/UPDATE, soit { deleteId } pour
     DELETE. Le front fait un upsert/filter par id côté state. */
  subscribe(handlers) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return () => {};

    const filter = `user_id=eq.${uid}`;
    const channel = sb.channel(`cb-rt-${uid}-${Date.now()}`);

    const listen = (table, mapper, handlerName) => {
      channel.on(
        "postgres_changes",
        { event: "*", schema: "public", table, filter },
        (payload) => {
          const fn = handlers[handlerName];
          if (!fn) return;
          if (payload.eventType === "DELETE") {
            fn({ deleteId: payload.old && payload.old.id });
          } else {
            fn({ row: mapper(payload.new) });
          }
        }
      );
    };

    listen("appointments",  fromAppointment, "onAppointment");
    listen("clients",       fromClient,      "onClient");
    listen("invoices",      fromInvoice,     "onInvoice");
    listen("services",      fromService,     "onService");
    listen("stock_items",   fromStock,       "onStock");
    listen("notifications", fromNotif,       "onNotification");
    listen("vacations",     fromVacation,    "onVacation");

    channel.subscribe();
    return () => { try { sb.removeChannel(channel); } catch {} };
  },

  /* ----- Compte (RGPD) ----- */
  async resetMyData() {
    const sb = _sb(); if (!sb) return;
    const { error } = await sb.rpc("cb_reset_my_data");
    if (error) throw error;
  },
  async deleteMyAccount() {
    const sb = _sb(); if (!sb) return;
    const { error } = await sb.rpc("cb_delete_my_account");
    if (error) throw error;
  },

  /* ----- Services ----- */
  async upsertService(s, isInsert) {
    const sb = _sb(); if (!sb) return;
    const uid = _uid(); if (!uid) return;
    if (isInsert) {
      const { data, error } = await sb.from("services")
        .insert({ ...toService(s), user_id: uid, id: s.id }).select().single();
      if (error) throw error; return fromService(data);
    }
    const { error } = await sb.from("services").update(toService(s)).eq("id", s.id);
    if (error) throw error;
  },
  async removeService(id) {
    const { error } = await _sb().from("services").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Clients ----- */
  async upsertClient(c, isInsert) {
    const sb = _sb(); if (!sb) return;
    const uid = _uid(); if (!uid) return;
    if (isInsert) {
      const { data, error } = await sb.from("clients").insert({ ...toClient(c), user_id: uid, id: c.id }).select().single();
      if (error) throw error; return fromClient(data);
    }
    const { error } = await sb.from("clients").update(toClient(c)).eq("id", c.id);
    if (error) throw error;
  },
  async removeClient(id) {
    const { error } = await _sb().from("clients").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Appointments ----- */
  async upsertAppointment(a, isInsert) {
    const sb = _sb(); if (!sb) return;
    const uid = _uid(); if (!uid) return;
    if (isInsert) {
      const { data, error } = await sb.from("appointments").insert({ ...toAppointment(a), user_id: uid, id: a.id }).select().single();
      if (error) throw error; return fromAppointment(data);
    }
    const { error } = await sb.from("appointments").update(toAppointment(a)).eq("id", a.id);
    if (error) throw error;
  },
  async removeAppointment(id) {
    const { error } = await _sb().from("appointments").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Invoices ----- */
  async upsertInvoice(i, isInsert) {
    const sb = _sb(); if (!sb) return;
    const uid = _uid(); if (!uid) return;
    if (isInsert) {
      const { data, error } = await sb.from("invoices").insert({ ...toInvoice(i), user_id: uid, id: i.id }).select().single();
      if (error) throw error; return fromInvoice(data);
    }
    const { error } = await sb.from("invoices").update(toInvoice(i)).eq("id", i.id);
    if (error) throw error;
  },
  async removeInvoice(id) {
    const { error } = await _sb().from("invoices").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Stock ----- */
  async upsertStock(s, isInsert) {
    const sb = _sb(); if (!sb) return;
    const uid = _uid(); if (!uid) return;
    if (isInsert) {
      const { data, error } = await sb.from("stock_items").insert({ ...toStock(s), user_id: uid, id: s.id }).select().single();
      if (error) throw error; return fromStock(data);
    }
    const { error } = await sb.from("stock_items").update(toStock(s)).eq("id", s.id);
    if (error) throw error;
  },
  async removeStock(id) {
    const { error } = await _sb().from("stock_items").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Business (paramètres + compteur de facture + règles fidélité) ----- */
  async updateBusiness(patch) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return;
    const { error } = await sb.from("businesses").update(toBusiness(patch)).eq("user_id", uid);
    if (error) throw error;
  },
  async setFideliteRules(rules) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return;
    const p = {};
    if (rules.visits !== undefined)      p.fidelity_visits = rules.visits;
    if (rules.rewardLabel !== undefined) p.fidelity_reward = rules.rewardLabel;
    const { error } = await sb.from("businesses").update(p).eq("user_id", uid);
    if (error) throw error;
  },
  async incrementInvoiceCounter(next) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return;
    const { error } = await sb.from("businesses").update({ invoice_counter: next }).eq("user_id", uid);
    if (error) throw error;
  },

  /* ----- Booking settings ----- */
  async updateBookingSettings(patch) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return;
    const p = {};
    if (patch.enabled !== undefined)         p.enabled = patch.enabled;
    if (patch.slotDuration !== undefined)    p.slot_duration = patch.slotDuration;
    if (patch.leadTimeMinutes !== undefined) p.lead_time_minutes = patch.leadTimeMinutes;
    if (patch.schedule !== undefined)        p.schedule = patch.schedule;
    if (patch.preferredSocial !== undefined) p.preferred_social = patch.preferredSocial;
    if (Object.keys(p).length === 0) return;
    // upsert : si la ligne n'existe pas encore (cas rare)
    const { error } = await sb.from("booking_settings").upsert({ user_id: uid, ...p }, { onConflict: "user_id" });
    if (error) throw error;
  },

  /* ----- Vacations ----- */
  async addVacation(v) {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return v;
    const { data, error } = await sb.from("vacations")
      .insert({ user_id: uid, starts_on: v.start, ends_on: v.end, reason: v.reason || "Fermeture" })
      .select().single();
    if (error) throw error;
    return fromVacation(data);
  },
  async removeVacation(id) {
    const { error } = await _sb().from("vacations").delete().eq("id", id);
    if (error) throw error;
  },

  /* ----- Notifications ----- */
  async markAllNotificationsRead() {
    const sb = _sb(); const uid = _uid();
    if (!sb || !uid) return;
    const { error } = await sb.from("notifications").update({ read: true }).eq("user_id", uid);
    if (error) throw error;
  },
  async removeNotification(id) {
    const { error } = await _sb().from("notifications").delete().eq("id", id);
    if (error) throw error;
  },
};

window.cbCloud = cbCloud;
