/**
 * Export column picker.
 *
 * Icon button that sits next to "Export CSV" and opens a dropdown listing
 * every exportable column, grouped by section. Ticking a box updates the
 * selection used by the next export and is saved to the admin's user meta.
 *
 * @package ArraySubs
 * @since 1.0.0
 */

import React, { useEffect, useMemo, useRef, useState } from "react";
import { __, _n, sprintf } from "@wordpress/i18n";
import { Columns3, Loader2, RotateCcw } from "lucide-react";

/**
 * Case-insensitive match of a column against the filter box.
 *
 * @param {Object} column Column definition.
 * @param {string} needle Lowercased search term.
 * @returns {boolean} Whether the column should stay visible.
 */
const matchesFilter = (column, needle) => {
  if (!needle) {
    return true;
  }

  return (
    String(column.label || "")
      .toLowerCase()
      .includes(needle) ||
    String(column.key || "")
      .toLowerCase()
      .includes(needle)
  );
};

const ExportColumnsMenu = ({
  groups = [],
  selected = [],
  loading = false,
  saving = false,
  loadError = "",
  onToggleColumn,
  onApplySelection,
  onSelectAll,
  onResetToDefaults,
}) => {
  const [open, setOpen] = useState(false);
  const [filter, setFilter] = useState("");
  const wrapperRef = useRef(null);
  const triggerRef = useRef(null);
  const filterRef = useRef(null);

  const selectedSet = useMemo(() => new Set(selected), [selected]);
  const totalColumns = useMemo(
    () =>
      groups.reduce((total, group) => total + (group.columns || []).length, 0),
    [groups],
  );

  const needle = filter.trim().toLowerCase();
  const visibleGroups = useMemo(
    () =>
      groups
        .map((group) => ({
          ...group,
          columns: (group.columns || []).filter((column) =>
            matchesFilter(column, needle),
          ),
        }))
        .filter((group) => group.columns.length > 0),
    [groups, needle],
  );

  // Close on outside click and on Escape, returning focus to the trigger.
  useEffect(() => {
    if (!open) {
      return undefined;
    }

    const handlePointerDown = (event) => {
      if (!wrapperRef.current?.contains(event.target)) {
        setOpen(false);
      }
    };

    const handleKeyDown = (event) => {
      if (event.key === "Escape") {
        setOpen(false);
        triggerRef.current?.focus();
      }
    };

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [open]);

  useEffect(() => {
    if (open) {
      filterRef.current?.focus();
    } else {
      setFilter("");
    }
  }, [open]);

  /**
   * Tick or untick every column in one group at once.
   *
   * @param {Object}  group    Group definition.
   * @param {boolean} checkAll Whether to select rather than clear the group.
   */
  const handleGroupToggle = (group, checkAll) => {
    const groupKeys = (group.columns || []).map((column) => column.key);
    const next = checkAll
      ? [...selected, ...groupKeys]
      : selected.filter((key) => !groupKeys.includes(key));

    onApplySelection?.(next);
  };

  const renderGroup = (group) => {
    const groupKeys = (group.columns || []).map((column) => column.key);
    const checkedCount = groupKeys.filter((key) => selectedSet.has(key)).length;
    const allChecked = checkedCount === groupKeys.length;
    // Clearing a group is blocked when it holds every selected column, so the
    // export always keeps at least one.
    const clearingEmptiesAll = allChecked && checkedCount === selected.length;

    return (
      <div className="arraysubs-export-columns__group" key={group.key}>
        {/* Title first, then the count and the group tick box on the right, so
            the column checkboxes below start on the same left edge. */}
        <label className="arraysubs-export-columns__group-head">
          <span className="arraysubs-export-columns__group-title">
            {group.label}
          </span>
          <span className="arraysubs-export-columns__group-count">
            {checkedCount}/{groupKeys.length}
          </span>
          <input
            type="checkbox"
            checked={allChecked}
            disabled={clearingEmptiesAll}
            aria-label={sprintf(
              /* translators: %s: export column group name, e.g. "Shipping Address" */
              __("Select all %s columns", "arraysubs"),
              group.label,
            )}
            ref={(node) => {
              if (node) {
                node.indeterminate = checkedCount > 0 && !allChecked;
              }
            }}
            onChange={(event) => handleGroupToggle(group, event.target.checked)}
          />
        </label>

        <ul className="arraysubs-export-columns__list">
          {(group.columns || []).map((column) => {
            const checked = selectedSet.has(column.key);
            const isLastChecked = checked && selected.length === 1;

            return (
              <li key={column.key}>
                <label
                  className={`arraysubs-export-columns__item${
                    isLastChecked
                      ? " arraysubs-export-columns__item--locked"
                      : ""
                  }`}
                  title={
                    isLastChecked
                      ? __("At least one column must stay selected.", "arraysubs")
                      : undefined
                  }
                >
                  <input
                    type="checkbox"
                    checked={checked}
                    disabled={isLastChecked}
                    onChange={() => onToggleColumn?.(column.key)}
                  />
                  <span>{column.label}</span>
                </label>
              </li>
            );
          })}
        </ul>
      </div>
    );
  };

  const renderBody = () => {
    if (loading) {
      return (
        <p className="arraysubs-export-columns__status">
          <Loader2 size={14} className="arraysubs-spin" />
          {__("Loading columns…", "arraysubs")}
        </p>
      );
    }

    if (loadError) {
      return (
        <p className="arraysubs-export-columns__status arraysubs-export-columns__status--error">
          {loadError}
        </p>
      );
    }

    if (visibleGroups.length === 0) {
      return (
        <p className="arraysubs-export-columns__status">
          {__("No columns match your search.", "arraysubs")}
        </p>
      );
    }

    return visibleGroups.map(renderGroup);
  };

  return (
    <div className="arraysubs-export-columns" ref={wrapperRef}>
      <button
        type="button"
        ref={triggerRef}
        className="button button-secondary arraysubs-dl-custom-btn arraysubs-export-columns__trigger"
        aria-haspopup="dialog"
        aria-expanded={open}
        aria-label={__("Choose export columns", "arraysubs")}
        title={__("Choose export columns", "arraysubs")}
        onClick={() => setOpen((current) => !current)}
      >
        <Columns3 size={16} aria-hidden="true" />
      </button>

      {open && (
        <div
          className="arraysubs-export-columns__panel"
          role="dialog"
          aria-label={__("Export columns", "arraysubs")}
        >
          <div className="arraysubs-export-columns__header">
            <div>
              <h2 className="arraysubs-export-columns__title">
                {__("Export columns", "arraysubs")}
              </h2>
              <p className="arraysubs-export-columns__subtitle">
                {__(
                  "Pick the columns to include in the CSV. Your choice is saved for next time.",
                  "arraysubs",
                )}
              </p>
            </div>
          </div>

          <input
            type="search"
            ref={filterRef}
            className="arraysubs-export-columns__filter"
            placeholder={__("Search columns…", "arraysubs")}
            value={filter}
            onChange={(event) => setFilter(event.target.value)}
          />

          <div className="arraysubs-export-columns__body">{renderBody()}</div>

          <div className="arraysubs-export-columns__footer">
            <span className="arraysubs-export-columns__summary">
              {saving ? (
                <>
                  <Loader2 size={13} className="arraysubs-spin" />
                  {__("Saving…", "arraysubs")}
                </>
              ) : (
                sprintf(
                  /* translators: 1: selected column count, 2: total column count */
                  _n(
                    "%1$d of %2$d column selected",
                    "%1$d of %2$d columns selected",
                    selected.length,
                    "arraysubs",
                  ),
                  selected.length,
                  totalColumns,
                )
              )}
            </span>

            <span className="arraysubs-export-columns__actions">
              <button
                type="button"
                className="button-link"
                onClick={onSelectAll}
                disabled={loading || selected.length === totalColumns}
              >
                {__("Select all", "arraysubs")}
              </button>
              <button
                type="button"
                className="button-link"
                onClick={onResetToDefaults}
                disabled={loading}
              >
                <RotateCcw size={12} aria-hidden="true" />
                {__("Reset to default", "arraysubs")}
              </button>
            </span>
          </div>
        </div>
      )}
    </div>
  );
};

export default ExportColumnsMenu;
