"use client";

/**
 * V2Date
 *
 * 통합 날짜/시간 컴포넌트
 * - date: 날짜 선택
 * - time: 시간 선택
 * - datetime: 날짜+시간 선택
 * - range 옵션: 범위 선택 (시작~종료)
 */

import React, { forwardRef, useCallback, useMemo, useState } from "react";
import { format, parse, isValid } from "date-fns";
import { ko } from "date-fns/locale";
import { Calendar as CalendarIcon, Clock } from "lucide-react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { V2DateProps, V2DateType } from "@/types/v2-components";

// 날짜 형식 매핑
const DATE_FORMATS: Record<string, string> = {
  "YYYY-MM-DD": "yyyy-MM-dd",
  "YYYY/MM/DD": "yyyy/MM/dd",
  "DD-MM-YYYY": "dd-MM-yyyy",
  "DD/MM/YYYY": "dd/MM/yyyy",
  "MM-DD-YYYY": "MM-dd-yyyy",
  "MM/DD/YYYY": "MM/dd/yyyy",
  "YYYY-MM-DD HH:mm": "yyyy-MM-dd HH:mm",
  "YYYY-MM-DD HH:mm:ss": "yyyy-MM-dd HH:mm:ss",
};

// 날짜 문자열 → Date 객체
function parseDate(value: string | undefined, formatStr: string): Date | undefined {
  if (!value) return undefined;

  const dateFnsFormat = DATE_FORMATS[formatStr] || formatStr;

  try {
    // ISO 형식 먼저 시도
    const isoDate = new Date(value);
    if (isValid(isoDate)) return isoDate;

    // 포맷에 맞게 파싱
    const parsed = parse(value, dateFnsFormat, new Date());
    return isValid(parsed) ? parsed : undefined;
  } catch {
    return undefined;
  }
}

// Date 객체 → 날짜 문자열
function formatDate(date: Date | undefined, formatStr: string): string {
  if (!date || !isValid(date)) return "";
  const dateFnsFormat = DATE_FORMATS[formatStr] || formatStr;
  return format(date, dateFnsFormat);
}

/**
 * 단일 날짜 선택 컴포넌트
 */
const SingleDatePicker = forwardRef<
  HTMLButtonElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    dateFormat: string;
    showToday?: boolean;
    minDate?: string;
    maxDate?: string;
    disabled?: boolean;
    readonly?: boolean;
    className?: string;
    placeholder?: string;
  }
>(
  (
    { value, onChange, dateFormat = "YYYY-MM-DD", showToday = true, minDate, maxDate, disabled, readonly, className, placeholder = "날짜 선택" },
    ref,
  ) => {
    const [open, setOpen] = useState(false);

    const date = useMemo(() => parseDate(value, dateFormat), [value, dateFormat]);
    const minDateObj = useMemo(() => parseDate(minDate, dateFormat), [minDate, dateFormat]);
    const maxDateObj = useMemo(() => parseDate(maxDate, dateFormat), [maxDate, dateFormat]);

    // 표시할 날짜 텍스트 계산 (ISO 형식이면 포맷팅, 아니면 그대로)
    const displayText = useMemo(() => {
      if (!value) return "";
      // Date 객체로 변환 후 포맷팅
      if (date && isValid(date)) {
        return formatDate(date, dateFormat);
      }
      return value;
    }, [value, date, dateFormat]);

    const handleSelect = useCallback(
      (selectedDate: Date | undefined) => {
        if (selectedDate) {
          onChange?.(formatDate(selectedDate, dateFormat));
          setOpen(false);
        }
      },
      [dateFormat, onChange],
    );

    const handleToday = useCallback(() => {
      onChange?.(formatDate(new Date(), dateFormat));
      setOpen(false);
    }, [dateFormat, onChange]);

    const handleClear = useCallback(() => {
      onChange?.("");
      setOpen(false);
    }, [onChange]);

    return (
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <Button
            ref={ref}
            variant="outline"
            disabled={disabled || readonly}
            className={cn(
              "h-full w-full justify-start text-left font-normal",
              !displayText && "text-muted-foreground",
              className,
            )}
          >
            <CalendarIcon className="mr-2 h-4 w-4 flex-shrink-0" />
            {displayText || placeholder}
          </Button>
        </PopoverTrigger>
        <PopoverContent className="w-auto p-0" align="start">
          <Calendar
            mode="single"
            selected={date}
            onSelect={handleSelect}
            initialFocus
            locale={ko}
            disabled={(date) => {
              if (minDateObj && date < minDateObj) return true;
              if (maxDateObj && date > maxDateObj) return true;
              return false;
            }}
          />
          <div className="flex gap-2 p-3 pt-0">
            {showToday && (
              <Button variant="outline" size="sm" onClick={handleToday}>
                오늘
              </Button>
            )}
            <Button variant="ghost" size="sm" onClick={handleClear}>
              초기화
            </Button>
          </div>
        </PopoverContent>
      </Popover>
    );
  },
);
SingleDatePicker.displayName = "SingleDatePicker";

/**
 * 날짜 범위 선택 컴포넌트
 */
const RangeDatePicker = forwardRef<
  HTMLDivElement,
  {
    value?: [string, string];
    onChange?: (value: [string, string]) => void;
    dateFormat: string;
    minDate?: string;
    maxDate?: string;
    disabled?: boolean;
    readonly?: boolean;
    className?: string;
  }
>(({ value = ["", ""], onChange, dateFormat = "YYYY-MM-DD", minDate, maxDate, disabled, readonly, className }, ref) => {
  const [openStart, setOpenStart] = useState(false);
  const [openEnd, setOpenEnd] = useState(false);

  const startDate = useMemo(() => parseDate(value[0], dateFormat), [value, dateFormat]);
  const endDate = useMemo(() => parseDate(value[1], dateFormat), [value, dateFormat]);
  const minDateObj = useMemo(() => parseDate(minDate, dateFormat), [minDate, dateFormat]);
  const maxDateObj = useMemo(() => parseDate(maxDate, dateFormat), [maxDate, dateFormat]);

  const handleStartSelect = useCallback(
    (date: Date | undefined) => {
      if (date) {
        const newStart = formatDate(date, dateFormat);
        // 시작일이 종료일보다 크면 종료일도 같이 변경
        if (endDate && date > endDate) {
          onChange?.([newStart, newStart]);
        } else {
          onChange?.([newStart, value[1]]);
        }
        setOpenStart(false);
      }
    },
    [value, dateFormat, endDate, onChange],
  );

  const handleEndSelect = useCallback(
    (date: Date | undefined) => {
      if (date) {
        const newEnd = formatDate(date, dateFormat);
        // 종료일이 시작일보다 작으면 시작일도 같이 변경
        if (startDate && date < startDate) {
          onChange?.([newEnd, newEnd]);
        } else {
          onChange?.([value[0], newEnd]);
        }
        setOpenEnd(false);
      }
    },
    [value, dateFormat, startDate, onChange],
  );

  return (
    <div ref={ref} className={cn("flex items-center gap-2", className)}>
      {/* 시작 날짜 */}
      <Popover open={openStart} onOpenChange={setOpenStart}>
        <PopoverTrigger asChild>
          <Button
            variant="outline"
            disabled={disabled || readonly}
            className={cn("h-10 flex-1 justify-start text-left font-normal", !value[0] && "text-muted-foreground")}
          >
            <CalendarIcon className="mr-2 h-4 w-4" />
            {value[0] || "시작일"}
          </Button>
        </PopoverTrigger>
        <PopoverContent className="w-auto p-0" align="start">
          <Calendar
            mode="single"
            selected={startDate}
            onSelect={handleStartSelect}
            initialFocus
            locale={ko}
            disabled={(date) => {
              if (minDateObj && date < minDateObj) return true;
              if (maxDateObj && date > maxDateObj) return true;
              return false;
            }}
          />
        </PopoverContent>
      </Popover>

      <span className="text-muted-foreground">~</span>

      {/* 종료 날짜 */}
      <Popover open={openEnd} onOpenChange={setOpenEnd}>
        <PopoverTrigger asChild>
          <Button
            variant="outline"
            disabled={disabled || readonly}
            className={cn("h-10 flex-1 justify-start text-left font-normal", !value[1] && "text-muted-foreground")}
          >
            <CalendarIcon className="mr-2 h-4 w-4" />
            {value[1] || "종료일"}
          </Button>
        </PopoverTrigger>
        <PopoverContent className="w-auto p-0" align="start">
          <Calendar
            mode="single"
            selected={endDate}
            onSelect={handleEndSelect}
            initialFocus
            locale={ko}
            disabled={(date) => {
              if (minDateObj && date < minDateObj) return true;
              if (maxDateObj && date > maxDateObj) return true;
              // 시작일보다 이전 날짜는 선택 불가
              if (startDate && date < startDate) return true;
              return false;
            }}
          />
        </PopoverContent>
      </Popover>
    </div>
  );
});
RangeDatePicker.displayName = "RangeDatePicker";

/**
 * 시간 선택 컴포넌트
 */
const TimePicker = forwardRef<
  HTMLInputElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    disabled?: boolean;
    readonly?: boolean;
    className?: string;
  }
>(({ value, onChange, disabled, readonly, className }, ref) => {
  return (
    <div className={cn("relative", className)}>
      <Clock className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
      <Input
        ref={ref}
        type="time"
        value={value || ""}
        onChange={(e) => onChange?.(e.target.value)}
        disabled={disabled}
        readOnly={readonly}
        className="h-10 pl-10"
      />
    </div>
  );
});
TimePicker.displayName = "TimePicker";

/**
 * 날짜+시간 선택 컴포넌트
 */
const DateTimePicker = forwardRef<
  HTMLDivElement,
  {
    value?: string;
    onChange?: (value: string) => void;
    dateFormat: string;
    minDate?: string;
    maxDate?: string;
    disabled?: boolean;
    readonly?: boolean;
    className?: string;
  }
>(({ value, onChange, dateFormat = "YYYY-MM-DD HH:mm", minDate, maxDate, disabled, readonly, className }, ref) => {
  // 날짜와 시간 분리
  const [datePart, timePart] = useMemo(() => {
    if (!value) return ["", ""];
    const parts = value.split(" ");
    return [parts[0] || "", parts[1] || ""];
  }, [value]);

  const handleDateChange = useCallback(
    (newDate: string) => {
      const newValue = `${newDate} ${timePart || "00:00"}`;
      onChange?.(newValue.trim());
    },
    [timePart, onChange],
  );

  const handleTimeChange = useCallback(
    (newTime: string) => {
      const newValue = `${datePart || format(new Date(), "yyyy-MM-dd")} ${newTime}`;
      onChange?.(newValue.trim());
    },
    [datePart, onChange],
  );

  return (
    <div ref={ref} className={cn("flex gap-2", className)}>
      <div className="flex-1">
        <SingleDatePicker
          value={datePart}
          onChange={handleDateChange}
          dateFormat="YYYY-MM-DD"
          minDate={minDate}
          maxDate={maxDate}
          disabled={disabled}
          readonly={readonly}
        />
      </div>
      <div className="w-32">
        <TimePicker value={timePart} onChange={handleTimeChange} disabled={disabled} readonly={readonly} />
      </div>
    </div>
  );
});
DateTimePicker.displayName = "DateTimePicker";

/**
 * 메인 V2Date 컴포넌트
 */
export const V2Date = forwardRef<HTMLDivElement, V2DateProps>((props, ref) => {
  const { id, label, required, readonly, disabled, style, size, config: configProp, value, onChange } = props;

  // config가 없으면 기본값 사용
  const config = configProp || { type: "date" as const };

  const dateFormat = config.format || "YYYY-MM-DD";

  // 타입별 컴포넌트 렌더링
  const renderDatePicker = () => {
    const isDisabled = disabled || readonly;

    // 범위 선택
    if (config.range) {
      return (
        <RangeDatePicker
          value={Array.isArray(value) ? (value as [string, string]) : ["", ""]}
          onChange={onChange as (value: [string, string]) => void}
          dateFormat={dateFormat}
          minDate={config.minDate}
          maxDate={config.maxDate}
          disabled={isDisabled}
          readonly={readonly}
        />
      );
    }

    // 타입별 렌더링
    switch (config.type) {
      case "date":
        return (
          <SingleDatePicker
            value={typeof value === "string" ? value : ""}
            onChange={(v) => onChange?.(v)}
            dateFormat={dateFormat}
            showToday={config.showToday}
            minDate={config.minDate}
            maxDate={config.maxDate}
            disabled={isDisabled}
            readonly={readonly}
            placeholder={config.placeholder}
          />
        );

      case "time":
        return (
          <TimePicker
            value={typeof value === "string" ? value : ""}
            onChange={(v) => onChange?.(v)}
            disabled={isDisabled}
            readonly={readonly}
          />
        );

      case "datetime":
        return (
          <DateTimePicker
            value={typeof value === "string" ? value : ""}
            onChange={(v) => onChange?.(v)}
            dateFormat={dateFormat}
            minDate={config.minDate}
            maxDate={config.maxDate}
            disabled={isDisabled}
            readonly={readonly}
          />
        );

      default:
        return (
          <SingleDatePicker
            value={typeof value === "string" ? value : ""}
            onChange={(v) => onChange?.(v)}
            dateFormat={dateFormat}
            showToday={config.showToday}
            disabled={isDisabled}
            readonly={readonly}
            placeholder={config.placeholder}
          />
        );
    }
  };

  const showLabel = label && style?.labelDisplay !== false;
  const componentWidth = size?.width || style?.width;
  const componentHeight = size?.height || style?.height;

  // 라벨 높이 계산 (기본 20px, 사용자 설정에 따라 조정)
  const labelFontSize = style?.labelFontSize ? parseInt(String(style.labelFontSize)) : 14;
  const labelMarginBottom = style?.labelMarginBottom ? parseInt(String(style.labelMarginBottom)) : 4;
  const estimatedLabelHeight = labelFontSize + labelMarginBottom + 2;

  return (
    <div
      ref={ref}
      id={id}
      className="relative"
      style={{
        width: componentWidth,
        height: componentHeight,
      }}
    >
      {/* 🔧 라벨을 absolute로 컴포넌트 위에 배치 */}
      {showLabel && (
        <Label
          htmlFor={id}
          style={{
            position: "absolute",
            top: `-${estimatedLabelHeight}px`,
            left: 0,
            fontSize: style?.labelFontSize || "14px",
            color: style?.labelColor || "#64748b",
            fontWeight: style?.labelFontWeight || "500",
          }}
          className="text-sm font-medium whitespace-nowrap"
        >
          {label}
          {required && <span className="ml-0.5 text-orange-500">*</span>}
        </Label>
      )}
      <div className="h-full w-full">
        {renderDatePicker()}
      </div>
    </div>
  );
});

V2Date.displayName = "V2Date";

export default V2Date;
