"use client";

import type { TextBlockProps } from "@/features/editor/state/editorStore";
import { PropertySliderField } from "@/features/editor/components/PropertySliderField";
import { ColorPickerField, TEXT_COLOR_PALETTE } from "@/features/editor/components/ColorPickerField";
import { NumericPropertyControl } from "@/features/editor/components/NumericPropertyControl";
import { useAppLocale } from "@/features/i18n/LocaleProvider";
import { getEditorTextPanelMessages } from "@/features/i18n/messages/editorTextPanel";

export type TextPropertiesPanelProps = {
  textProps: TextBlockProps;
  selectedBlockId: string;
  updateBlock: (id: string, partial: Partial<TextBlockProps>) => void;
  editingBlockId: string | null;
  setEditingText: (value: string) => void;
};

export function TextPropertiesPanel({
  textProps,
  selectedBlockId,
  updateBlock,
  editingBlockId,
  setEditingText,
}: TextPropertiesPanelProps) {
  const locale = useAppLocale();
  const m = getEditorTextPanelMessages(locale);
  const fontSizeMode = textProps.fontSizeMode ?? "scale";
  const fallbackScale =
    textProps.size === "sm" ? "sm" : textProps.size === "lg" ? "lg" : "base";
  const fontSizeScale = textProps.fontSizeScale ?? fallbackScale;
  const lineHeightMode = textProps.lineHeightMode ?? "scale";
  const lineHeightScale = textProps.lineHeightScale ?? "normal";
  const fontWeightMode = textProps.fontWeightMode ?? "scale";
  const fontWeightScale = textProps.fontWeightScale ?? "normal";
  const colorPalette = textProps.colorPalette ?? "default";
  const maxWidthMode = textProps.maxWidthMode ?? "scale";
  const maxWidthScale = textProps.maxWidthScale ?? "none";

  const fontSizeScaleToPx: Record<NonNullable<TextBlockProps["fontSizeScale"]>, number> = {
    xs: 12,
    sm: 14,
    base: 16,
    lg: 18,
    xl: 20,
    "2xl": 24,
    "3xl": 30,
  };

  const parsedFontSize = (() => {
    const raw = textProps.fontSizeCustom ?? "";
    const match = raw.match(/([0-9]+(?:\.[0-9]+)?)/);
    if (match) return Number(match[1]);
    return fontSizeScaleToPx[fontSizeScale];
  })();

  const parsedLineHeight = (() => {
    const raw = textProps.lineHeightCustom ?? "";
    const match = raw.match(/(-?[0-9]+(?:\.[0-9]+)?)/);
    if (match) return Number(match[1]);
    return 1.5;
  })();

  // 글자 간격: 내부 저장은 em 이지만, UI 에서는 px 단위로 보여준다.
  const parsedLetterSpacingPx = (() => {
    const raw = textProps.letterSpacingCustom ?? "";
    const match = raw.match(/(-?[0-9]+(?:\.[0-9]+)?)/);
    if (match) {
      const em = Number(match[1]);
      if (Number.isFinite(em)) {
        return em * 16; // 1em = 16px 기준으로 환산
      }
    }
    return 0;
  })();

  const letterSpacingPreset = (() => {
    const px = parsedLetterSpacingPx;
    if (px <= -1) return "tighter" as const;
    if (px < 0) return "tight" as const;
    if (px < 1) return "normal" as const;
    if (px < 3) return "wide" as const;
    return "wider" as const;
  })();

  const parsedFontWeight = (() => {
    const raw = textProps.fontWeightCustom ?? "";
    const match = raw.match(/([0-9]{3})/);
    if (match) return Number(match[1]);
    switch (fontWeightScale) {
      case "medium":
        return 500;
      case "semibold":
        return 600;
      case "bold":
        return 700;
      default:
        return 400;
    }
  })();

  return (
    <>
      <div className="space-y-1">
        <p className="text-xs text-slate-400">{m.selectedTextLabel}</p>
        <textarea
          className="w-full min-h-[80px] rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
          aria-label={m.selectedTextAria}
          value={textProps.text}
          onChange={(e) => {
            const value = e.target.value;
            updateBlock(selectedBlockId, { text: value });
            if (editingBlockId === selectedBlockId) {
              setEditingText(value);
            }
          }}
        />
      </div>

      <div className="space-y-1">
        <label className="flex flex-col gap-1 text-xs text-slate-400">
          <span>{m.alignLabel}</span>
          <select
            className="rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
            aria-label={m.alignAria}
            value={textProps.align}
            onChange={(e) => {
              const value = e.target.value as "left" | "center" | "right";
              updateBlock(selectedBlockId, { align: value });
            }}
          >
            <option value="left">{m.alignOptionLeft}</option>
            <option value="center">{m.alignOptionCenter}</option>
            <option value="right">{m.alignOptionRight}</option>
          </select>
        </label>
      </div>

      <div className="space-y-1">
        <div className="flex items-center justify-between text-[11px] text-slate-400">
          <span>{m.textStyleLabel}</span>
          <div className="flex gap-1">
            <button
              type="button"
              className={`px-2 py-0.5 rounded border text-[11px] ${
                textProps.underline
                  ? "border-sky-500 bg-sky-900/40 text-sky-100"
                  : "border-slate-300 bg-white text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
              }`}
              onClick={() => {
                updateBlock(selectedBlockId, {
                  underline: !textProps.underline,
                } as any);
              }}
            >
              {m.underlineLabel}
            </button>
            <button
              type="button"
              className={`px-2 py-0.5 rounded border text-[11px] ${
                textProps.strike
                  ? "border-sky-500 bg-sky-900/40 text-sky-100"
                  : "border-slate-300 bg-white text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
              }`}
              onClick={() => {
                updateBlock(selectedBlockId, {
                  strike: !textProps.strike,
                } as any);
              }}
            >
              {m.strikeLabel}
            </button>
            <button
              type="button"
              className={`px-2 py-0.5 rounded border text-[11px] ${
                textProps.italic
                  ? "border-sky-500 bg-sky-900/40 text-sky-100"
                  : "border-slate-300 bg-white text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
              }`}
              onClick={() => {
                updateBlock(selectedBlockId, {
                  italic: !textProps.italic,
                } as any);
              }}
            >
              {m.italicLabel}
            </button>
          </div>
        </div>
      </div>

      <div className="space-y-1">
        <NumericPropertyControl
          label={m.fontSizeLabel}
          unitLabel={m.fontSizeUnitLabel}
          value={Number.isFinite(parsedFontSize) ? parsedFontSize : 16}
          min={10}
          max={72}
          step={1}
          presets={(
            ["xs", "sm", "base", "lg", "xl", "2xl", "3xl"] as NonNullable<
              TextBlockProps["fontSizeScale"]
            >[]
          ).map((scale) => ({
            id: scale,
            label:
              scale === "xs"
                ? "XS"
                : scale === "sm"
                ? "S"
                : scale === "base"
                ? "M"
                : scale === "lg"
                ? "L"
                : scale === "xl"
                ? "XL"
                : scale === "2xl"
                ? "2XL"
                : "3XL",
            value: fontSizeScaleToPx[scale],
          }))}
          onChangeValue={(px) => {
            const raw = textProps.fontSizeCustom ?? "";
            const suffixMatch = raw.match(/px|rem|em|%/);
            const suffix = suffixMatch ? suffixMatch[0] : "px";

            const entries = Object.entries(fontSizeScaleToPx) as [
              NonNullable<TextBlockProps["fontSizeScale"]>,
              number,
            ][];
            const closest = entries.reduce(
              (best, [scale, value]) => {
                const dist = Math.abs(value - px);
                if (dist < best.dist) return { scale, dist };
                return best;
              },
              { scale: fontSizeScale as NonNullable<TextBlockProps["fontSizeScale"]>, dist: Infinity },
            ).scale;

            updateBlock(selectedBlockId, {
              fontSizeScale: closest,
              fontSizeCustom: `${px}${suffix}`,
              fontSizeMode: "custom",
            } as any);
          }}
        />
      </div>

      <div className="space-y-1">
        <label className="flex flex-col gap-1 text-xs text-slate-400">
          <span>{m.letterSpacingLabel}</span>
          <div className="flex items-center gap-2">
            <select
              className="w-32 rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
              aria-label={m.letterSpacingPresetAria}
              value={letterSpacingPreset}
              onChange={(e) => {
                const preset = e.target.value as
                  | "tighter"
                  | "tight"
                  | "normal"
                  | "wide"
                  | "wider";

                const presetPxMap: Record<
                  "tighter" | "tight" | "normal" | "wide" | "wider",
                  number
                > = {
                  tighter: -1.5,
                  tight: -0.5,
                  normal: 0,
                  wide: 1,
                  wider: 2,
                };

                const px = presetPxMap[preset];
                const em = px / 16;

                updateBlock(selectedBlockId, {
                  letterSpacingCustom: `${em}em`,
                } as any);
              }}
            >
              <option value="tighter">{m.letterSpacingPresetTighter}</option>
              <option value="tight">{m.letterSpacingPresetTight}</option>
              <option value="normal">{m.letterSpacingPresetNormal}</option>
              <option value="wide">{m.letterSpacingPresetWide}</option>
              <option value="wider">{m.letterSpacingPresetWider}</option>
            </select>

            <PropertySliderField
              label=""
              ariaLabelSlider={m.letterSpacingSliderAria}
              ariaLabelInput={m.letterSpacingInputAria}
              value={Number.isFinite(parsedLetterSpacingPx) ? parsedLetterSpacingPx : 0}
              min={-2}
              max={10}
              step={0.1}
              onChange={(px) => {
                const em = px / 16;
                updateBlock(selectedBlockId, {
                  letterSpacingCustom: `${em}em`,
                } as any);
              }}
            />
          </div>
        </label>
      </div>

      <div className="space-y-1">
        <NumericPropertyControl
          label={m.lineHeightLabel}
          unitLabel=""
          value={Number.isFinite(parsedLineHeight) ? parsedLineHeight : 1.5}
          min={-1}
          max={3}
          step={0.05}
          presets={[
            { id: "tight", label: m.lineHeightPresetTight, value: 1.25 },
            { id: "snug", label: m.lineHeightPresetSnug, value: 1.35 },
            { id: "normal", label: m.lineHeightPresetNormal, value: 1.5 },
            { id: "relaxed", label: m.lineHeightPresetRelaxed, value: 1.7 },
            { id: "loose", label: m.lineHeightPresetLoose, value: 1.9 },
          ]}
          onChangeValue={(v) => {
            const preset: Record<NonNullable<TextBlockProps["lineHeightScale"]>, number> = {
              tight: 1.25,
              snug: 1.35,
              normal: 1.5,
              relaxed: 1.7,
              loose: 1.9,
            };

            const entries = Object.entries(preset) as [
              NonNullable<TextBlockProps["lineHeightScale"]>,
              number,
            ][];
            const closest = entries.reduce(
              (best, [scale, value]) => {
                const dist = Math.abs(value - v);
                if (dist < best.dist) return { scale, dist };
                return best;
              },
              { scale: lineHeightScale as NonNullable<TextBlockProps["lineHeightScale"]>, dist: Infinity },
            ).scale;

            updateBlock(selectedBlockId, {
              lineHeightScale: closest,
              lineHeightCustom: v.toString(),
              lineHeightMode: "custom",
            } as any);
          }}
        />
      </div>

      <div className="space-y-1">
        <NumericPropertyControl
          label={m.fontWeightLabel}
          value={Number.isFinite(parsedFontWeight) ? parsedFontWeight : 400}
          min={100}
          max={900}
          step={100}
          presets={(
            ["normal", "medium", "semibold", "bold"] as NonNullable<
              TextBlockProps["fontWeightScale"]
            >[]
          ).map((scale) => ({
            id: scale,
            label:
              scale === "normal"
                ? m.fontWeightPresetNormal
                : scale === "medium"
                ? m.fontWeightPresetMedium
                : scale === "semibold"
                ? m.fontWeightPresetSemibold
                : m.fontWeightPresetBold,
            value:
              scale === "normal"
                ? 400
                : scale === "medium"
                ? 500
                : scale === "semibold"
                ? 600
                : 700,
          }))}
          onChangeValue={(v) => {
            const preset: Record<NonNullable<TextBlockProps["fontWeightScale"]>, number> = {
              normal: 400,
              medium: 500,
              semibold: 600,
              bold: 700,
            };

            const entries = Object.entries(preset) as [
              NonNullable<TextBlockProps["fontWeightScale"]>,
              number,
            ][];
            const closest = entries.reduce(
              (best, [scale, value]) => {
                const dist = Math.abs(value - v);
                if (dist < best.dist) return { scale, dist };
                return best;
              },
              { scale: fontWeightScale as NonNullable<TextBlockProps["fontWeightScale"]>, dist: Infinity },
            ).scale;

            updateBlock(selectedBlockId, {
              fontWeightScale: closest,
              fontWeightCustom: v.toString(),
              fontWeightMode: "custom",
            } as any);
          }}
        />
      </div>

      <div className="space-y-1">
        <ColorPickerField
          label={m.textColorLabel}
          ariaLabelColorInput={m.textColorPickerAria}
          ariaLabelHexInput={m.textColorHexAria}
          value={
            (textProps.colorCustom && textProps.colorCustom.startsWith("#")
              ? textProps.colorCustom
              : TEXT_COLOR_PALETTE.find((p) => p.id === colorPalette)?.color ?? "#ffffff")
          }
          onChange={(hex) => {
            updateBlock(selectedBlockId, {
              colorCustom: hex,
              colorMode: "custom",
            } as any);
          }}
          palette={TEXT_COLOR_PALETTE}
          selectedPaletteId={colorPalette}
          onPaletteSelect={(item) => {
            const paletteId = item.id as NonNullable<TextBlockProps["colorPalette"]>;
            updateBlock(selectedBlockId, {
              colorPalette: paletteId,
              colorCustom: item.color,
              colorMode: "custom",
            } as any);
          }}
        />
      </div>

      <div className="space-y-1">
        <ColorPickerField
          label={m.backgroundColorLabel}
          ariaLabelColorInput={m.backgroundColorPickerAria}
          ariaLabelHexInput={m.backgroundColorHexAria}
          value={textProps.backgroundColorCustom ?? ""}
          onChange={(hex) => {
            updateBlock(selectedBlockId, {
              backgroundColorCustom: hex,
            } as any);
          }}
          palette={TEXT_COLOR_PALETTE}
        />
      </div>

      <div className="space-y-1">
        <label className="flex flex-col gap-1 text-xs text-slate-400">
          <span>{m.maxWidthLabel}</span>
          <div className="flex items-center gap-2">
            <select
              className="w-32 rounded border border-slate-300 bg-white px-2 py-1 text-[11px] text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
              aria-label={m.maxWidthPresetAria}
              value={maxWidthScale}
              onChange={(e) => {
                const value = e.target.value as NonNullable<TextBlockProps["maxWidthScale"]>;
                let custom = textProps.maxWidthCustom ?? "";
                if (value === "prose") custom = "60ch";
                else if (value === "narrow") custom = "40ch";
                else custom = "";
                updateBlock(selectedBlockId, {
                  maxWidthScale: value,
                  maxWidthMode: custom ? "custom" : "scale",
                  maxWidthCustom: custom,
                } as any);
              }}
            >
              <option value="none">{m.maxWidthPresetNone}</option>
              <option value="prose">{m.maxWidthPresetProse}</option>
              <option value="narrow">{m.maxWidthPresetNarrow}</option>
            </select>
            <input
              type="range"
              min={20}
              max={120}
              step={5}
              aria-label={m.maxWidthSliderAria}
              value={(() => {
                const raw = textProps.maxWidthCustom ?? "";
                const match = raw.match(/([0-9]+)ch/);
                if (match) return Number(match[1]);
                if (maxWidthScale === "prose") return 60;
                if (maxWidthScale === "narrow") return 40;
                return 120;
              })()}
              onChange={(e) => {
                const ch = Number(e.target.value || 60);
                updateBlock(selectedBlockId, {
                  maxWidthCustom: `${ch}ch`,
                  maxWidthMode: "custom",
                } as any);
              }}
              className="flex-1"
            />
          </div>
          <input
            className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
            aria-label={m.maxWidthCustomAria}
            placeholder={m.maxWidthPlaceholder}
            value={textProps.maxWidthCustom ?? ""}
            onChange={(e) => {
              updateBlock(selectedBlockId, {
                maxWidthCustom: e.target.value,
                maxWidthMode: "custom",
              } as any);
            }}
          />
        </label>
      </div>
    </>
  );
}
