import { useCallback, useEffect, useMemo, useState } from "react";
import LocalMediaGrid from "./local-media-grid.component";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getJobMedia } from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";

const mapStateToProps = createStructuredSelector({
  bodyshop: selectBodyshop,
  allMedia: selectAllMedia
});

const mapDispatchToProps = (dispatch) => ({
  getJobMedia: (id) => dispatch(getJobMedia(id))
});

export default connect(mapStateToProps, mapDispatchToProps)(JobDocumentsLocalGalleryExternal);

/**
 * JobDocumentsLocalGalleryExternal
 * Fetches and displays job-related image media using the custom LocalMediaGrid.
 *
 * Props:
 *  - jobId: string | number (required to fetch media)
 *  - externalMediaState: [imagesArray, setImagesFn] (state lifted to parent for shared selection)
 *  - getJobMedia: dispatching function to retrieve media for a job
 *  - allMedia: redux slice keyed by jobId containing raw media records
 *  - context: "chat" | "email" | other string used to drive grid behavior
 *
 * Notes:
 *  - The previous third-party gallery required a remount key (openVersion); custom grid no longer does.
 *  - Selection flags are preserved when media refreshes.
 *  - Loading state ends after transformation regardless of whether any images were found.
 */
function JobDocumentsLocalGalleryExternal({ jobId, externalMediaState, getJobMedia, allMedia, context = "chat" }) {
  const [galleryImages, setgalleryImages] = externalMediaState;
  const [isLoading, setIsLoading] = useState(false);
  const { t } = useTranslation(); // i18n hook retained if future translations are added
  const DEBUG_LOCAL_GALLERY = false; // flip to true for verbose console logging

  // Transform raw media record into a normalized image object consumed by the grid.
  const transformMediaToImages = useCallback((raw) => {
    return raw
      .filter((m) => m.type?.mime?.startsWith("image"))
      .map((m) => ({
        ...m,
        src: m.thumbnail,
        thumbnail: m.thumbnail,
        fullsize: m.src,
        width: 225,
        height: 225,
        thumbnailWidth: 225,
        thumbnailHeight: 225,
        caption: m.filename || m.key
      }));
  }, []);

  // Fetch media when jobId changes (network request triggers Redux update -> documents memo recalculates).
  useEffect(() => {
    if (!jobId) return;
    setIsLoading(true);
    getJobMedia(jobId);
  }, [jobId, getJobMedia]);

  // Memo: transform raw redux media into gallery documents.
  const documents = useMemo(
    () => transformMediaToImages(allMedia?.[jobId] || []),
    [allMedia, jobId, transformMediaToImages]
  );

  // Sync transformed documents into external state while preserving selection flags.
  useEffect(() => {
    const prevSelection = new Map(galleryImages.map((p) => [p.filename, p.isSelected]));
    const nextImages = documents.map((d) => ({ ...d, isSelected: prevSelection.get(d.filename) || false }));
    // Micro-optimization: if array length and each filename + selection flag match, skip creating a new array.
    if (galleryImages.length === nextImages.length) {
      let identical = true;
      for (let i = 0; i < nextImages.length; i++) {
        if (
          galleryImages[i].filename !== nextImages[i].filename ||
          galleryImages[i].isSelected !== nextImages[i].isSelected
        ) {
          identical = false;
          break;
        }
      }
      if (identical) {
        setIsLoading(false); // ensure loading stops even on no-change
        if (DEBUG_LOCAL_GALLERY) {
          console.log("[LocalGallery] documents unchanged", { jobId, count: documents.length });
        }
        return;
      }
    }
    setgalleryImages(nextImages);
    setIsLoading(false); // stop loading after transform regardless of emptiness
    if (DEBUG_LOCAL_GALLERY) {
      console.log("[LocalGallery] documents transformed", { jobId, count: documents.length });
    }
  }, [documents, setgalleryImages, galleryImages, jobId, DEBUG_LOCAL_GALLERY]);

  // Toggle handler (stable reference)
  const handleToggle = useCallback(
    (idx) => {
      setgalleryImages((imgs) => imgs.map((g, gIdx) => (gIdx === idx ? { ...g, isSelected: !g.isSelected } : g)));
    },
    [setgalleryImages]
  );

  const messageStyle = { textAlign: "center", padding: "1rem" }; // retained for potential future states

  if (!jobId) {
    return (
      <div aria-label="media gallery unavailable" style={{ position: "relative", minHeight: 80 }}>
        <div style={messageStyle}>No job selected.</div>
      </div>
    );
  }

  return (
    <div
      className="clearfix"
      style={{ position: "relative", minHeight: 80 }}
      data-jobid={jobId}
      aria-label={`media gallery for job ${jobId}`}
    >
      {isLoading && galleryImages.length === 0 && (
        <div className="local-gallery-loading" style={messageStyle} role="status" aria-live="polite">
          <LoadingSpinner />
        </div>
      )}
      {galleryImages.length > 0 && (
        <LocalMediaGrid images={galleryImages} minColumns={4} context={context} onToggle={handleToggle} />
      )}
      {galleryImages.length > 0 && (
        <div style={{ fontSize: 10, color: "#888", marginTop: 4 }} aria-live="off">
          {`${t("general.labels.media")}: ${galleryImages.length}`}
        </div>
      )}
    </div>
  );
}
