// Data loader — fetches positions / candidates / app data from Supabase.
// On admin's first load, auto-seeds DB from data.jsx mock seed.
// Exposes window.useAppData() React hook.

(function () {
  const supabase = window.AUTH && window.AUTH.supabase;
  // The mock data file (data.jsx) sets window.MOCK on load. We treat it as the seed.
  const SEED = window.MOCK || {};

  // Internal cache so we don't refetch on every render.
  // `version` bumps on every invalidate() so subscribed hooks re-fetch.
  let cache = null;
  let version = 0;
  const subscribers = new Set();
  function notifyAll() { version++; subscribers.forEach((fn) => fn(version)); }

  async function seedIfEmpty() {
    if (!supabase) return;
    const { count: posCount, error: countErr } = await supabase
      .from("positions")
      .select("id", { count: "exact", head: true });
    if (countErr) {
      console.warn("[data] count error:", countErr.message);
      return;
    }
    if (posCount && posCount > 0) return;  // already seeded

    console.info("[data] DB empty — seeding from data.jsx mock");

    const positionsRows  = (SEED.POSITIONS  || []).map((p) => ({ id: p.id, data: p }));
    const candidatesRows = (SEED.CANDIDATES || []).map((c) => ({ id: c.id, data: c }));
    const appDataRows = [
      { key: "dash_stats",    data: SEED.DASH_STATS    || {} },
      { key: "activity",      data: SEED.ACTIVITY      || [] },
      { key: "audit_events",  data: SEED.AUDIT_EVENTS  || [] },
      { key: "today_actions", data: SEED.TODAY_ACTIONS || [] },
    ];

    const errors = [];
    if (positionsRows.length) {
      const { error } = await supabase.from("positions").upsert(positionsRows);
      if (error) errors.push("positions: " + error.message);
    }
    if (candidatesRows.length) {
      const { error } = await supabase.from("candidates").upsert(candidatesRows);
      if (error) errors.push("candidates: " + error.message);
    }
    {
      const { error } = await supabase.from("app_data").upsert(appDataRows);
      if (error) errors.push("app_data: " + error.message);
    }

    if (errors.length) console.warn("[data] seed errors:", errors);
    else console.info("[data] ✅ seed complete");
  }

  async function fetchAll() {
    if (!supabase) {
      // No supabase client → fall back to mock data so UI doesn't break
      return {
        POSITIONS:    SEED.POSITIONS    || [],
        CANDIDATES:   SEED.CANDIDATES   || [],
        DASH_STATS:   SEED.DASH_STATS   || {},
        ACTIVITY:     SEED.ACTIVITY     || [],
        AUDIT_EVENTS: SEED.AUDIT_EVENTS || [],
        TODAY_ACTIONS:SEED.TODAY_ACTIONS|| [],
        source: "mock",
      };
    }

    const [posRes, candRes, miscRes] = await Promise.all([
      supabase.from("positions").select("data").order("id"),
      supabase.from("candidates").select("data").order("id"),
      supabase.from("app_data").select("key, data"),
    ]);

    const POSITIONS  = (posRes.data  || []).map((r) => r.data);
    const CANDIDATES = (candRes.data || []).map((r) => r.data);
    const miscByKey  = Object.fromEntries((miscRes.data || []).map((r) => [r.key, r.data]));

    const realActivity   = miscByKey.activity      || SEED.ACTIVITY      || [];
    const mockAuditEvts  = miscByKey.audit_events  || SEED.AUDIT_EVENTS  || [];

    // Adapt real activity log → AUDIT_EVENTS shape so the AuditLog UI renders them.
    const findName = (id, type) =>
      (type === "candidate" ? CANDIDATES : POSITIONS).find(x => x.id === id);
    const adaptActivity = (a) => {
      const cand = a.candidateId ? findName(a.candidateId, "candidate") : null;
      const pos  = a.positionId  ? findName(a.positionId,  "position")  : null;
      const target = a.name
        ? `${a.name}${pos ? " — " + pos.title : ""}`
        : a.title
          ? a.title
          : (cand ? cand.name : (pos ? pos.title : ""));
      return {
        id: a.id,
        ts: a.ts || a.timestamp || "",
        actor: a.actor || "system",
        actorRole: a.actor && a.actor.includes("@") ? "User" : "System",
        kind:
          a.type === "candidate_status_changed" ? "status" :
          a.type === "candidate_created"        ? "upload" :
          a.type === "candidate_saved"          ? "override" :
          a.type === "position_saved"           ? "edit"   :
          a.type === "position_deleted"         ? "delete" :
          a.type === "candidate_status_changed" ? "status" : (a.type || "event"),
        target,
        targetType: a.candidateId ? "candidate" : (a.positionId ? "position" : "system"),
        positionId: a.positionId || (cand && cand.positionId) || null,
        before: a.from || null,
        after: a.to || (a.type === "position_saved" ? "Position saved" :
                       a.type === "candidate_created" ? "Created from upload" :
                       a.type === "candidate_saved" ? "Updated" : null),
        reason: a.reason || null,
      };
    };

    const adaptedActivity = realActivity.map(adaptActivity);
    const mergedAudit = [...adaptedActivity, ...mockAuditEvts]
      .sort((a, b) => String(b.ts).localeCompare(String(a.ts)));

    // Compute Dashboard stats from REAL data instead of the static seed
    const liveDashStats = computeDashStats(POSITIONS, CANDIDATES, SEED.DASH_STATS);

    return {
      POSITIONS,
      CANDIDATES,
      DASH_STATS:    liveDashStats,
      ACTIVITY:      realActivity,
      AUDIT_EVENTS:  mergedAudit,
      TODAY_ACTIONS: miscByKey.today_actions || SEED.TODAY_ACTIONS || [],
      source: "supabase",
    };
  }

  // Aggregate live counts from positions + candidates so the Dashboard reflects reality.
  // Keeps the same field names + delta keys the existing Dashboard component renders.
  function computeDashStats(positions, candidates, seed) {
    const total = candidates.length;
    const pending = candidates.filter(c => c.status === "New" || c.status === "AI screened").length;
    const shortlisted = candidates.filter(c => c.status === "Shortlisted").length;
    const scores = candidates.map(c => Number(c.score) || 0).filter(n => n > 0);
    const avg = scores.length ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length) : 0;
    return {
      totalCandidates: total,
      totalCandidatesDeltaKey: (seed && seed.totalCandidatesDeltaKey) || "weekly24",
      pendingReview: pending,
      pendingReviewDeltaKey: (seed && seed.pendingReviewDeltaKey) || "awaitingHR",
      avgFitScore: avg,
      avgFitScoreDeltaKey: (seed && seed.avgFitScoreDeltaKey) || "vsLastMonth",
      shortlisted: shortlisted,
      shortlistedDeltaKey: (seed && seed.shortlistedDeltaKey) || "across12",
    };
  }

  function useAppData(isAdmin) {
    const [state, setState] = React.useState({ data: cache, loading: !cache, error: null });
    const [tick, setTick] = React.useState(version);

    // Subscribe to invalidate() events
    React.useEffect(() => {
      subscribers.add(setTick);
      return () => subscribers.delete(setTick);
    }, []);

    React.useEffect(() => {
      if (cache && tick === version) {
        // We already have fresh data
        setState({ data: cache, loading: false, error: null });
        return;
      }
      let cancelled = false;
      (async () => {
        try {
          if (isAdmin) {
            try { await seedIfEmpty(); }
            catch (e) { console.warn("[data] seed failed (continuing):", e); }
          }
          const data = await fetchAll();
          if (cancelled) return;
          cache = data;
          setState({ data, loading: false, error: null });
        } catch (e) {
          if (cancelled) return;
          console.error("[data] fetch failed, falling back to mock:", e);
          const fallback = {
            POSITIONS:    SEED.POSITIONS    || [],
            CANDIDATES:   SEED.CANDIDATES   || [],
            DASH_STATS:   SEED.DASH_STATS   || {},
            ACTIVITY:     SEED.ACTIVITY     || [],
            AUDIT_EVENTS: SEED.AUDIT_EVENTS || [],
            TODAY_ACTIONS:SEED.TODAY_ACTIONS|| [],
            source: "mock-fallback",
          };
          cache = fallback;
          setState({ data: fallback, loading: false, error: e });
        }
      })();
      return () => { cancelled = true; };
    }, [isAdmin, tick]);

    return state;
  }

  // Manual cache invalidation — every subscribed useAppData refetches.
  function invalidate() { cache = null; notifyAll(); }

  // ---------- mutation API ----------

  function newId(prefix) {
    return prefix + "_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
  }

  async function savePosition(position) {
    if (!supabase) return { error: { message: "offline" } };
    const id = position.id || newId("pos");
    const data = { ...position, id };
    const { error } = await supabase.from("positions").upsert({ id, data });
    if (!error) {
      invalidate();
      logActivity({ type: "position_saved", positionId: id, title: data.title });
    }
    return { error, id };
  }

  async function deletePosition(id) {
    if (!supabase) return { error: { message: "offline" } };
    const { error } = await supabase.from("positions").delete().eq("id", id);
    if (!error) {
      invalidate();
      logActivity({ type: "position_deleted", positionId: id });
    }
    return { error };
  }

  async function saveCandidate(candidate) {
    if (!supabase) return { error: { message: "offline" } };
    const id = candidate.id || newId("cand");
    const data = { ...candidate, id };
    const { error } = await supabase.from("candidates").upsert({ id, data });
    if (!error) {
      invalidate();
      logActivity({ type: "candidate_saved", candidateId: id, name: data.name });
    }
    return { error, id };
  }

  async function updateCandidateStatus(candidateId, newStatus, opts = {}) {
    if (!supabase) return { error: { message: "offline" } };
    const { data: rows, error: readErr } = await supabase
      .from("candidates").select("data").eq("id", candidateId).single();
    if (readErr) return { error: readErr };
    const updated = { ...rows.data, status: newStatus, ...(opts.recommendation && { recommendation: opts.recommendation }) };
    const { error } = await supabase.from("candidates").update({ data: updated }).eq("id", candidateId);
    if (!error) {
      invalidate();
      logActivity({ type: "candidate_status_changed", candidateId, name: updated.name, from: rows.data.status, to: newStatus });
    }
    return { error };
  }

  // Create a candidate stub from a freshly-uploaded resume file
  async function createCandidateFromUpload({ file, positionId, storagePath }) {
    if (!supabase) return { error: { message: "offline" } };
    const id = newId("cand");
    const nameFromFile = file.name
      .replace(/\.(pdf|docx?|png|jpe?g)$/i, "")
      .replace(/[_\-]+/g, " ")
      .replace(/\s+/g, " ")
      .trim();
    const initials = nameFromFile.split(" ").map((s) => s[0]).filter(Boolean).slice(0, 2).join("").toUpperCase() || "??";
    const palette = ["#E8C39E", "#C19A6B", "#B08458", "#9F7849", "#8B6A3F", "#735639"];
    const avatarColor = palette[Math.floor(Math.random() * palette.length)];

    const data = {
      id,
      name: nameFromFile || file.name,
      initials,
      avatarColor,
      positionId,
      currentTitle: "—",
      currentCompany: "—",
      location: "—",
      yearsExperience: 0,
      score: 0,
      recommendation: "Pending review",
      status: "New",
      appliedDate: new Date().toISOString().slice(0, 10),
      resumePath: storagePath || null,
      resumeFilename: file.name,
      uploadedAt: new Date().toISOString(),
    };
    const { error } = await supabase.from("candidates").insert({ id, data });
    if (!error) {
      invalidate();
      logActivity({ type: "candidate_created", candidateId: id, name: data.name, positionId, source: "resume_upload" });
    }
    return { error, id };
  }

  // Activity log — appends to app_data.activity (cap at last 500 events)
  async function logActivity(event) {
    if (!supabase) return;
    try {
      const enriched = {
        id: newId("act"),
        ts: new Date().toISOString(),
        actor: (window.AUTH && window.AUTH.supabase && window.AUTH.supabase.auth)
          ? (await window.AUTH.supabase.auth.getUser()).data?.user?.email || "unknown"
          : "system",
        ...event,
      };
      const { data: rows } = await supabase.from("app_data").select("data").eq("key", "activity").single();
      const prev = Array.isArray(rows?.data) ? rows.data : [];
      const next = [enriched, ...prev].slice(0, 500);
      await supabase.from("app_data").upsert({ key: "activity", data: next });
    } catch (e) {
      console.warn("[data] logActivity failed (non-fatal):", e);
    }
  }

  // Get a signed URL for a private resume file (valid 1 hour)
  async function getResumeUrl(storagePath) {
    if (!supabase || !storagePath) return null;
    const { data, error } = await supabase.storage.from("resumes").createSignedUrl(storagePath, 3600);
    if (error) { console.warn("[data] signed url error:", error.message); return null; }
    return data.signedUrl;
  }

  window.DATA = {
    useAppData, invalidate, fetchAll, seedIfEmpty,
    api: {
      savePosition,
      deletePosition,
      saveCandidate,
      updateCandidateStatus,
      createCandidateFromUpload,
      logActivity,
      getResumeUrl,
      newId,
    },
  };
})();
