// Real data from backend API (dev server proxies /api to cloud backend)
const API_BASE_URL = '/api';

// Stage mapping: Backend stage name → Frontend stage ID
const STAGE_MAP = {
  'sourced': 'sourced',
  'intro_call': 'intro',
  'first_technical': 'tech1',
  'second_technical': 'tech2',
  'third_technical': 'tech3',
  'onsite': 'onsite',
  'offer': 'offer',
  'hired': 'hired',
  'backburner': 'backburner',
  'rejected': 'rejected',
  'not_interested': 'not_interested',
  'declined_offer': 'declined',
};

// Frontend stages (with visual styling)
const STAGES = [
  { id: "sourced",    label: "Sourced",         short: "Sourced",     color: "var(--nl-paper)",     fg: "var(--fg-2)",     accent: "#A8AEB0" },
  { id: "intro",      label: "Intro call",      short: "Intro",       color: "#D9E2DC",             fg: "#3E5A4D",         accent: "#96B1AD" },
  { id: "tech1",      label: "1st technical",   short: "Tech 1",      color: "#D9E1EE",             fg: "#3E4E72",         accent: "#7595D5" },
  { id: "tech2",      label: "2nd technical",   short: "Tech 2",      color: "#C8D3E8",             fg: "#324769",         accent: "#637DA7" },
  { id: "tech3",      label: "3rd technical",   short: "Tech 3",      color: "#B7C3DD",             fg: "#2C4060",         accent: "#536C97" },
  { id: "onsite",     label: "Onsite",          short: "Onsite",      color: "#EFE2C4",             fg: "#7A5A24",         accent: "#E3A54E" },
  { id: "offer",      label: "Offer",           short: "Offer",       color: "#E6D2B4",             fg: "#6E4F26",         accent: "#A9824C" },
  { id: "hired",      label: "Hired",           short: "Hired",       color: "#CADDC9",             fg: "#3F5E45",         accent: "#6B8E76" },
  { id: "backburner", label: "Backburner",      short: "Hold",        color: "#E2DFD7",             fg: "#67676A",         accent: "#9A9A9D" },
  { id: "rejected",       label: "Rejected by us",      short: "Rejected",    color: "#E8DDD8",             fg: "#7A5A52",         accent: "#B4553D" },
  { id: "not_interested", label: "Not interested in us", short: "Not interested", color: "#E0D8D2",          fg: "#6B5E55",         accent: "#8B7060" },
  { id: "declined",       label: "Declined offer",       short: "Declined",    color: "#DCD5CE",             fg: "#6B5E55",         accent: "#8B7765" },
];

const STAGE_BY_ID = Object.fromEntries(STAGES.map(s => [s.id, s]));
const STAGE_FLOW = ["sourced", "intro", "tech1", "tech2", "tech3", "onsite", "offer", "hired"];

// Team data — fetched from /api/auth/users, refreshed every 10 min
const TEAM_COLORS = [
  "#7595D5", "#E3A54E", "#96B1AD", "#A9824C", "#637DA7",
  "#6B8E76", "#B4553D", "#9C6B4F", "#536C97", "#8B7765",
];

function _userColor(id) {
  let hash = 0;
  for (let i = 0; i < id.length; i++) hash = ((hash << 5) - hash + id.charCodeAt(i)) | 0;
  return TEAM_COLORS[Math.abs(hash) % TEAM_COLORS.length];
}

function _initials(name) {
  return (name || "").split(" ").map(p => p[0] || "").join("").slice(0, 2).toUpperCase();
}

function _apiUserToTeam(u) {
  const roleLabel = { admin: "Admin", nuance_team: "Team", external_recruiter: "External" };
  return {
    id: u.id,
    email: (u.email || "").toLowerCase(),
    name: u.full_name,
    initials: _initials(u.full_name),
    color: _userColor(u.id),
    role: roleLabel[u.role] || u.role,
    raw_role: u.role,
    avatar_url: u.avatar_url || null,
    last_login: u.last_login || null,
  };
}

let TEAM = [];
let RECRUITERS = [];
let TEAM_BY_ID = {};
let TEAM_BY_EMAIL = {};

const EXT_AGENCIES = [];
const EXT_RECRUITERS = [];

const ROLES = {
  admin:        { id: "admin",        label: "Admin",           accent: "#1D3140", desc: "Full access · see and change everything" },
  interviewer:  { id: "interviewer",  label: "Interviewer",     accent: "#7595D5", desc: "All candidates · limited visibility" },
  external:     { id: "external",     label: "External Agent",  accent: "#9C6B4F", desc: "Only candidates they submitted" },
};

let VIEWERS = [];

const SOURCES = [
  { id: "greenhouse",        label: "Greenhouse",        icon: "gh" },
  { id: "paraform",          label: "Paraform",          icon: "pf" },
  { id: "Jon Luzha",         label: "Jon Luzha",         icon: "jl" },
  { id: "techire",           label: "Techire",           icon: "te" },
  { id: "Nicholas Jackson",  label: "Nicholas Jackson",  icon: "nj" },
  { id: "referral",          label: "Referral",          icon: "ref" },
  { id: "inbound",           label: "Inbound",           icon: "→" },
  { id: "outbound",          label: "Outbound",          icon: "src" },
  { id: "unknown",           label: "Unknown",           icon: "?" },
];

// Helper functions
function nextStage(stageId) {
  const idx = STAGE_FLOW.indexOf(stageId);
  if (idx === -1 || idx === STAGE_FLOW.length - 1) return null;
  return STAGE_FLOW[idx + 1];
}

function suggestedForStage(stageId) {
  return TEAM;
}

// Calculate days in stage
function daysInStage(enteredStageAt) {
  if (!enteredStageAt) return 0;
  // Backend stores UTC times without 'Z' suffix — ensure JS parses as UTC
  const ts = enteredStageAt.endsWith('Z') ? enteredStageAt : enteredStageAt + 'Z';
  const entered = new Date(ts);
  const now = new Date();
  return Math.max(0, Math.floor((now - entered) / (1000 * 60 * 60 * 24)));
}

// Global data store (will be populated on app init)
let JOBS = [];
let CANDIDATES = [];
let JOBS_BY_ID = {};

// Fetch data from backend
async function fetchJobs() {
  try {
    const response = await fetch(`${API_BASE_URL}/jobs?limit=5000`);
    if (!response.ok) throw new Error(`Failed to fetch jobs: ${response.statusText}`);

    const jobs = await response.json();

    JOBS = jobs.map(job => ({
      id: job.id,
      title: job.title,
      dept: job.department || "N/A",
      loc: "Seattle",  // Default location (not in backend data yet)
      type: "Full-time",
      opened: new Date(job.created_at).toLocaleDateString('en-US', { timeZone: 'America/Los_Angeles', month: 'short', day: 'numeric' }),
      manager: "Fangchang M.",  // Default (not in backend data yet)
      priority: job.priority || "P1",
      status: job.status,
      candidateCount: job.candidate_count || 0,
    }));

    JOBS_BY_ID = Object.fromEntries(JOBS.map(j => [j.id, j]));
    return JOBS;
  } catch (error) {
    console.error('Error fetching jobs:', error);
    return [];
  }
}

async function fetchCandidates() {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates?limit=5000`);
    if (!response.ok) throw new Error(`Failed to fetch candidates: ${response.statusText}`);

    const candidates = await response.json();

    CANDIDATES = candidates.map((c, idx) => {
      const job = JOBS_BY_ID[c.job_id] || { id: c.job_id, title: "Unknown Job" };
      const stage = STAGE_MAP[c.stage] || 'sourced';
      const recruiter = (c.assigned_recruiter_id && TEAM_BY_ID[c.assigned_recruiter_id])
        ? TEAM_BY_ID[c.assigned_recruiter_id]
        : { id: null, name: "No owner", initials: "—", color: "#A8AEB0" };
      const source = SOURCES.find(s => s.id === c.source) || SOURCES[0];

      // Extract first and last name from full name
      const nameParts = (c.name || "Unknown").split(' ');
      const first = nameParts[0] || "Unknown";
      const last = nameParts.slice(1).join(' ') || "";

      return {
        id: c.id,
        first,
        last,
        name: c.name,
        avatar: c.photo_url || null,
        stage,
        jobId: job.id,
        jobTitle: job.title,
        recruiter: recruiter.id,
        recruiterName: recruiter.name,
        recruiterInitials: recruiter.initials,
        recruiterColor: recruiter.color,
        recruiterAvatar: recruiter.avatar_url || null,
        source: source.id,
        sourceLabel: source.label,
        company: c.current_company || "N/A",
        title: c.current_role || "N/A",
        location: c.location || "N/A",
        fit: c.ai_fit_score || 0,
        days: daysInStage(c.entered_stage_at),
        lastActivity: c.entered_stage_at
          ? `Entered ${(STAGE_BY_ID[stage] || {}).label || stage} ${new Date(c.entered_stage_at.endsWith('Z') ? c.entered_stage_at : c.entered_stage_at + 'Z').toLocaleDateString("en-US", { timeZone: "America/Los_Angeles", month: "short", day: "numeric" })}`
          : "Imported from Greenhouse",
        salary: c.desired_compensation || "N/A",
        salaryLo: 0,
        salaryHi: 0,
        starred: false,
        flagged: false,
        referredBy: c.referrer || null,
        submittedById: null,
        submittedByName: c.submitted_by || null,
        submittedByAgency: null,
        panelMembers: [],  // TODO: Populate from interviews
        phone: c.phone,
        email: c.email,
        created_at: c.created_at,
        companiesText: c.companies_text || "",
        educationText: c.education_text || "",
        _searchHay: [c.name, c.email, c.companies_text, c.education_text]
          .filter(Boolean).join(" ").toLowerCase(),
        duplicateIds: [],
      };
    });

    // Compute duplicate groups by email (case-insensitive, skip placeholders)
    const emailGroups = {};
    for (const c of CANDIDATES) {
      if (!c.email || c.email.includes("placeholder")) continue;
      const key = c.email.toLowerCase();
      if (!emailGroups[key]) emailGroups[key] = [];
      emailGroups[key].push(c.id);
    }
    // Also group by exact normalized name (lowercase, trimmed)
    const nameGroups = {};
    for (const c of CANDIDATES) {
      const key = c.name.toLowerCase().trim();
      if (!key || key === "unknown") continue;
      if (!nameGroups[key]) nameGroups[key] = [];
      nameGroups[key].push(c.id);
    }
    // Merge: for each candidate, collect all duplicate IDs (union of email + name matches)
    for (const c of CANDIDATES) {
      const dupSet = new Set();
      const emailKey = c.email && !c.email.includes("placeholder") ? c.email.toLowerCase() : null;
      const nameKey = c.name.toLowerCase().trim();
      if (emailKey && emailGroups[emailKey]) {
        for (const id of emailGroups[emailKey]) if (id !== c.id) dupSet.add(id);
      }
      if (nameKey && nameKey !== "unknown" && nameGroups[nameKey]) {
        for (const id of nameGroups[nameKey]) if (id !== c.id) dupSet.add(id);
      }
      c.duplicateIds = Array.from(dupSet);
    }

    return CANDIDATES;
  } catch (error) {
    console.error('Error fetching candidates:', error);
    return [];
  }
}

async function fetchCandidateDetail(candidateId) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}`);
    if (!response.ok) return null;
    return await response.json();
  } catch (error) {
    console.error('Error fetching candidate detail:', error);
    return null;
  }
}

async function fetchCandidateInterviews(candidateId) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/interviews`);
    if (!response.ok) {
      console.error(`Failed to fetch interviews: ${response.status}`);
      return [];
    }
    const interviews = await response.json();
    return interviews;
  } catch (error) {
    console.error('Error fetching interviews:', error);
    return [];
  }
}

// Reverse map: frontend stage ID → backend stage name
const REVERSE_STAGE_MAP = Object.fromEntries(
  Object.entries(STAGE_MAP).map(([backend, frontend]) => [frontend, backend])
);

async function updateCandidateStage(candidateId, frontendStageId) {
  const backendStage = REVERSE_STAGE_MAP[frontendStageId];
  if (!backendStage) {
    console.error('Unknown stage:', frontendStageId);
    return null;
  }
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/stage`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem("ats_token")}` },
      body: JSON.stringify({ stage: backendStage }),
    });
    if (!response.ok) {
      const err = await response.json().catch(() => ({}));
      console.error('Failed to update stage:', err);
      return null;
    }
    const result = await response.json();
    const entry = CANDIDATES.find(c => c.id === candidateId);
    if (entry) {
      entry.stage = frontendStageId;
      entry.days = 0;
      entry.lastActivity = `Entered ${(STAGE_BY_ID[frontendStageId] || {}).label || frontendStageId} just now`;
    }
    notifyCandidatesChanged();
    return result;
  } catch (error) {
    console.error('Error updating candidate stage:', error);
    return null;
  }
}

async function updateCandidateSource(candidateId, source, referrer) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/source`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem("ats_token")}` },
      body: JSON.stringify({ source, referrer: referrer || null }),
    });
    if (!response.ok) {
      const err = await response.json().catch(() => ({}));
      console.error('Failed to update source:', err);
      return null;
    }
    const result = await response.json();
    const entry = CANDIDATES.find(c => c.id === candidateId);
    if (entry) {
      const srcObj = SOURCES.find(s => s.id === source) || SOURCES[0];
      entry.source = srcObj.id;
      entry.sourceLabel = srcObj.label;
      entry.referredBy = referrer || null;
    }
    notifyCandidatesChanged();
    return result;
  } catch (error) {
    console.error('Error updating candidate source:', error);
    return null;
  }
}

async function updateCandidateProfile(candidateId, fields) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/profile`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem("ats_token")}` },
      body: JSON.stringify(fields),
    });
    if (!response.ok) return null;
    const result = await response.json();
    const entry = CANDIDATES.find(c => c.id === candidateId);
    if (entry) {
      if (result.name) { entry.name = result.name; entry.first = result.name.split(" ")[0]; }
      if (result.current_role !== undefined) entry.title = result.current_role || "N/A";
      if (result.job_id) {
        entry.jobId = result.job_id;
        const j = JOBS.find(j => j.id === result.job_id);
        if (j) entry.jobTitle = j.title;
      }
      if (result.desired_compensation !== undefined) entry.salary = result.desired_compensation || "";
      if (result.assigned_recruiter_id !== undefined) {
        entry.recruiter = result.assigned_recruiter_id;
        const t = TEAM_BY_ID[result.assigned_recruiter_id];
        if (t) { entry.recruiterName = t.name; entry.recruiterInitials = t.initials; entry.recruiterColor = t.color; }
      }
    }
    notifyCandidatesChanged();
    return result;
  } catch (error) {
    console.error('Error updating candidate profile:', error);
    return null;
  }
}

// Fetch email threads for a candidate
async function fetchCandidateEmails(candidateId) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/emails`);
    if (!response.ok) return [];
    return await response.json();
  } catch (error) {
    console.error('Error fetching emails:', error);
    return [];
  }
}

// Fetch activities for a candidate
async function fetchCandidateActivities(candidateId) {
  try {
    const response = await fetch(`${API_BASE_URL}/candidates/${candidateId}/activities`);
    if (!response.ok) {
      console.error(`Failed to fetch activities: ${response.status}`);
      return [];
    }
    const activities = await response.json();
    return activities;
  } catch (error) {
    console.error('Error fetching activities:', error);
    return [];
  }
}

// Initialize data on page load
async function fetchUsers() {
  try {
    const response = await fetch(`${API_BASE_URL}/auth/users`);
    if (!response.ok) throw new Error(`Failed to fetch users: ${response.statusText}`);
    const users = await response.json();

    const internal = users.filter(u => u.role !== "external_recruiter");
    const external = users.filter(u => u.role === "external_recruiter");

    TEAM = internal.map(_apiUserToTeam);
    RECRUITERS = internal.map(u => ({
      id: u.id, name: u.full_name, initials: _initials(u.full_name), color: _userColor(u.id),
    }));
    TEAM_BY_ID = Object.fromEntries(TEAM.map(t => [t.id, t]));
    TEAM_BY_EMAIL = Object.fromEntries(TEAM.filter(t => t.email).map(t => [t.email, t]));

    const roleMap = { admin: "admin", nuance_team: "interviewer", external_recruiter: "external" };
    VIEWERS = internal.map(u => ({
      id: u.id, name: u.full_name, initials: _initials(u.full_name),
      color: _userColor(u.id), role: roleMap[u.role] || u.role, title: u.email,
    }));

    // Re-export so other screens see the update
    Object.assign(window, { TEAM, RECRUITERS, TEAM_BY_ID, TEAM_BY_EMAIL, VIEWERS });
    return users;
  } catch (error) {
    console.error('Error fetching users:', error);
    return [];
  }
}

let _usersRefreshInterval = null;
function _startUsersRefresh() {
  if (_usersRefreshInterval) return;
  _usersRefreshInterval = setInterval(fetchUsers, 10 * 60 * 1000);
}

async function initializeData() {
  console.log('Initializing data from API...');
  try {
    const resp = await fetch(`${API_BASE_URL}/bootstrap`);
    if (resp.ok) {
      const data = await resp.json();
      _processUsers(data.users);
      _processJobs(data.jobs);
      _processCandidates(data.candidates);
      console.log(`Bootstrap loaded: ${TEAM.length} users, ${JOBS.length} jobs, ${CANDIDATES.length} candidates`);
      _startUsersRefresh();
      return;
    }
  } catch (e) {
    console.warn('Bootstrap failed, falling back to sequential:', e);
  }
  await fetchUsers();
  _startUsersRefresh();
  await fetchJobs();
  await fetchCandidates();
  console.log(`Fallback loaded: ${TEAM.length} users, ${JOBS.length} jobs, ${CANDIDATES.length} candidates`);
}

function _processUsers(users) {
  const internal = users.filter(u => u.role !== "external_recruiter");
  TEAM = internal.map(_apiUserToTeam);
  RECRUITERS = internal.map(u => ({
    id: u.id, name: u.full_name, initials: _initials(u.full_name), color: _userColor(u.id),
  }));
  TEAM_BY_ID = Object.fromEntries(TEAM.map(t => [t.id, t]));
  TEAM_BY_EMAIL = Object.fromEntries(TEAM.filter(t => t.email).map(t => [t.email, t]));
  const roleMap = { admin: "admin", nuance_team: "interviewer", external_recruiter: "external" };
  VIEWERS = internal.map(u => ({
    id: u.id, name: u.full_name, initials: _initials(u.full_name),
    color: _userColor(u.id), role: roleMap[u.role] || u.role, title: u.email,
  }));
  Object.assign(window, { TEAM, RECRUITERS, TEAM_BY_ID, TEAM_BY_EMAIL, VIEWERS });
}

function _processJobs(jobs) {
  JOBS = jobs.map(job => ({
    id: job.id,
    title: job.title,
    dept: job.department || "N/A",
    loc: "Seattle",
    type: "Full-time",
    opened: job.created_at ? new Date(job.created_at).toLocaleDateString('en-US', { timeZone: 'America/Los_Angeles', month: 'short', day: 'numeric' }) : "",
    manager: "Fangchang M.",
    priority: job.priority || "P1",
    status: job.status,
    candidateCount: job.candidate_count || 0,
  }));
  JOBS_BY_ID = Object.fromEntries(JOBS.map(j => [j.id, j]));
}

function _processCandidates(candidates) {
  CANDIDATES = candidates.map((c, idx) => {
    const job = JOBS_BY_ID[c.job_id] || { id: c.job_id, title: "Unknown Job" };
    const stage = STAGE_MAP[c.stage] || 'sourced';
    const recruiter = (c.assigned_recruiter_id && TEAM_BY_ID[c.assigned_recruiter_id])
      ? TEAM_BY_ID[c.assigned_recruiter_id]
      : { id: null, name: "No owner", initials: "—", color: "#A8AEB0" };
    const source = SOURCES.find(s => s.id === c.source) || SOURCES[0];
    const nameParts = (c.name || "Unknown").split(' ');
    const first = nameParts[0] || "Unknown";
    const last = nameParts.slice(1).join(' ') || "";

    return {
      id: c.id, first, last, name: c.name,
      avatar: c.photo_url || null,
      stage, jobId: job.id, jobTitle: job.title,
      recruiter: recruiter.id, recruiterName: recruiter.name,
      recruiterInitials: recruiter.initials, recruiterColor: recruiter.color,
      recruiterAvatar: recruiter.avatar_url || null,
      source: source.id, sourceLabel: source.label,
      company: c.current_company || "N/A",
      title: c.current_role || "N/A",
      location: c.location || "N/A",
      fit: c.ai_fit_score || 0,
      days: daysInStage(c.entered_stage_at),
      lastActivity: c.entered_stage_at
        ? `Entered ${(STAGE_BY_ID[stage] || {}).label || stage} ${new Date(c.entered_stage_at.endsWith('Z') ? c.entered_stage_at : c.entered_stage_at + 'Z').toLocaleDateString("en-US", { timeZone: "America/Los_Angeles", month: "short", day: "numeric" })}`
        : "Imported from Greenhouse",
      salary: c.desired_compensation || "N/A",
      salaryLo: 0, salaryHi: 0,
      starred: false, flagged: false,
      referredBy: c.referrer || null,
      submittedById: null, submittedByName: c.submitted_by || null,
      submittedByAgency: null, panelMembers: [],
      phone: c.phone, email: c.email,
      created_at: c.created_at,
      companiesText: c.companies_text || "",
      educationText: c.education_text || "",
      _searchHay: [c.name, c.email, c.companies_text, c.education_text]
        .filter(Boolean).join(" ").toLowerCase(),
      duplicateIds: [],
    };
  });

  // Compute duplicate groups
  const emailGroups = {};
  for (const c of CANDIDATES) {
    if (!c.email || c.email.includes("placeholder")) continue;
    const key = c.email.toLowerCase();
    if (!emailGroups[key]) emailGroups[key] = [];
    emailGroups[key].push(c.id);
  }
  const nameGroups = {};
  for (const c of CANDIDATES) {
    const key = c.name.toLowerCase().trim();
    if (!key || key === "unknown") continue;
    if (!nameGroups[key]) nameGroups[key] = [];
    nameGroups[key].push(c.id);
  }
  for (const c of CANDIDATES) {
    const dupSet = new Set();
    const emailKey = c.email && !c.email.includes("placeholder") ? c.email.toLowerCase() : null;
    const nameKey = c.name.toLowerCase().trim();
    if (emailKey && emailGroups[emailKey]) {
      for (const id of emailGroups[emailKey]) if (id !== c.id) dupSet.add(id);
    }
    if (nameKey && nameKey !== "unknown" && nameGroups[nameKey]) {
      for (const id of nameGroups[nameKey]) if (id !== c.id) dupSet.add(id);
    }
    c.duplicateIds = Array.from(dupSet);
  }
}

// Helper functions
function candidatesByStage(stageId, jobId = null) {
  return CANDIDATES.filter(c => c.stage === stageId && (!jobId || c.jobId === jobId));
}

function candidatesByJob(jobId) {
  return CANDIDATES.filter(c => c.jobId === jobId);
}

function jobStageCount(jobId, stageId) {
  return CANDIDATES.filter(c => c.jobId === jobId && c.stage === stageId).length;
}

// Access control - filter candidates based on viewer role
function visibleCandidates(viewer, list = CANDIDATES) {
  if (!viewer) return list;

  switch (viewer.role) {
    case "admin":
      return list;

    case "interviewer":
      return list;

    case "external":
      return list.filter(c => c.submittedById === viewer.id || c.source === viewer.sourceTag);

    default:
      return [];
  }
}

// Field visibility based on viewer role
function canSeeField(viewer, field) {
  if (!viewer) return true;
  if (viewer.role === "admin") return true;
  if (viewer.role === "interviewer") {
    return field !== "emails" && field !== "slack" && field !== "other_notes";
  }
  return false;
}

async function fetchIntegrationStatus() {
  try {
    const r = await fetch(`${API_BASE_URL}/integrations/status`);
    if (!r.ok) return null;
    return await r.json();
  } catch { return null; }
}

async function updateJobStatus(jobId, status) {
  try {
    const resp = await fetch(`${API_BASE_URL}/jobs/${jobId}/status`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("ats_token")}` },
      body: JSON.stringify({ status }),
    });
    if (!resp.ok) return null;
    const result = await resp.json();
    const job = JOBS.find(j => j.id === jobId);
    if (job) job.status = status;
    window.dispatchEvent(new CustomEvent("jobs-updated"));
    return result;
  } catch { return null; }
}

function notifyCandidatesChanged() {
  window.dispatchEvent(new CustomEvent("candidates-updated"));
}

// Export to global scope for screen components
Object.assign(window, {
  STAGES, STAGE_BY_ID, JOBS, CANDIDATES, RECRUITERS, SOURCES,
  STAGE_FLOW, TEAM, TEAM_BY_ID, TEAM_BY_EMAIL, EXT_AGENCIES, EXT_RECRUITERS, ROLES, VIEWERS,
  candidatesByStage, candidatesByJob, jobStageCount,
  nextStage, suggestedForStage, visibleCandidates, canSeeField,
  initializeData, fetchUsers, fetchCandidateDetail, fetchCandidateInterviews, fetchCandidateEmails, fetchCandidateActivities,
  updateCandidateStage, updateCandidateSource, updateCandidateProfile, updateJobStatus, notifyCandidatesChanged,
  fetchIntegrationStatus,
});
