﻿// Central sync store — cloud-hybrid: localStorage cache + cloudSave/cloudLoad (Make DataStore)
(function(){
const STORAGE_KEY = 'ybp_sync_store';
const CLOUD_KEY   = 'sync_store';      // key used in cloudSave / cloudLoad

// Resolve a project's display name from its ID.
// Looks first in window.YBP_DATA.projects (in-memory), then in localStorage 'ybp_user_projects'
// (persistent user-created projects). Returns empty string if not found — never returns the projectId itself.
function resolveProjectName(projectId) {
  if (!projectId) return '';
  try {
    const fromData = (window.YBP_DATA?.projects || []).find(p => p.id === projectId);
    if (fromData?.name) return fromData.name;
  } catch (_) {}
  try {
    const userProjects = JSON.parse(localStorage.getItem('ybp_user_projects') || '[]');
    const fromLocal = (userProjects || []).find(p => p.id === projectId);
    if (fromLocal?.name) return fromLocal.name;
  } catch (_) {}
  return '';
}

// IDs of old demo projects — used to purge stale demo data from localStorage
const DEMO_PROJECT_IDS = new Set(['p1','p2','p3','p4','p5','p6']);
const DEMO_CUSTOMER_IDS = new Set(['cust_demo1','cust_demo2','cust_demo3']);
const DEMO_VENDOR_IDS = new Set(['vnd_d1','vnd_d2','vnd_d3','vnd_d4','vnd_d5']);
const DEMO_ITEM_IDS = new Set(['tnd_demo1','tnd_demo2','prot_demo1','prot_demo2','REJ-2026-001','task-REJ-2026-001-rj1','task-REJ-2026-001-rj2','task-REJ-2026-001-rj3','task-REJ-2026-001-rj4']);

const defaultStore = {
  tasks: [],
  reports: [],
  findings: [],
  activity: [],
  meetings: [],
  reportCounters: {},
  directRejects: [],
  customers: [],
  vendors: [],
  tenders: [],
  protocols: [],
};

function purgeDemo(stored) {
  // Remove any stale demo data from localStorage (one-time migration)
  let changed = false;
  const filterDemo = (arr, fn) => { if (!Array.isArray(arr)) return []; const f = arr.filter(fn); if (f.length !== arr.length) changed = true; return f; };
  stored.tasks      = filterDemo(stored.tasks,     t => !DEMO_PROJECT_IDS.has(t.projectId) && !DEMO_ITEM_IDS.has(t.id));
  stored.reports    = filterDemo(stored.reports,   r => !DEMO_PROJECT_IDS.has(r.projectId) && !DEMO_ITEM_IDS.has(r.id));
  stored.tenders    = filterDemo(stored.tenders,   t => !DEMO_PROJECT_IDS.has(t.projectId) && !DEMO_ITEM_IDS.has(t.id));
  stored.protocols  = filterDemo(stored.protocols, p => !DEMO_PROJECT_IDS.has(p.projectId) && !DEMO_ITEM_IDS.has(p.id));
  stored.customers  = filterDemo(stored.customers, c => !DEMO_CUSTOMER_IDS.has(c.id));
  stored.vendors    = filterDemo(stored.vendors,   v => !DEMO_VENDOR_IDS.has(v.id));
  stored.directRejects = filterDemo(stored.directRejects, d => !DEMO_PROJECT_IDS.has(d.projectId));
  if (changed) localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
  return stored;
}

function load() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw) {
      const stored = purgeDemo(JSON.parse(raw));
      return { ...defaultStore, ...stored };
    }
  } catch(e){}
  return { ...defaultStore };
}

// Initial cloud load — runs once on startup, merges remote into local
function initCloudLoad() {
  if (typeof window.cloudLoad !== 'function') return;
  window.cloudLoad(CLOUD_KEY, null).then(remote => {
    if (remote) mergeIfNewer(remote);
    // Migration: if local has base64 photos, push clean version to cloud
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return;
      const local = JSON.parse(raw);
      const hasBase64 = (arr) => (arr||[]).some(r =>
        (r.photos||[]).some(p => typeof p === 'string' && p.startsWith('data:'))
      );
      if (hasBase64(local.directRejects) || (local.reports||[]).some(r => hasBase64(r.rejects||[]))) {
        const clean = stripBase64(local);
        localStorage.setItem(STORAGE_KEY, JSON.stringify(clean));
        window.cloudSave(CLOUD_KEY, clean).then(() => {
          window.dispatchEvent(new CustomEvent('ybp-sync-change', { detail: clean }));
          console.info('[SyncStore] Migration: base64 photos stripped, cloud updated');
        });
      }
    } catch(e) {}
  }).catch(e => console.warn('[SyncStore] initCloudLoad error:', e));
}

// Cross-device polling — every 5 minutes when tab visible
function startCloudPolling() {
  if (window.__ybpSyncPolling) return;
  window.__ybpSyncPolling = true;
  const pollFn = () => {
    if (typeof window.cloudLoad !== 'function') return;
    window.cloudLoad(CLOUD_KEY, null)
      .then(remote => { if (remote) mergeIfNewer(remote); })
      .catch(() => {});
  };
  if (typeof window.createSmartPoll === 'function') {
    window.createSmartPoll(pollFn, 5 * 60 * 1000);
  } else {
    setInterval(() => { if (document.visibilityState === 'visible') pollFn(); }, 5 * 60 * 1000);
  }
}
// Strip base64 images before any persistence (cloud or localStorage)
// Photos stored as Drive URLs (https://...) are kept; only data: URIs are removed.
function stripBase64(state) {
  // Keep Drive URLs always. Keep up to 5 base64 per item as local fallback.
  const MAX_BASE64 = 5;
  function smartFilter(photos) {
    if (!Array.isArray(photos)) return photos;
    const urls = photos.filter(p => p && !p.startsWith('data:'));
    const base64s = photos.filter(p => p && p.startsWith('data:')).slice(0, MAX_BASE64);
    return [...urls, ...base64s];
  }
  function stripPhoto(val) {
    return (val && !val.startsWith('data:')) ? val : undefined;
  }
  function stripTempFields(obj) {
    if (!obj || typeof obj !== 'object') return obj;
    const out = {};
    for (const k of Object.keys(obj)) { if (!k.startsWith('_')) out[k] = obj[k]; }
    return out;
  }
  return {
    ...state,
    directRejects: (state.directRejects || []).map(r => ({
      ...stripTempFields(r),
      photos: smartFilter(r.photos),
      photo:  stripPhoto(r.photo),
    })),
    findings: (state.findings || []).map(f => ({
      ...stripTempFields(f),
      photos: smartFilter(f.photos),
      photo:  stripPhoto(f.photo),
    })),
    reports: (state.reports || []).map(r => ({
      ...stripTempFields(r),
      rejects: (r.rejects || []).map(rj => ({
        ...stripTempFields(rj),
        photos: smartFilter(rj.photos),
        photo:  stripPhoto(rj.photo),
      })),
    })),
    tasks: (state.tasks || []).map(t => ({
      ...stripTempFields(t),
      photos: smartFilter(t.photos),
      photo:  stripPhoto(t.photo),
    })),
  };
}

async function uploadPhotosToDrive(photos, context) {
  if (!Array.isArray(photos) || !photos.length) return [];

  // ── ולידציה: שם מנהל ושם פרויקט חייבים להיות מוגדרים ──
  const pm = (context.pmName || '').trim();
  const proj = (context.projectName || '').trim();
  const isInvalidName = !pm || pm === 'מנהל' || pm === 'מנהל פרויקט';
  const isInvalidProj = !proj || proj === context.projectId;
  if (isInvalidName) {
    if (window.toastError) window.toastError('הגדר שם מנהל בפרופיל לפני העלאת תמונות');
    console.warn('[uploadPhotosToDrive] חסום — שם מנהל ריק/דיפולטי:', pm);
    return photos;
  }
  if (isInvalidProj) {
    if (window.toastError) window.toastError('שם הפרויקט חסר — לא ניתן להעלות');
    console.warn('[uploadPhotosToDrive] חסום — שם פרויקט ריק:', proj);
    return photos;
  }

  const photoWh = (function() {
    try {
      const s = JSON.parse(localStorage.getItem('ybp_settings') || '{}');
      return (s.webhooks && s.webhooks.PLACEHOLDER_UPLOAD_PHOTO) || 'https://hook.eu2.make.com/od77j53zjvkzdc5rsyfpjfpg6rjuz4e4';
    } catch (_) { return 'https://hook.eu2.make.com/od77j53zjvkzdc5rsyfpjfpg6rjuz4e4'; }
  })();
  const sk = '1h4M4SEXbhoYF1YVW1mS6YJZs84yxSVHaBjPDDxMpbak';
  // העלאה רציפה — תמונה אחת אחרי השנייה.
  // הראשונה יוצרת/מאתרת תיקייה; השאר משתמשות בה.
  const results = [];
  for (let photoIndex = 0; photoIndex < photos.length; photoIndex++) {
    const photo = photos[photoIndex];
    if (!photo || !photo.startsWith('data:')) {
      results.push(photo);
      continue;
    }
    try {
      const base64 = photo.replace(/^data:[^;]+;base64,/, '');
      const controller = new AbortController();
      const timeoutId = setTimeout(function() { controller.abort(); }, 15000);
      const resp = await fetch(photoWh, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          _sk: sk,
          manager: pm,
          project: proj,
          projectId: context.projectId,
          rejection_name: context.rejectionName || 'תמונה',  // שם תיקייה (folder name)
          reject_title: context.rejectTitle || context.rejectionName || 'תמונה',  // שם הקובץ (file prefix)
          date: context.date || new Date().toISOString().slice(0, 10),
          photo_index: photoIndex,
          image_base64: base64,
        }),
        signal: controller.signal,
      });
      clearTimeout(timeoutId);
      const json = await resp.json();
      results.push(json.file_url || json.fileUrl || json.url || json.drive_url || photo);
      // המתנה אחרי התמונה הראשונה — נותן ל-Drive זמן לאינדקס את התיקייה
      // 2500ms (v3.7.26): 1500ms לא הספיק, ראינו תיקיות כפולות (race condition)
      if (photoIndex === 0 && photos.length > 1) {
        await new Promise(r => setTimeout(r, 2500));
      }
    } catch (_) {
      results.push(photo);
    }
  }
  return results;
}

function save(state) {
  const clean = stripBase64(state);

  // 1. localStorage — cache mimeiat (sync)
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(clean));
  } catch(e) {
    if (e.name === 'QuotaExceededError' || e.code === 22 || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
      try {
        // trim old reports (keep last 30) as last resort
        const trimmed = { ...clean, reports: (clean.reports || []).slice(-30) };
        localStorage.setItem(STORAGE_KEY, JSON.stringify(trimmed));
        console.warn('[SyncStore] Quota — trimmed to last 30 reports');
      } catch(e2) {
        console.error('[SyncStore] localStorage full:', e2);
      }
    }
  }

  // 2. Cloud — fire-and-forget (non-blocking)
  if (typeof window.cloudSave === 'function') {
    window.cloudSave(CLOUD_KEY, clean).catch(e =>
      console.debug('[SyncStore] cloudSave skipped (Make off?):', e?.message)
    );
  }

  // Broadcast to all subscribers
  window.dispatchEvent(new CustomEvent('ybp-sync-change', { detail: state }));
}

// Merge remote state if it is newer than current local state (v57: per-id merge)
function mergeIfNewer(remote) {
  if (!remote || typeof remote !== 'object') return;
  try {
    const local = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null') || { ...defaultStore };
    const merged = { ...defaultStore, ...local };

    // מיזוג משימות + דוחות לפי id + updatedAt (newer wins) — לא דורסים מקומי חדש
    const mergeById = (localArr, remoteArr, tsField) => {
      const map = new Map((localArr || []).map(x => [x.id, x]));
      (remoteArr || []).forEach(r => {
        if (!r || !r.id) return;
        const ex = map.get(r.id);
        const rt = new Date(r[tsField] || r.updatedAt || 0).getTime();
        const lt = ex ? new Date(ex[tsField] || ex.updatedAt || 0).getTime() : 0;
        if (!ex || rt >= lt) map.set(r.id, r);
      });
      return Array.from(map.values());
    };
    merged.tasks   = mergeById(local.tasks,   remote.tasks,   'updatedAt');
    merged.reports = mergeById(local.reports, remote.reports, 'updatedAt');
    // אוספים אחרים — שמור התנהגות קודמת (קח remote אם קיים, אחרת local)
    ['findings','directRejects','pendingRejects','pendingApprovals','pendingSummaries','projects']
      .forEach(k => { if (remote[k] != null) merged[k] = remote[k]; });

    if (JSON.stringify(merged) !== JSON.stringify(local)) {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
      window.dispatchEvent(new CustomEvent('ybp-sync-change', { detail: merged }));
      console.info('[SyncStore] merged remote (per-id) — cross-device sync');
    }
  } catch(e) {
    console.warn('[SyncStore] mergeIfNewer error:', e);
  }
}

const SyncStore = {
  get() { return load(); },
  subscribe(fn) {
    const handler = (e) => fn(e.detail);
    window.addEventListener('ybp-sync-change', handler);
    return () => window.removeEventListener('ybp-sync-change', handler);
  },
  reset() {
    save({ ...defaultStore });
  },
  // Generate next report id for a project
  // kind: 'meeting' | 'report' | 'daily' | 'weekly' | 'rejects'
  nextReportId(projectId, kind) {
    const state = load();
    const year = new Date().getFullYear();
    const key = `${projectId}-${kind}-${year}`;
    const counter = (state.reportCounters[key] || 0) + 1;
    state.reportCounters[key] = counter;
    save(state);
    const prefixMap = {
      meeting: 'M',
      report: 'R',
      daily: 'D',
      weekly: 'W',
      rejects: 'RJ',
      infra: 'INF',
    };
    const prefix = prefixMap[kind] || 'R';
    return `${prefix}-${year}-${String(counter).padStart(3, '0')}`;
  },
  // Preview next report id without incrementing
  peekNextReportId(projectId, kind) {
    const state = load();
    const year = new Date().getFullYear();
    const key = `${projectId}-${kind}-${year}`;
    const counter = (state.reportCounters[key] || 0) + 1;
    const prefixMap = { meeting:'M', report:'R', daily:'D', weekly:'W', rejects:'RJ', infra:'INF' };
    const prefix = prefixMap[kind] || 'R';
    return `${prefix}-${year}-${String(counter).padStart(3, '0')}`;
  },
  // Submit a daily/weekly/rejects report — rejects become tasks
  async submitSiteReport(report /* {id, projectId, kind, title, date, author, content, rejects:[...]} */) {
    // Upload any base64 photos to Drive before saving
    const _rawName = ((window.YBP_AUTH && window.YBP_AUTH.getCurrentUser) ? window.YBP_AUTH.getCurrentUser().name : '') || 'מנהל';
    const pmName = _rawName.split(' ')[0] || _rawName;
    const projectName = resolveProjectName(report.projectId);
    const hasBase64 = (report.rejects || []).some(r => (r.photos || []).some(p => p && p.startsWith('data:')));
    if (hasBase64 && window.toastInfo) window.toastInfo('מעלה תמונות ל-Drive...');
    // ⭐ תיקייה אחת לדוח (במקום אחת לכל ריג'קט)
    const reportFolderName = report.kind === 'rejects'
      ? 'דוח ריגקטים'
      : (report.kind === 'daily' ? 'דוח יומי' : 'דוח');

    // v3.7.26: סדרתי במקום מקבילי — ריג'קטים בו-זמנית יצרו תיקיות כפולות בDrive
    // (race condition: שניהם יוצרים תיקייה לפני שאחד מהם מאונדקס)
    const cleanRejects = [];
    for (const r of (report.rejects || [])) {
      const cleanPhotos = await uploadPhotosToDrive(r.photos || [], {
        projectId: report.projectId,
        projectName,
        pmName,
        rejectionName: reportFolderName,   // ← אותה תיקייה לכל הריג'קטים
        date: report.date,
        rejectTitle: r.title || 'תמונה',  // ← לשם הקובץ
      });
      cleanRejects.push(Object.assign({}, r, { photos: cleanPhotos }));
    }
    if (hasBase64 && window.toastSuccess) window.toastSuccess('תמונות הועלו ✓');
    report = Object.assign({}, report, { rejects: cleanRejects });
    const state = load();
    // Upsert — עדכן אם קיים, הוסף אם חדש
    const existingIdx = state.reports.findIndex(r => r.id === report.id);
    const saved = { ...report, status: 'sent', updatedAt: new Date().toISOString() };
    if (existingIdx >= 0) {
      state.reports[existingIdx] = { ...state.reports[existingIdx], ...saved };
      state.tasks = state.tasks.filter(t => t.sourceReportId !== report.id);
      state.findings = (state.findings || []).filter(f => f.reportId !== report.id);
    } else {
      state.reports.push({ ...saved, createdAt: new Date().toISOString() });
    }
    (report.rejects || []).forEach((r, i) => {
      const taskId = `task-${report.id}-rj${i+1}`;
      const findingId = `find-${report.id}-rj${i+1}`;
      state.findings.push({
        id: findingId,
        reportId: report.id,
        projectId: report.projectId,
        ...r,
        taskId,
      });
      state.tasks.push({
        id: taskId,
        findingId,
        projectId: report.projectId,
        sourceReportId: report.id,
        sourceType: report.kind,
        title: r.title,
        description: r.description || '',
        trades: r.trades || [],
        assignee: r.assignee || 'לא שויך',
        severity: r.severity || 'medium',
        due: r.due || window.defaultDueDate(),
        originalDue: r.due,
        status: 'open',
        photo: r.photo || (r.photos && r.photos[0]) || null,
        photos: r.photos || (r.photo ? [r.photo] : []),
        comments: [],
        createdAt: new Date().toISOString(),
        source: 'reject',
      });
    });
    save(state);
    return report;
  },
  // Create a new report + auto-create tasks from its findings
  submitReport(report /* {id, projectId, type, title, date, findings:[{title, location, assignee, severity, photo, due}]} */) {
    const state = load();
    state.reports.push({
      ...report,
      status: 'sent',
      createdAt: new Date().toISOString(),
    });
    // Each finding becomes a task
    report.findings.forEach((f, i) => {
      const taskId = `task-${report.id}-${i+1}`;
      const findingId = `find-${report.id}-${i+1}`;
      state.findings.push({
        id: findingId,
        reportId: report.id,
        projectId: report.projectId,
        ...f,
        taskId,
      });
      state.tasks.push({
        id: taskId,
        findingId,
        projectId: report.projectId,
        sourceReportId: report.id,
        sourceType: report.type,
        title: f.title,
        description: f.description || '',
        location: f.location || '',
        assignee: f.assignee || 'לא שויך',
        severity: f.severity || 'medium',
        due: f.due,
        originalDue: f.due,
        status: 'open', // open | in-progress | done
        photo: f.photo || null,
        comments: [],
        createdAt: new Date().toISOString(),
        source: 'finding',
      });
    });
    save(state);
    return report;
  },
  // Called when client/contractor interacts with the shared doc
  clientAction(taskId, action /* {type: 'complete'|'postpone'|'comment', ...} */) {
    const state = load();
    const task = state.tasks.find(t => t.id === taskId);
    if (!task) return;
    const actor = action.actor || 'נציג לקוח';
    if (action.type === 'complete') {
      task.status = 'done';
      task.completedAt = new Date().toISOString();
      task.completedBy = actor;
    }
    if (action.type === 'postpone') {
      task.due = action.newDue;
      task.postponed = true;
    }
    if (action.type === 'comment') {
      task.comments.push({ author: actor, text: action.text, at: new Date().toISOString() });
    }
    state.activity.push({
      id: `act-${Date.now()}`,
      taskId,
      reportId: task.sourceReportId,
      actor,
      type: action.type,
      details: action,
      at: new Date().toISOString(),
    });
    save(state);
  },
  // Schedule a meeting
  scheduleMeeting(meeting) {
    const state = load();
    state.meetings.push({
      ...meeting,
      id: meeting.id || `mtg-${Date.now()}`,
      createdAt: new Date().toISOString(),
    });
    save(state);
  },
  // Update a task — stamps updatedAt (v57)
  updateTask(taskId, changes) {
    const state = load();
    const idx = state.tasks.findIndex(t => t.id === taskId);
    if (idx >= 0) {
      state.tasks[idx] = { ...state.tasks[idx], ...changes, updatedAt: new Date().toISOString() };
      save(state);
    }
  },
  // Delete a task — soft delete (tombstone), so deletion propagates cross-device (v57)
  deleteTask(taskId) {
    const state = load();
    const idx = state.tasks.findIndex(t => t.id === taskId);
    if (idx >= 0) {
      state.tasks[idx] = { ...state.tasks[idx], status: 'archived', updatedAt: new Date().toISOString() };
      save(state);
    }
  },
  // Add comment to task
  addComment(taskId, comment) {
    const state = load();
    const task = state.tasks.find(t => t.id === taskId);
    if (task) {
      task.comments = [...(task.comments || []), comment];
      task.updatedAt = new Date().toISOString();
      save(state);
    }
  },
  // Add a manual task (PM task) — accepts optional data (startDate/percentDone/stage/deps) (v57)
  addTask(task) {
    const state = load();
    const id = task.id || `task-pm-${Date.now()}`;
    const now = new Date().toISOString();
    state.tasks.push({
      id,
      projectId: task.projectId,
      title: task.title,
      description: task.description || '',
      assignee: task.assignee || '',
      due: task.due || window.defaultDueDate(),
      status: task.status || 'open',
      severity: task.severity || 'medium',
      source: task.source || 'pm',
      trades: task.trades || [],
      comments: [],
      ...(task.startDate   != null ? { startDate:   task.startDate   } : {}),
      ...(task.percentDone != null ? { percentDone: task.percentDone } : {}),
      ...(task.stage       != null ? { stage:       task.stage       } : {}),
      ...(task.data        != null ? { data:        task.data        } : {}),
      createdAt: now,
      updatedAt: now,
    });
    save(state);
    return id;
  },
  async addDirectReject(projectId, reject) {
    const projectName = (reject.projectName || '').trim()
      || resolveProjectName(projectId)
      || '';
    if (!projectName && window.toastWarning) {
      window.toastWarning('שם פרויקט חסר — נשמר מקומית, תמונות לא יעלו ל-Drive');
    }
    const _rawName = ((window.YBP_AUTH && window.YBP_AUTH.getCurrentUser) ? window.YBP_AUTH.getCurrentUser().name : '') || 'מנהל';
    const pmName = _rawName.split(' ')[0] || _rawName;
    const hasBase64 = (reject.photos || []).some(p => p && p.startsWith('data:'));
    let cleanPhotos = reject.photos || [];
    if (projectName && hasBase64) {
      if (window.toastInfo) window.toastInfo('מעלה תמונות ל-Drive...');
      try {
        cleanPhotos = await uploadPhotosToDrive(reject.photos, {
          projectId, projectName, pmName,
          rejectionName: 'דוח ריגקטים',  // ← תיקייה משותפת ליום
          date: new Date().toISOString().slice(0, 10),
          rejectTitle: reject.title || 'תמונה',
        });
        if (window.toastSuccess) window.toastSuccess('תמונות הועלו ✓');
      } catch(e) {
        console.warn('[addDirectReject] photo upload failed, keeping base64:', e);
        cleanPhotos = reject.photos;
      }
    }
    const rejectClean = { ...reject, photos: cleanPhotos };

    const state = load();
    if (!state.directRejects) state.directRejects = [];
    const id = 'drj-' + Date.now();
    const taskId = 'task-' + id;
    state.directRejects.push({
      id,
      projectId,
      createdAt: new Date().toISOString().slice(0,10),
      ...rejectClean,
      taskId,
    });
    // גם יוצר task כדי שיופיע במסך המשימות ובלוח קבלן
    state.tasks.push({
      id: taskId,
      projectId,
      title: rejectClean.title,
      description: rejectClean.description || '',
      assignee: rejectClean.assignee || '',
      due: rejectClean.due || window.defaultDueDate(),
      status: 'open',
      severity: rejectClean.severity || 'medium',
      source: 'reject',
      trades: rejectClean.trades || [],
      photos: cleanPhotos,
      comments: [],
      createdAt: new Date().toISOString(),
      directRejectId: id,
    });
    save(state);
    return id;
  },
  deleteDirectReject(id) {
    const state = load();
    const reject = (state.directRejects || []).find(r => r.id === id);
    state.directRejects = (state.directRejects || []).filter(r => r.id !== id);
    if (reject?.taskId) {
      state.tasks = (state.tasks || []).filter(t => t.id !== reject.taskId);
    }
    save(state);
  },
  async updateDirectReject(id, fields) {
    let cleanFields = fields;
    const state0 = load();
    const dr = (state0.directRejects || []).find(r => r.id === id) || {};
    const projectName = (fields.projectName || dr.projectName || '').trim()
      || resolveProjectName(dr.projectId)
      || '';
    if (Array.isArray(fields.photos) && fields.photos.some(p => p && p.startsWith('data:'))) {
      if (projectName) {
        const _rawName = ((window.YBP_AUTH && window.YBP_AUTH.getCurrentUser) ? window.YBP_AUTH.getCurrentUser().name : '') || 'מנהל';
        const pmName = _rawName.split(' ')[0] || _rawName;
        try {
          if (window.toastInfo) window.toastInfo('מעלה תמונות ל-Drive...');
          const cleanPhotos = await uploadPhotosToDrive(fields.photos, {
            projectId: dr.projectId,
            projectName,
            pmName,
            rejectionName: 'דוח ריגקטים',
            date: dr.createdAt || new Date().toISOString().slice(0, 10),
            rejectTitle: fields.title || dr.title || 'תמונה',
          });
          cleanFields = { ...fields, photos: cleanPhotos };
          if (window.toastSuccess) window.toastSuccess('תמונות הועלו ✓');
        } catch(e) {
          console.warn('[updateDirectReject] photo upload failed, keeping base64:', e);
        }
      } else {
        console.warn('[updateDirectReject] no projectName, keeping base64 locally');
      }
    }
    const state = load();
    const idx = (state.directRejects || []).findIndex(r => r.id === id);
    if (idx !== -1) {
      state.directRejects[idx] = { ...state.directRejects[idx], ...cleanFields };
      // sync linked task
      const taskId = state.directRejects[idx].taskId;
      if (taskId) {
        const ti = (state.tasks || []).findIndex(t => t.id === taskId);
        if (ti !== -1) {
          state.tasks[ti] = { ...state.tasks[ti], title: cleanFields.title || state.tasks[ti].title, description: cleanFields.description ?? state.tasks[ti].description, assignee: cleanFields.assignee ?? state.tasks[ti].assignee, due: cleanFields.due || state.tasks[ti].due, severity: cleanFields.severity || state.tasks[ti].severity, trades: cleanFields.trades || state.tasks[ti].trades, photos: cleanFields.photos || state.tasks[ti].photos };
        }
      }
      save(state);
    }
  },

  // ── CRM: Customers ──────────────────────────────────────────────────────────
  getCustomers() {
    return load().customers || [];
  },
  saveCustomers(list) {
    const state = load();
    state.customers = list;
    save(state);
  },
  addCustomer(customer) {
    const state = load();
    if (!state.customers) state.customers = [];
    const id = customer.id || ('cust_' + Date.now().toString(36));
    const newC = { ...customer, id, createdAt: customer.createdAt || new Date().toLocaleDateString('he-IL'), projectIds: customer.projectIds || [] };
    state.customers.push(newC);
    save(state);
    return id;
  },
  updateCustomer(id, changes) {
    const state = load();
    const idx = (state.customers || []).findIndex(c => c.id === id);
    if (idx >= 0) { state.customers[idx] = { ...state.customers[idx], ...changes }; save(state); }
  },
  deleteCustomer(id) {
    const state = load();
    state.customers = (state.customers || []).filter(c => c.id !== id);
    save(state);
  },

  // ── CRM: Vendors ────────────────────────────────────────────────────────────
  getVendors() {
    return load().vendors || [];
  },
  saveVendors(list) {
    const state = load();
    state.vendors = list;
    save(state);
  },
  addVendor(vendor) {
    const state = load();
    if (!state.vendors) state.vendors = [];
    const id = vendor.id || ('vnd_' + Date.now().toString(36));
    const newV = { ...vendor, id, createdAt: vendor.createdAt || new Date().toLocaleDateString('he-IL'), projectIds: vendor.projectIds || [] };
    state.vendors.push(newV);
    save(state);
    return id;
  },
  updateVendor(id, changes) {
    const state = load();
    const idx = (state.vendors || []).findIndex(v => v.id === id);
    if (idx >= 0) { state.vendors[idx] = { ...state.vendors[idx], ...changes }; save(state); }
  },
  deleteVendor(id) {
    const state = load();
    state.vendors = (state.vendors || []).filter(v => v.id !== id);
    save(state);
  },

  // ── CRM: Tenders ────────────────────────────────────────────────────────────
  getTenders() {
    return load().tenders || [];
  },
  saveTenders(list) {
    const state = load();
    state.tenders = list;
    save(state);
  },
  addTender(tender) {
    const state = load();
    if (!state.tenders) state.tenders = [];
    const id = tender.id || ('tnd_' + Date.now().toString(36));
    const newT = { ...tender, id, createdAt: new Date().toLocaleDateString('he-IL'), bids: tender.bids || [] };
    state.tenders.push(newT);
    save(state);
    return id;
  },
  updateTender(id, changes) {
    const state = load();
    const idx = (state.tenders || []).findIndex(t => t.id === id);
    if (idx >= 0) { state.tenders[idx] = { ...state.tenders[idx], ...changes }; save(state); }
  },
  deleteTender(id) {
    const state = load();
    state.tenders = (state.tenders || []).filter(t => t.id !== id);
    save(state);
  },

  // Update a specific field on an existing report (e.g. driveUrl after PDF upload)
  updateReport(reportId, changes) {
    const state = load();
    const idx = (state.reports || []).findIndex(r => r.id === reportId);
    if (idx >= 0) {
      state.reports[idx] = { ...state.reports[idx], ...changes };
      save(state);
    }
  },

  // ── Protocols ───────────────────────────────────────────────────────────────
  getProtocols() {
    return load().protocols || [];
  },
  saveProtocols(list) {
    const state = load();
    state.protocols = list;
    save(state);
  },
  addProtocol(protocol) {
    const state = load();
    if (!state.protocols) state.protocols = [];
    const id = protocol.id || ('prot_' + Date.now().toString(36));
    state.protocols.push({ ...protocol, id });
    save(state);
    return id;
  },
  updateProtocol(id, changes) {
    const state = load();
    const idx = (state.protocols || []).findIndex(p => p.id === id);
    if (idx >= 0) { state.protocols[idx] = { ...state.protocols[idx], ...changes }; save(state); }
  },
  deleteProtocol(id) {
    const state = load();
    state.protocols = (state.protocols || []).filter(p => p.id !== id);
    save(state);
  },
};

window.SyncStore = SyncStore;

// Boot cloud sync after DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => { initCloudLoad(); startCloudPolling(); });
} else {
  setTimeout(() => { initCloudLoad(); startCloudPolling(); }, 0);
}

// Helper: add 7 days to today
window.defaultDueDate = function() {
  const d = new Date();
  d.setDate(d.getDate() + 7);
  return d.toISOString().slice(0, 10);
};

// Helper: build Google Calendar URL
window.buildGCalUrl = function({ title, date, time, duration, location, description, attendees }) {
  // date: YYYY-MM-DD, time: HH:MM
  const [h, m] = time.split(':');
  const start = new Date(`${date}T${time}:00`);
  const end = new Date(start.getTime() + (duration || 60) * 60000);
  const fmt = (d) => d.toISOString().replace(/[-:]|\.\d{3}/g, '');
  const params = new URLSearchParams({
    action: 'TEMPLATE',
    text: title,
    dates: `${fmt(start)}/${fmt(end)}`,
    location: location || '',
    details: description || '',
  });
  if (attendees && attendees.length) params.append('add', attendees.join(','));
  return `https://calendar.google.com/calendar/render?${params.toString()}`;
};

// Helper: build Gmail compose URL
window.buildGmailUrl = function({ to, cc, subject, body }) {
  const params = new URLSearchParams();
  if (to && to.length) params.append('to', to.join(','));
  if (cc && cc.length) params.append('cc', cc.join(','));
  params.append('su', subject || '');
  params.append('body', body || '');
  return `https://mail.google.com/mail/?view=cm&fs=1&${params.toString()}`;
};

})();

