// ------------------------------------------------------------------------
//  useTableScroll - owns the horizontal scroll behaviour of the hourly
//  table (sticky header + body scroller layout).
//
//  Responsibilities:
//    1. SCROLL INDICATORS - track whether the body can scroll left/right so
//       the table edges can show fade + chevron indicators.
//    2. DRAG-TO-SCROLL - pointer-event drag on the body scroller for
//       desktop users.
//    3. SCROLL SYNC - keep the sticky header track shifted horizontally to
//       match the body's scrollLeft, and keep header cell widths in lock-
//       step with body cell widths even as columns toggle / window resizes.
//
//  Inputs (passed by app.js):
//    refs: { headTableRef, bodyTableRef, bodyScrollRef, headTrackRef }
//    deps: { forecast, visibleCols, selectedDay, skinType, vehicleType,
//            indoorMode, indoorManaged }
//      - anything that should cause a re-sync when it changes.
//
//  Outputs:
//    tableCanScrollLeft, tableCanScrollRight  - drive the fade/chevron CSS
//    handleBodyScroll                          - attach to body onScroll
// ------------------------------------------------------------------------

import { useState, useEffect, useLayoutEffect } from '../../vendor/preact-hooks.js';

export function useTableScroll({
  headTableRef,
  bodyTableRef,
  bodyScrollRef,
  headTrackRef,
  forecast,
  visibleCols,
  selectedDay,
  skinType,
  vehicleType,
  indoorMode,
  indoorManaged,
  showDecimals,
  showUnits,
}) {
  // --- SCROLL INDICATORS ---------------------------------------------
  const [tableCanScrollLeft,  setTableCanScrollLeft]  = useState(false);
  const [tableCanScrollRight, setTableCanScrollRight] = useState(false);

  const updateTableScrollIndicators = () => {
    const el = bodyScrollRef.current;
    if (!el) return;
    setTableCanScrollLeft(el.scrollLeft > 1);
    setTableCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
  };

  // --- DRAG-TO-SCROLL ------------------------------------------------
  useEffect(() => {
    const el = bodyScrollRef.current;
    if (!el) return;
    let isDown = false;
    let startX = 0;
    let startScroll = 0;

    const onMouseDown = (e) => {
      // Only act on clicks that land inside the body scroller
      if (!el.contains(e.target)) return;
      if (e.button !== 0) return;
      if (e.target.closest('button, a, input, select')) return;
      isDown = true;
      startX = e.clientX;
      startScroll = el.scrollLeft;
      el.style.cursor = 'grabbing';
      document.body.style.userSelect = 'none';
      document.body.style.webkitUserSelect = 'none';
    };
    const onMouseMove = (e) => {
      if (!isDown) return;
      const dx = e.clientX - startX;
      el.scrollLeft = startScroll - dx;
    };
    const onMouseUp = () => {
      if (!isDown) return;
      isDown = false;
      el.style.cursor = '';
      document.body.style.userSelect = '';
      document.body.style.webkitUserSelect = '';
    };

    // Attach everything to document so Preact's synthetic event system
    // cannot intercept or swallow the events before we see them.
    document.addEventListener('mousedown', onMouseDown);
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup',   onMouseUp);

    // Also update indicators on scroll
    el.addEventListener('scroll', updateTableScrollIndicators);

    return () => {
      document.removeEventListener('mousedown', onMouseDown);
      document.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mouseup',   onMouseUp);
      el.removeEventListener('scroll', updateTableScrollIndicators);
    };
  }, [forecast]);

  // Update indicators after layout sync (columns may have changed width)
  useEffect(() => {
    updateTableScrollIndicators();
  }, [forecast, visibleCols, selectedDay]);

  // --- BODY SCROLL HANDLER (called from JSX onScroll) ---------------
  const handleBodyScroll = () => {
    const track = headTrackRef.current;
    const body  = bodyScrollRef.current;
    if (!track || !body) return;
    track.style.transform = `translate3d(${-body.scrollLeft}px, 0, 0)`;
    updateTableScrollIndicators();
  };

  // --- COLUMN-WIDTH SCROLL SYNC (layout effect) ---------------------
  // Synchronise the head and body table column widths with a
  // "shrink-to-fit then distribute" strategy. See the comments inside
  // sync() for the algorithm.
  useLayoutEffect(() => {
    const sync = () => {
      const headTable  = headTableRef.current;
      const bodyTable  = bodyTableRef.current;
      const bodyScroll = bodyScrollRef.current;
      if (!headTable || !bodyTable || !bodyScroll) return;
      const bodyRow = bodyTable.querySelector('tbody tr');
      const headRow = headTable.querySelector('thead tr:last-child');
      if (!bodyRow || !headRow) return;
      const headCells = Array.from(headRow.children);
      const bodyCells = Array.from(bodyRow.children);
      const n = Math.min(headCells.length, bodyCells.length);
      if (n === 0) return;

      // Step 1: clear any previously-forced cell widths and switch the
      // tables to natural sizing so the measurement reflects the true
      // content-fit width - independent of how wide the container is.
      headCells.forEach(c => { c.style.width = ''; c.style.minWidth = ''; c.style.maxWidth = ''; });
      bodyCells.forEach(c => { c.style.width = ''; c.style.minWidth = ''; c.style.maxWidth = ''; });
      headTable.style.width = 'max-content';
      bodyTable.style.width = 'max-content';
      headTable.style.tableLayout = 'auto';
      bodyTable.style.tableLayout = 'auto';

      // Step 2: read each cell's natural width. getBoundingClientRect
      // forces synchronous layout - that's what we want.
      const naturalW = new Array(n);
      let naturalTotal = 0;
      for (let i = 0; i < n; i++) {
        const headW = headCells[i].getBoundingClientRect().width;
        const bodyW = bodyCells[i].getBoundingClientRect().width;
        const w = Math.max(Math.ceil(headW), Math.ceil(bodyW));
        naturalW[i] = w;
        naturalTotal += w;
      }

      // Step 3: decide final widths based on available container width.
      const containerW = bodyScroll.clientWidth;
      const finalW = new Array(n);
      let totalWidth;
      if (naturalTotal > 0 && naturalTotal < containerW) {
        // Spare space - distribute proportionally across columns so they
        // fan out to fill the scroller (no awkward right-hand gap).
        const scale = containerW / naturalTotal;
        let running = 0;
        for (let i = 0; i < n - 1; i++) {
          finalW[i] = Math.floor(naturalW[i] * scale);
          running += finalW[i];
        }
        // Absorb sub-pixel rounding into the last column so the total
        // exactly matches the container width.
        finalW[n - 1] = containerW - running;
        totalWidth = containerW;
      } else {
        // Naturals don't fit - use them as-is and let the body scroll.
        for (let i = 0; i < n; i++) finalW[i] = naturalW[i];
        totalWidth = naturalTotal;
      }

      // Step 4: restore the CSS-defined table-layout: fixed so the
      // explicit cell widths we apply below are honoured by the browser
      // (not redistributed by the auto-layout algorithm).
      headTable.style.tableLayout = 'auto';
      bodyTable.style.tableLayout = '';

      // Step 5: apply the final width to both head and body cells.
      for (let i = 0; i < n; i++) {
        const px = `${finalW[i]}px`;
        headCells[i].style.width    = px;
        headCells[i].style.minWidth = px;
        headCells[i].style.maxWidth = px;
        bodyCells[i].style.width    = px;
        bodyCells[i].style.minWidth = px;
        bodyCells[i].style.maxWidth = px;
      }
      // Make both tables exactly totalWidth wide so they share the same
      // horizontal extent - column N in the header sits directly above
      // column N in the body, no drift as you scroll right.
      headTable.style.width = `${totalWidth}px`;
      bodyTable.style.width = `${totalWidth}px`;
      // Re-apply current horizontal offset so column alignment survives.
      handleBodyScroll();
    };
    // Run once after layout
    sync();
    // Re-sync when the scroll container's width changes (window resize,
    // sidebar opens, etc). We observe the scroller - not the body table -
    // because the body table's width is now driven by sync itself, which
    // would otherwise create a feedback loop.
    let ro = null;
    if (typeof ResizeObserver !== 'undefined' && bodyScrollRef.current) {
      ro = new ResizeObserver(sync);
      ro.observe(bodyScrollRef.current);
    }
    window.addEventListener('resize', sync);
    return () => {
      if (ro) ro.disconnect();
      window.removeEventListener('resize', sync);
    };
  }, [forecast, visibleCols, selectedDay, skinType, vehicleType, indoorMode, indoorManaged, showDecimals, showUnits]);

  return {
    tableCanScrollLeft,
    tableCanScrollRight,
    handleBodyScroll,
  };
}
