"use client";

import * as React from "react";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "@/lib/utils";

interface AccordionContextValue {
  type: "single" | "multiple";
  collapsible?: boolean;
  value?: string | string[];
  onValueChange?: (value: string | string[]) => void;
}

const AccordionContext = React.createContext<AccordionContextValue | null>(null);

interface AccordionItemContextValue {
  value: string;
}

const AccordionItemContext = React.createContext<AccordionItemContextValue | null>(null);

interface AccordionProps {
  type: "single" | "multiple";
  collapsible?: boolean;
  value?: string | string[];
  defaultValue?: string | string[];
  onValueChange?: (value: string | string[]) => void;
  className?: string;
  children: React.ReactNode;
  onClick?: (e: React.MouseEvent) => void;
}

function Accordion({
  type,
  collapsible = false,
  value: controlledValue,
  defaultValue,
  onValueChange,
  className,
  children,
  onClick,
  ...props
}: AccordionProps) {
  const [uncontrolledValue, setUncontrolledValue] = React.useState<string | string[]>(
    defaultValue || (type === "multiple" ? [] : ""),
  );

  const value = controlledValue !== undefined ? controlledValue : uncontrolledValue;

  const handleValueChange = React.useCallback(
    (newValue: string | string[]) => {
      if (controlledValue === undefined) {
        setUncontrolledValue(newValue);
      }
      onValueChange?.(newValue);
    },
    [controlledValue, onValueChange],
  );

  const contextValue = React.useMemo(
    () => ({
      type,
      collapsible,
      value,
      onValueChange: handleValueChange,
    }),
    [type, collapsible, value, handleValueChange],
  );

  return (
    <AccordionContext.Provider value={contextValue}>
      <div className={cn(className)} onClick={onClick} {...props}>
        {children}
      </div>
    </AccordionContext.Provider>
  );
}

interface AccordionItemProps {
  value: string;
  className?: string;
  children: React.ReactNode;
}

function AccordionItem({ value, className, children, ...props }: AccordionItemProps) {
  const context = React.useContext(AccordionContext);

  const handleClick = (e: React.MouseEvent) => {
    if (!context?.onValueChange) return;

    const target = e.target as HTMLElement;
    if (target.closest('button[type="button"]') && !target.closest(".accordion-trigger")) {
      return;
    }

    const isOpen =
      context.type === "multiple"
        ? Array.isArray(context.value) && context.value.includes(value)
        : context.value === value;

    if (context.type === "multiple") {
      const currentValue = Array.isArray(context.value) ? context.value : [];
      const newValue = isOpen ? currentValue.filter((v) => v !== value) : [...currentValue, value];
      context.onValueChange(newValue);
    } else {
      const newValue = isOpen && context.collapsible ? "" : value;
      context.onValueChange(newValue);
    }
  };

  return (
    <div className={cn("cursor-pointer", className)} data-value={value} onClick={handleClick} {...props}>
      {children}
    </div>
  );
}

interface AccordionTriggerProps {
  className?: string;
  children: React.ReactNode;
}

function AccordionTrigger({ className, children, ...props }: AccordionTriggerProps) {
  const context = React.useContext(AccordionContext);
  const parent = React.useContext(AccordionItemContext);

  if (!context || !parent) {
    throw new Error("AccordionTrigger must be used within AccordionItem");
  }

  const isOpen =
    context.type === "multiple"
      ? Array.isArray(context.value) && context.value.includes(parent.value)
      : context.value === parent.value;

  const handleClick = () => {
    if (!context.onValueChange) return;

    if (context.type === "multiple") {
      const currentValue = Array.isArray(context.value) ? context.value : [];
      const newValue = isOpen ? currentValue.filter((v) => v !== parent.value) : [...currentValue, parent.value];
      context.onValueChange(newValue);
    } else {
      const newValue = isOpen && context.collapsible ? "" : parent.value;
      context.onValueChange(newValue);
    }
  };

  return (
    <button
      className={cn(
        "accordion-trigger flex w-full cursor-pointer items-center justify-between p-4 text-left font-medium transition-colors focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-none",
        className,
      )}
      onClick={handleClick}
      type="button"
      {...props}
    >
      {children}
      <ChevronDownIcon className={cn("h-4 w-4 transition-transform duration-200", isOpen && "rotate-180")} />
    </button>
  );
}

interface AccordionContentProps {
  className?: string;
  children: React.ReactNode;
}

function AccordionContent({ className, children, ...props }: AccordionContentProps) {
  const context = React.useContext(AccordionContext);
  const parent = React.useContext(AccordionItemContext);
  const contentRef = React.useRef<HTMLDivElement>(null);

  if (!context || !parent) {
    throw new Error("AccordionContent must be used within AccordionItem");
  }

  const isOpen =
    context.type === "multiple"
      ? Array.isArray(context.value) && context.value.includes(parent.value)
      : context.value === parent.value;

  return (
    <div
      ref={contentRef}
      className={cn(
        "text-muted-foreground overflow-hidden text-sm transition-all duration-300 ease-in-out",
        isOpen ? "max-h-[2000px] opacity-100" : "max-h-0 opacity-0",
        className,
      )}
      onClick={(e) => e.stopPropagation()}
      {...props}
    >
      <div className="cursor-default">{children}</div>
    </div>
  );
}

// AccordionItem을 래핑하여 컨텍스트 제공
const AccordionItemWithContext = React.forwardRef<HTMLDivElement, AccordionItemProps>(
  ({ value, children, ...props }, ref) => {
    return (
      <AccordionItemContext.Provider value={{ value }}>
        <AccordionItem ref={ref} value={value} {...props}>
          {children}
        </AccordionItem>
      </AccordionItemContext.Provider>
    );
  },
);

AccordionItemWithContext.displayName = "AccordionItem";

export { Accordion, AccordionItemWithContext as AccordionItem, AccordionTrigger, AccordionContent };
