import { useMemo, useState, type ReactNode } from "react";
import { InspectorSelectPolicyContext } from "./InspectorContext.js";
import { Select, type SelectProps } from "./Select.js";
import { cn } from "./cn.js";

export interface InspectorSectionSpec {
  id: string;
  label: string;
  description?: string;
  group?: string;
  content: ReactNode;
  disabled?: boolean;
  tone?: "default" | "accent";
}

export interface InspectorProps {
  sections: InspectorSectionSpec[];
  defaultOpen?: string[];
  openSections?: string[];
  activeId?: string;
  singleOpen?: boolean;
  className?: string;
  onOpenSectionsChange?: (ids: string[]) => void;
  onActiveChange?: (id: string) => void;
}

export function Inspector({
  sections,
  defaultOpen = [],
  openSections,
  activeId,
  singleOpen = false,
  className,
  onOpenSectionsChange,
  onActiveChange,
}: InspectorProps) {
  const [internalOpenIds, setInternalOpenIds] = useState(() => new Set(defaultOpen));
  const openIds = useMemo(
    () => openSections === undefined ? internalOpenIds : new Set(openSections),
    [internalOpenIds, openSections],
  );
  const groups = useMemo(() => {
    const result: Array<{ label?: string; sections: InspectorSectionSpec[] }> = [];
    sections.forEach((section) => {
      const previous = result[result.length - 1];
      if (previous && previous.label === section.group) {
        previous.sections.push(section);
      } else {
        result.push({ label: section.group, sections: [section] });
      }
    });
    return result;
  }, [sections]);

  const toggle = (id: string) => {
    const next = singleOpen ? new Set<string>() : new Set(openIds);
    if (openIds.has(id)) next.delete(id);
    else next.add(id);
    if (openSections === undefined) setInternalOpenIds(next);
    onOpenSectionsChange?.([...next]);
    onActiveChange?.(id);
  };

  return (
    <InspectorSelectPolicyContext.Provider value>
      <div className={cn("nodedc-inspector", className)}>
        {groups.map((group, groupIndex) => (
          <div className="nodedc-inspector__group" key={`${group.label ?? "root"}-${groupIndex}`}>
            {group.sections.map((section) => {
              const isOpen = openIds.has(section.id);
              return (
                <section className="nodedc-inspector__section" key={section.id}>
                  <button
                    type="button"
                    className="nodedc-inspector__section-trigger"
                    data-open={isOpen ? "true" : undefined}
                    data-active={activeId === section.id ? "true" : undefined}
                    data-tone={section.tone === "accent" ? "accent" : undefined}
                    aria-expanded={isOpen}
                    disabled={section.disabled}
                    onClick={() => toggle(section.id)}
                  >
                    <span className="nodedc-inspector__section-label">{section.label}</span>
                    {section.description ? (
                      <span className="nodedc-inspector__section-description">{section.description}</span>
                    ) : null}
                  </button>
                  {isOpen ? <div className="nodedc-inspector__section-content">{section.content}</div> : null}
                </section>
              );
            })}
          </div>
        ))}
      </div>
    </InspectorSelectPolicyContext.Provider>
  );
}

export interface ControlRowProps {
  label: ReactNode;
  children: ReactNode;
  layout?: "inline" | "stack";
  className?: string;
}

export function ControlRow({ label, children, layout = "inline", className }: ControlRowProps) {
  return (
    <div
      className={cn("nodedc-control-row", className)}
      data-layout={layout === "inline" ? undefined : layout}
    >
      <div className="nodedc-control-row__label">{label}</div>
      <div className="nodedc-control-row__control">{children}</div>
    </div>
  );
}

export interface InspectorSelectFieldProps<T extends string>
  extends Omit<SelectProps<T>, "label" | "variant"> {
  label: string;
  fieldClassName?: string;
}

/**
 * Canonical Inspector selection field.
 *
 * Inspector labels always sit above a full-width Engine split select. The
 * public API intentionally exposes neither `layout` nor `variant`, so an
 * Inspector consumer cannot fall back to the Hub/Launcher integrated pill.
 */
export function InspectorSelectField<T extends string>({
  label,
  fieldClassName,
  ...selectProps
}: InspectorSelectFieldProps<T>) {
  return (
    <ControlRow label={label} layout="stack" className={fieldClassName}>
      <Select {...selectProps} label={label} variant="split" />
    </ControlRow>
  );
}
