// Data store — fetches live data from /api/* and provides it via React context
const { useState, useEffect, useCallback, createContext, useContext } = React;

const StoreCtx = createContext(null);

// ── Palette helpers ───────────────────────────────────────────────────────────
const RECIPE_TONES = ['mint', 'yellow', 'pink', 'orange', 'purple', 'blue'];
const CAT_COLORS   = ['#3cffd0', '#5200ff', '#ffd166', '#ef476f', '#06d6a0', '#118ab2', '#ff9f1c', '#e63946'];
const DOW_SHORT    = ['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom']; // ISO weekday 0=Lun

function toneForId(id) { return RECIPE_TONES[id % RECIPE_TONES.length]; }
function colorForIdx(i) { return CAT_COLORS[i % CAT_COLORS.length]; }

// ── Mapping helpers ───────────────────────────────────────────────────────────

function mapRecipes(raws) {
  return (raws || []).map(r => ({
    ...r,
    kinds:  r.meal_types || [],
    tone:   toneForId(r.id),
    public: r.is_public,
    raciones: 2,
  }));
}

function mapPlanningWeek(apiData, recipeList) {
  if (!apiData) return null;
  const todayISO = new Date().toISOString().slice(0, 10);
  const SLOT_IDS = ['desayuno', 'comida', 'merienda', 'cena'];

  const days = (apiData.days || []).map(d => {
    const dateObj = new Date(d.date + 'T12:00:00');
    const dow = DOW_SHORT[(dateObj.getDay() + 6) % 7];
    const dom = dateObj.getDate();
    return { date: d.date, dow, dom, today: d.date === todayISO };
  });

  const planning = (apiData.days || []).map(d => {
    return SLOT_IDS.map(sid => {
      const m = d.meals?.[sid];
      const recipes = (m?.recipes || []).map(r => ({
        id:          r.id,
        recipeId:    r.recipe_id,
        title:       r.recipe_name || '(sin nombre)',
        raciones:    r.raciones || 1,
        notConsumed: Boolean(r.not_consumed),
      }));
      const loose = (m?.loose || []).map(l => ({
        id: l.id, name: l.ingredient_name, qty: l.qty, unit: l.unit,
      }));
      if (recipes.length === 0 && loose.length === 0) return null;
      return {
        recipes,
        loose,
        looseCost: m?.loose_cost || 0,
        _date:   d.date,
        _slotId: sid,
      };
    });
  });

  return { days, planning, weekStart: apiData.week_start, weekLabel: apiData.label };
}

function mapExpenseSummary(summary, budgets, prevMonthTotal) {
  if (!summary) return null;

  const byCat = {};
  (summary.by_category || []).forEach(c => { byCat[c.name] = c; });

  const mappedBudgets = (budgets || []).map((b, i) => {
    const spent = byCat[b.name]?.total || 0;
    const limit = b.monthly_limit || 0;
    const pct   = limit > 0 ? Math.round((spent / limit) * 100) : 0;
    return {
      id:    b.id,
      cat:   b.name,
      icon:  b.icon || '📦',
      spent: round2(spent),
      limit: round2(limit),
      pct,
      over:  limit > 0 && spent > limit,
      color: colorForIdx(i),
    };
  });

  const totalMonth = summary.total || 0;
  const topCat = (summary.by_category || [])[0] || { name: '—', total: 0 };
  const budget  = mappedBudgets.reduce((s, b) => s + b.limit, 0);

  const vsLast = prevMonthTotal && prevMonthTotal > 0
    ? Math.round(((totalMonth - prevMonthTotal) / prevMonthTotal) * 100)
    : 0;

  const kpis = {
    totalMonth,
    budget,
    vsLast: (vsLast >= 0 ? '+' : '') + vsLast,
    topCategory:       topCat.name,
    topCategoryAmount: topCat.total,
    ticketAvg: 0,
    ticketsCount: 0,
  };

  const weekBars = (summary.evolution || []).map((e, i) => ({
    lbl: `S${i + 1}`,
    v:   e.total,
    c:   i % 2 === 0 ? 'col--mint' : 'col--ultra',
  }));

  const topPlaces = (summary.top_places || []).map(p => ({ name: p.name, v: p.total }));

  const byPayment      = summary.by_payment || {};
  const paymentEntries = Object.entries(byPayment).sort((a, b) => b[1] - a[1]);

  return { budgets: mappedBudgets, kpis, weekBars, topPlaces, byPayment, paymentEntries };
}

function round2(n) { return Math.round((n || 0) * 100) / 100; }

// ── Store provider ────────────────────────────────────────────────────────────

function StoreProvider({ children }) {
  const [recipes,       setRecipes]       = useState([]);
  const [categories,    setCategories]    = useState([]);
  const [places,        setPlaces]        = useState([]);
  const [paymentAccounts, setPaymentAccounts] = useState([]);
  const [budgets,       setBudgets]       = useState([]);
  const [planningWeek,  setPlanningWeek]  = useState(null);
  const [weekOffset,    setWeekOffset]    = useState(0); // 0 = current, +/-1 = next/prev
  const [expenseSummary, setExpenseSummary] = useState(null);
  const [expenses,      setExpenses]      = useState([]);
  const [articles,      setArticles]      = useState([]);
  const [notifications, setNotifications] = useState([]);
  const [ingredients,   setIngredients]   = useState([]);
  const [recipeCosts,   setRecipeCosts]   = useState({});
  const [loaded,        setLoaded]        = useState(false);

  // Compute monday of the displayed week
  function getWeekMonday(offset) {
    const today = new Date();
    const dow   = (today.getDay() + 6) % 7; // 0=Mon
    const monday = new Date(today);
    monday.setDate(today.getDate() - dow + offset * 7);
    return monday.toISOString().slice(0, 10);
  }

  const loadRecipes = useCallback(async () => {
    const r = await window.api('/api/recipes');
    setRecipes(mapRecipes(r));
  }, []);

  const loadPlanning = useCallback(async (offset) => {
    const monday = getWeekMonday(offset);
    const data = await window.api(`/api/planning?week=${monday}`);
    setPlanningWeek(mapPlanningWeek(data, []));
  }, []);

  const loadExpenses = useCallback(async () => {
    const now  = new Date();
    const prev = new Date(now.getFullYear(), now.getMonth() - 1, 1);
    const prevRef = `${prev.getFullYear()}-${String(prev.getMonth() + 1).padStart(2, '0')}-01`;
    const [summary, expList, bud, prevSummary] = await Promise.all([
      window.api(`/api/expenses/summary?period=month`),
      window.api('/api/expenses'),
      window.api('/api/expense-budgets'),
      window.api(`/api/expenses/summary?period=month&date=${prevRef}`),
    ]);
    setBudgets(bud || []);
    setExpenses(expList || []);
    setExpenseSummary(mapExpenseSummary(summary, bud || [], prevSummary?.total || 0));
  }, []);

  const loadArticles = useCallback(async () => {
    const r = await window.api('/api/articles');
    setArticles(r || []);
  }, []);

  const loadNotifications = useCallback(async () => {
    const r = await window.api('/api/notifications');
    setNotifications(r || []);
  }, []);

  const loadStatic = useCallback(async () => {
    const [cats, pl, pa] = await Promise.all([
      window.api('/api/expense-categories'),
      window.api('/api/expense-places'),
      window.api('/api/payment-accounts'),
    ]);
    setCategories(cats || []);
    setPlaces(pl || []);
    setPaymentAccounts(pa || []);
  }, []);

  const loadIngredients = useCallback(async () => {
    const r = await window.api('/api/ingredients');
    setIngredients(r || []);
  }, []);

  const loadRecipeCosts = useCallback(async () => {
    const r = await window.api('/api/recipes/costs');
    setRecipeCosts(r || {});
  }, []);

  const loadAll = useCallback(async () => {
    try {
      await Promise.all([loadRecipes(), loadPlanning(0), loadExpenses(), loadStatic(), loadArticles(), loadNotifications(), loadIngredients(), loadRecipeCosts()]);
      setLoaded(true);
    } catch (e) {
      window.showToast('Error cargando datos: ' + e.message, 'error');
    }
  }, [loadRecipes, loadPlanning, loadExpenses, loadStatic, loadArticles, loadNotifications, loadIngredients, loadRecipeCosts]);

  const changeWeek = useCallback(async (newOffset) => {
    setWeekOffset(newOffset);
    try { await loadPlanning(newOffset); }
    catch (e) { window.showToast('Error cargando semana: ' + e.message, 'error'); }
  }, [loadPlanning]);

  useEffect(() => { loadAll(); }, []);

  const ctx = {
    recipes, categories, places, budgets, paymentAccounts,
    planningWeek, weekOffset, changeWeek,
    expenseSummary, expenses,
    articles, notifications,
    ingredients, recipeCosts,
    loaded,
    reload: {
      recipes:       loadRecipes,
      planning:      () => loadPlanning(weekOffset),
      expenses:      loadExpenses,
      articles:      loadArticles,
      notifications: loadNotifications,
      ingredients:   loadIngredients,
      recipeCosts:   loadRecipeCosts,
      static:        loadStatic,
      all:           loadAll,
    },
  };

  return <StoreCtx.Provider value={ctx}>{children}</StoreCtx.Provider>;
}

function useStore() { return useContext(StoreCtx); }

window.RC_STORE = { StoreProvider, useStore };

// Deriva texto visible desde type+payload (BD usa "read" 0/1, no "read_at")
window.RC_notifText = function(n) {
  const p = n.payload || {};
  if (n.type === 'ticket_ready') {
    const parts = [];
    if (p.supermarket) parts.push(p.supermarket);
    if (p.total != null) parts.push(p.total + '€');
    if (p.date) parts.push(p.date);
    const dup = p.duplicate_ticket_id ? ' · Posible duplicado' : '';
    return {
      title: 'Ticket listo para verificar',
      body: parts.join(' · ') + dup,
      queueItemId: p.queue_item_id || null,
    };
  }
  if (n.type === 'ticket_error') {
    const body = (p.reason === 'quota' || p.reason === 'transient')
      ? 'Procesamiento en pausa, reintentaremos pronto'
      : (p.message || 'Error procesando el ticket');
    return { title: 'Error al procesar ticket', body, queueItemId: null };
  }
  return { title: n.type || 'Notificación', body: '', queueItemId: null };
};

// Aplana ticket_data anidado (forma real del worker) a campos editables planos.
window.RC_ticketFlat = function (td) {
  td = td || {};
  return {
    supermarket: td.supermarket?.name || '',
    date: td.ticket_info?.date || '',
    total: td.totals?.total ?? null,
    items: (td.items || []).map(it => ({
      product_name: it.product_name || '',
      quantity:     it.quantity ?? 1,
      unit:         it.unit || 'ud',
      unit_price:   it.unit_price ?? null,
      total_price:  it.total_price ?? null,
    })),
  };
};

// Reconstruye la forma anidada que exige TicketData.model_validate.
// rawTd = ticket_data original del servidor; flat = estado editado (salida de RC_ticketFlat).
window.RC_ticketBuild = function (rawTd, flat) {
  rawTd = rawTd || {};
  const items = (flat.items || []).map(it => {
    const qty = Number(it.quantity) || 0;
    const up  = (it.unit_price === '' || it.unit_price == null) ? null : Number(it.unit_price);
    const tp  = (it.total_price === '' || it.total_price == null)
      ? +((up || 0) * qty).toFixed(2)
      : Number(it.total_price);
    return {
      product_name: it.product_name || '',
      quantity:     qty,
      unit:         it.unit || 'ud',
      unit_price:   up,
      total_price:  tp,
    };
  });
  return {
    ...rawTd,
    supermarket: { ...(rawTd.supermarket || {}), name: flat.supermarket || '' },
    ticket_info: { ...(rawTd.ticket_info || {}), date: flat.date || null },
    items,
    totals: { ...(rawTd.totals || {}), total: (flat.total === '' || flat.total == null) ? null : Number(flat.total) },
  };
};
