// ── Meal Planner ──────────────────────────────────────────────────────────────
// Week grid + library with drag-to-place (desktop), tap-to-place (all), per-edit save.

(function () {
  const { useState, useEffect, useRef, useMemo } = React;

  const MEAL_TYPES  = ['breakfast', 'lunch', 'dinner'];
  const MEAL_LABELS = { breakfast: 'Breakfast', lunch: 'Lunch', dinner: 'Dinner' };
  const DAY_FULL    = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];

  // ── Date helpers ──────────────────────────────────────────────────────────

  function getThisMonday() {
    const d = new Date();
    d.setHours(0, 0, 0, 0);
    const day = d.getDay();
    d.setDate(d.getDate() + (day === 0 ? -6 : 1 - day));
    return d;
  }

  function toWeekStr(d) {
    const y  = d.getFullYear();
    const m  = String(d.getMonth() + 1).padStart(2, '0');
    const dy = String(d.getDate()).padStart(2, '0');
    return `${y}-${m}-${dy}`;
  }

  function fromWeekStr(s) {
    const [y, m, d] = s.split('-').map(Number);
    return new Date(y, m - 1, d);
  }

  function addWeeks(weekStr, n) {
    const d = fromWeekStr(weekStr);
    d.setDate(d.getDate() + n * 7);
    return toWeekStr(d);
  }

  function formatWeekRange(weekStr) {
    const start = fromWeekStr(weekStr);
    const end   = new Date(start);
    end.setDate(end.getDate() + 6);
    const fmt = (x) => x.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
    return `${fmt(start)} – ${fmt(end)}`;
  }

  function formatDayDate(weekStr, dayIndex) {
    const d = fromWeekStr(weekStr);
    d.setDate(d.getDate() + dayIndex);
    return d.toLocaleDateString('en-US', { month: 'numeric', day: 'numeric' });
  }

  function isDayToday(weekStr, dayIndex) {
    const d     = fromWeekStr(weekStr);
    d.setDate(d.getDate() + dayIndex);
    const today = new Date();
    return d.getFullYear() === today.getFullYear() &&
           d.getMonth()    === today.getMonth()    &&
           d.getDate()     === today.getDate();
  }

  function countFilled(slots) {
    let n = 0;
    for (const day of Object.values(slots)) {
      for (const meal of MEAL_TYPES) { if (day[meal]) n++; }
    }
    return n;
  }

  // ── Week Nav ──────────────────────────────────────────────────────────────

  function WeekNav({ weekStr, onChange }) {
    const thisMonday = toWeekStr(getThisMonday());
    const minWeek    = addWeeks(thisMonday, -1);
    const maxWeek    = addWeeks(thisMonday,  8);
    const isMin      = weekStr <= minWeek;
    const isMax      = weekStr >= maxWeek;
    const isNow      = weekStr === thisMonday;

    return (
      <div className="pl-weeknav">
        <button
          className="pl-weeknav-btn"
          onClick={() => onChange(addWeeks(weekStr, -1))}
          disabled={isMin}
          title="Previous week"
        >
          <IconChevLeft size={18} />
        </button>
        <div className="pl-weeknav-center">
          <div className="pl-weeknav-range">{formatWeekRange(weekStr)}</div>
          {!isNow && (
            <button className="pl-weeknav-today" onClick={() => onChange(thisMonday)}>
              Back to this week
            </button>
          )}
        </div>
        <button
          className="pl-weeknav-btn"
          onClick={() => onChange(addWeeks(weekStr, 1))}
          disabled={isMax}
          title="Next week"
        >
          <IconChevRight size={18} />
        </button>
      </div>
    );
  }

  // ── Summary Bar ───────────────────────────────────────────────────────────

  function SummaryBar({ slots }) {
    const filled = countFilled(slots);
    const total  = 21;
    const pct    = Math.round((filled / total) * 100);

    let label;
    if (filled === 0)     label = 'No meals planned yet — drag a recipe from the library';
    else if (filled === total) label = 'Full week planned';
    else                  label = `${total - filled} meal${total - filled === 1 ? '' : 's'} left to plan`;

    return (
      <div className="pl-summary">
        <div className="pl-summary-stat">
          <span className="pl-summary-num">{filled}</span>
          <span className="pl-summary-denom">/{total}</span>
          <div className="pl-summary-label">meals planned</div>
        </div>
        <div className="pl-summary-mid">
          <div className="pl-summary-track">
            <div className="pl-summary-fill" style={{ width: `${pct}%` }} />
          </div>
          <div className="pl-summary-desc">{label}</div>
        </div>
      </div>
    );
  }

  // ── Library ───────────────────────────────────────────────────────────────

  function LibCard({ recipe, picked, onPick }) {
    return (
      <div
        className={`pl-lib-card${picked ? ' is-picked' : ''}`}
        title={picked ? 'Tap a slot to place it — or tap again to cancel' : recipe.name}
        draggable
        onDragStart={e => { e.dataTransfer.setData('text/plain', String(recipe.id)); e.dataTransfer.effectAllowed = 'copy'; }}
        onClick={() => onPick(recipe.id)}
      >
        <div
          className="pl-lib-thumb"
          style={recipe.image ? { backgroundImage: `url(${recipe.image})` } : {}}
        >
          {!recipe.image && <IconBook size={18} stroke={1.4} />}
          {recipe.favorite && (
            <span className="pl-lib-fav" aria-label="Favourite">
              <IconHeart size={9} filled />
            </span>
          )}
        </div>
        <div className="pl-lib-body">
          {recipe.category && <div className="pl-lib-cat">{recipe.category}</div>}
          <div className="pl-lib-name">{recipe.name}</div>
          {(recipe.prep || recipe.cook) && (
            <div className="pl-lib-meta">
              <IconClock size={11} stroke={1.6} />
              {[recipe.prep, recipe.cook].filter(Boolean).join(' + ')}
            </div>
          )}
        </div>
        <div className="pl-lib-grip" aria-hidden="true">
          {[0, 1, 2, 3, 4, 5].map(i => <span key={i} />)}
        </div>
      </div>
    );
  }

  function LibraryPanel({ recipes, picked, onPick }) {
    const [search, setSearch] = useState('');
    const inputRef = useRef(null);

    const filtered = useMemo(() => {
      if (!search.trim()) return recipes;
      const q = search.toLowerCase();
      return recipes.filter(r =>
        r.name.toLowerCase().includes(q) ||
        (r.category || '').toLowerCase().includes(q) ||
        (r.author || '').toLowerCase().includes(q)
      );
    }, [recipes, search]);

    return (
      <aside className="pl-library">
        <div className="pl-library-head">
          <h3 className="pl-library-title">Recipe Library</h3>
          <span className="pl-library-count">{filtered.length}</span>
        </div>

        <div className="pl-library-search">
          <IconSearch size={15} stroke={1.7} />
          <input
            ref={inputRef}
            type="text"
            placeholder="Search recipes…"
            value={search}
            onChange={e => setSearch(e.target.value)}
          />
          {search && (
            <button className="pl-lib-clear" onClick={() => { setSearch(''); inputRef.current?.focus(); }}>
              <IconX size={13} />
            </button>
          )}
        </div>

        <div className={`pl-library-hint${picked ? ' is-active' : ''}`}>
          <div className="pl-lib-dash-box" />
          {picked ? 'now tap a slot on the week' : 'drag a recipe to a slot — or tap it'}
        </div>

        <div className="pl-library-list">
          {filtered.length === 0
            ? <div className="pl-library-empty">No recipes match "{search}"</div>
            : filtered.map(r => <LibCard key={r.id} recipe={r} picked={picked === r.id} onPick={onPick} />)
          }
        </div>
      </aside>
    );
  }

  // ── Week grid ─────────────────────────────────────────────────────────────

  function SlotCell({ mealType, recipe, canPlace, onPlace, onClear }) {
    const [over, setOver] = useState(false);
    const dropProps = {
      onDragOver: e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; setOver(true); },
      onDragLeave: () => setOver(false),
      onDrop: e => {
        e.preventDefault(); setOver(false);
        const id = parseInt(e.dataTransfer.getData('text/plain'));
        if (Number.isFinite(id)) onPlace(id);
      },
    };

    if (!recipe) {
      return (
        <button
          className={`pl-slot-empty${over ? ' is-over' : ''}${canPlace ? ' can-place' : ''}`}
          onClick={canPlace ? () => onPlace(null) : undefined}
          {...dropProps}
        >
          <span className="pl-slot-plus">+</span>
          <span className="pl-slot-label">{MEAL_LABELS[mealType]}</span>
        </button>
      );
    }

    return (
      <div
        className={`pl-slot-card${recipe.image ? '' : ' pl-slot-card-plain'}${over ? ' is-over' : ''}`}
        style={recipe.image ? { backgroundImage: `url(${recipe.image})` } : {}}
        onClick={canPlace ? () => onPlace(null) : undefined}
        {...dropProps}
      >
        <button className="pl-slot-x" title="Remove" onClick={e => { e.stopPropagation(); onClear(); }}>
          <IconX size={11} />
        </button>
        <div className="pl-slot-card-body">
          <div className="pl-slot-card-meal">{MEAL_LABELS[mealType]}</div>
          <div className="pl-slot-card-title">{recipe.name}</div>
          {(recipe.prep || recipe.cook) && (
            <div className="pl-slot-card-meta">
              <IconClock size={10} />
              {[recipe.prep, recipe.cook].filter(Boolean).join(' + ')}
            </div>
          )}
        </div>
      </div>
    );
  }

  function DayColumn({ dayIndex, weekStr, slots, recipesById, picked, onPlace, onClear }) {
    const daySlots = slots[String(dayIndex)] || {};
    const today    = isDayToday(weekStr, dayIndex);
    const weekend  = dayIndex >= 5;

    return (
      <div className={`pl-day${today ? ' is-today' : ''}${weekend ? ' is-weekend' : ''}`}>
        <div className="pl-day-head">
          <div>
            <div className="pl-day-name">{DAY_FULL[dayIndex]}</div>
            <div className="pl-day-date">{formatDayDate(weekStr, dayIndex)}</div>
          </div>
          {today && <span className="pl-today-badge">Today</span>}
        </div>
        <div className="pl-day-slots">
          {MEAL_TYPES.map(meal => (
            <div key={meal} className="pl-slot">
              <SlotCell
                mealType={meal}
                recipe={daySlots[meal] ? (recipesById[daySlots[meal]] || null) : null}
                canPlace={picked != null}
                onPlace={(dropId) => onPlace(dayIndex, meal, dropId)}
                onClear={() => onClear(dayIndex, meal)}
              />
            </div>
          ))}
        </div>
      </div>
    );
  }

  function WeekGrid({ weekStr, slots, recipesById, picked, onPlace, onClear }) {
    return (
      <div className="pl-week">
        {[0, 1, 2, 3, 4, 5, 6].map(i => (
          <DayColumn
            key={i}
            dayIndex={i}
            weekStr={weekStr}
            slots={slots}
            recipesById={recipesById}
            picked={picked}
            onPlace={onPlace}
            onClear={onClear}
          />
        ))}
      </div>
    );
  }

  // ── Planner View (root) ───────────────────────────────────────────────────

  function PlannerView({ recipes, getAuthHeaders, onBack, onAddWeekToList }) {
    const thisMonday = toWeekStr(getThisMonday());
    const [weekStr, setWeekStr] = useState(thisMonday);
    const [slots,   setSlots]   = useState({});
    const [loading, setLoading] = useState(true);
    const [picked,  setPicked]  = useState(null);
    const [saveErr, setSaveErr] = useState(false);

    async function saveSlots(next) {
      setSlots(next);
      setSaveErr(false);
      try {
        const headers = await getAuthHeaders();
        const res = await fetch(`/api/meal-plans/${weekStr}`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json', ...headers },
          body: JSON.stringify({ slots: next }),
        });
        if (!res.ok) throw new Error();
      } catch {
        setSaveErr(true);
      }
    }

    function place(dayIndex, meal, dropId) {
      const recipeId = dropId != null ? dropId : picked;
      if (recipeId == null) return;
      const day = String(dayIndex);
      const next = { ...slots, [day]: { ...(slots[day] || {}), [meal]: recipeId } };
      setPicked(null);
      saveSlots(next);
    }

    function clearSlot(dayIndex, meal) {
      const day = String(dayIndex);
      const daySlots = { ...(slots[day] || {}) };
      delete daySlots[meal];
      const next = { ...slots, [day]: daySlots };
      saveSlots(next);
    }

    const plannedRecipes = useMemo(() => {
      const ids = new Set();
      for (const day of Object.values(slots)) for (const id of Object.values(day || {})) if (id) ids.add(id);
      const m = {};
      for (const r of recipes) m[r.id] = r;
      return [...ids].map(id => m[id]).filter(Boolean);
    }, [slots, recipes]);

    const recipesById = useMemo(() => {
      const m = {};
      for (const r of recipes) m[r.id] = r;
      return m;
    }, [recipes]);

    useEffect(() => {
      let cancelled = false;
      (async () => {
        setLoading(true);
        try {
          const headers = await getAuthHeaders();
          const res     = await fetch(`/api/meal-plans/${weekStr}`, { headers });
          if (!res.ok) throw new Error(`HTTP ${res.status}`);
          const data = await res.json();
          if (!cancelled) setSlots(data.slots || {});
        } catch {
          if (!cancelled) setSlots({});
        } finally {
          if (!cancelled) setLoading(false);
        }
      })();
      return () => { cancelled = true; };
    }, [weekStr]);

    return (
      <div className="pl-view">
        <div className="pl-head">
          <div className="pl-head-left">
            <button className="pl-back-btn" onClick={onBack} title="Back to cookbook">
              <IconChevLeft size={16} /> Back
            </button>
            <div className="pl-eyebrow"><span className="pl-eyebrow-line" />Meal Planner</div>
            <h2 className="pl-title">Plan the <em>week.</em></h2>
          </div>
          <WeekNav weekStr={weekStr} onChange={w => { setWeekStr(w); }} />
        </div>

        <SummaryBar slots={slots} />

        {plannedRecipes.length > 0 && (
          <div className="pl-tools">
            <button className="ghost-btn pl-shop-btn" onClick={() => onAddWeekToList && onAddWeekToList(plannedRecipes)}>
              <IconBag size={16} />
              Add this week's ingredients to the list
            </button>
            {saveErr && <span className="pl-save-err">couldn't save — changes may not stick</span>}
          </div>
        )}

        {loading ? (
          <div className="pl-loading">Loading plan…</div>
        ) : (
          <div className="pl-body">
            <LibraryPanel recipes={recipes} picked={picked} onPick={(id) => setPicked(p => p === id ? null : id)} />
            <WeekGrid weekStr={weekStr} slots={slots} recipesById={recipesById} picked={picked} onPlace={place} onClear={clearSlot} />
          </div>
        )}
      </div>
    );
  }

  window.PlannerView = PlannerView;
})();
