/**
 * ShapeProperties — property editor for a single selected shape.
 *
 * Shows stroke color, stroke weight, fill color (with toggle), dimensions.
 * For line objects: line style dropdown (solid, dashed, dotted).
 * For text objects: text content, font family, font size, letter spacing,
 * line height, and a "Convert to Paths" action button.
 */

import { useCallback, useState } from 'react';
import type { CanvasObject, ImageObject, LineStyle, TextObject } from '../../types/canvas';
import { getAvailableFonts, textToPathData } from '../../utils/fontService';

// -- Helpers ------------------------------------------------------------------

function getWidth(obj: CanvasObject): number {
  switch (obj.type) {
    case 'rect':
    case 'image':
      return Math.round(obj.width * 100) / 100;
    case 'circle':
      return Math.round(obj.radius * 2 * 100) / 100;
    case 'ellipse':
      return Math.round(obj.radiusX * 2 * 100) / 100;
    case 'line': {
      const xs = obj.points.filter((_, i) => i % 2 === 0);
      return Math.round((Math.max(...xs) - Math.min(...xs)) * 100) / 100;
    }
    case 'text':
      return Math.round(obj.width * 100) / 100;
  }
}

function getHeight(obj: CanvasObject): number {
  switch (obj.type) {
    case 'rect':
    case 'image':
      return Math.round(obj.height * 100) / 100;
    case 'circle':
      return Math.round(obj.radius * 2 * 100) / 100;
    case 'ellipse':
      return Math.round(obj.radiusY * 2 * 100) / 100;
    case 'line': {
      const ys = obj.points.filter((_, i) => i % 2 === 1);
      return Math.round((Math.max(...ys) - Math.min(...ys)) * 100) / 100;
    }
    case 'text':
      return Math.round(obj.fontSize * obj.lineHeight * 100) / 100;
  }
}

const DASH_PRESETS: Record<LineStyle, number[]> = {
  solid: [],
  dashed: [10, 5],
  dotted: [2, 4],
};

// -- Props --------------------------------------------------------------------

export interface ShapePropertiesProps {
  object: CanvasObject;
  onUpdate: (id: string, changes: Partial<CanvasObject>) => void;
  /** Called to replace a text object with an image object (convert-to-paths). */
  onConvertToPath?: (textObjectId: string, imageObject: ImageObject) => void;
}

// -- Component ----------------------------------------------------------------

export default function ShapeProperties({
  object,
  onUpdate,
  onConvertToPath,
}: ShapePropertiesProps) {
  const hasStroke = object.type !== 'image';
  const hasFill = object.type === 'rect' || object.type === 'circle' || object.type === 'ellipse' || object.type === 'text';
  const isLine = object.type === 'line';
  const isText = object.type === 'text';

  const [converting, setConverting] = useState(false);

  const handleChange = useCallback(
    (changes: Partial<CanvasObject>) => {
      onUpdate(object.id, changes);
    },
    [object.id, onUpdate],
  );

  /** Convert the current text object to an SVG image via fontService. */
  const handleConvertToPath = useCallback(async () => {
    if (object.type !== 'text' || !onConvertToPath) return;

    const confirmed = window.confirm(
      'Convert text to paths? This replaces the editable text with a vector image and cannot be undone.',
    );
    if (!confirmed) return;

    const textObj = object as TextObject;
    setConverting(true);

    try {
      const result = await textToPathData(
        textObj.text,
        textObj.fontFamily,
        textObj.fontSize,
        textObj.letterSpacing,
      );

      // Build an SVG string from the path data
      const svgString = [
        `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${result.width} ${result.height}" width="${result.width}" height="${result.height}">`,
        `<path d="${result.pathData}" fill="${textObj.fill}" stroke="${textObj.stroke}" stroke-width="${textObj.strokeWidth}"/>`,
        `</svg>`,
      ].join('');

      // Create a Blob URL for the SVG
      const blob = new Blob([svgString], { type: 'image/svg+xml' });
      const src = URL.createObjectURL(blob);

      const imageObj: ImageObject = {
        type: 'image',
        id: `path-${Date.now()}`,
        name: `${textObj.name} (paths)`,
        x: textObj.x,
        y: textObj.y,
        width: result.width,
        height: result.height,
        rotation: textObj.rotation,
        visible: textObj.visible,
        locked: textObj.locked,
        opacity: textObj.opacity,
        src,
      };

      onConvertToPath(textObj.id, imageObj);
    } catch (err) {
      console.error('Convert to paths failed:', err);
    } finally {
      setConverting(false);
    }
  }, [object, onConvertToPath]);

  return (
    <div className="shape-properties" data-testid="shape-properties">
      <div className="shape-properties-header">Properties</div>

      {/* Dimensions (read-only display) */}
      <div className="shape-prop-section">
        <div className="shape-prop-label">Dimensions</div>
        <div className="shape-prop-dims" data-testid="shape-dims">
          <span>W: {getWidth(object)}</span>
          <span>H: {getHeight(object)}</span>
        </div>
      </div>

      {/* Position */}
      <div className="shape-prop-section">
        <div className="shape-prop-label">Position</div>
        <div className="shape-prop-dims">
          <span>X: {Math.round(object.x * 100) / 100}</span>
          <span>Y: {Math.round(object.y * 100) / 100}</span>
        </div>
      </div>

      {/* ---- Text-specific controls ---- */}
      {isText && object.type === 'text' && (
        <>
          {/* Text content */}
          <div className="shape-prop-section">
            <label className="shape-prop-label" htmlFor="text-content">
              Text
            </label>
            <textarea
              id="text-content"
              className="shape-prop-textarea"
              rows={3}
              value={object.text}
              onChange={(e) =>
                handleChange({ text: e.target.value } as Partial<CanvasObject>)
              }
              data-testid="text-content-input"
            />
          </div>

          {/* Font family */}
          <div className="shape-prop-section">
            <label className="shape-prop-label" htmlFor="font-family">
              Font Family
            </label>
            <select
              id="font-family"
              className="shape-prop-select"
              value={object.fontFamily}
              onChange={(e) =>
                handleChange({ fontFamily: e.target.value } as Partial<CanvasObject>)
              }
              data-testid="font-family-select"
            >
              {getAvailableFonts().map((f) => (
                <option key={f.family} value={f.family}>
                  {f.family}
                </option>
              ))}
            </select>
          </div>

          {/* Font size */}
          <div className="shape-prop-section">
            <label className="shape-prop-label" htmlFor="font-size">
              Font Size
            </label>
            <input
              id="font-size"
              type="number"
              className="shape-prop-number-input"
              min={8}
              max={200}
              step={1}
              value={object.fontSize}
              onChange={(e) =>
                handleChange({ fontSize: Number(e.target.value) } as Partial<CanvasObject>)
              }
              data-testid="font-size-input"
            />
          </div>

          {/* Letter spacing */}
          <div className="shape-prop-section">
            <label className="shape-prop-label" htmlFor="letter-spacing">
              Letter Spacing
            </label>
            <input
              id="letter-spacing"
              type="number"
              className="shape-prop-number-input"
              min={-5}
              max={20}
              step={0.5}
              value={object.letterSpacing}
              onChange={(e) =>
                handleChange({ letterSpacing: Number(e.target.value) } as Partial<CanvasObject>)
              }
              data-testid="letter-spacing-input"
            />
          </div>

          {/* Line height */}
          <div className="shape-prop-section">
            <label className="shape-prop-label" htmlFor="line-height">
              Line Height
            </label>
            <input
              id="line-height"
              type="number"
              className="shape-prop-number-input"
              min={0.5}
              max={3}
              step={0.1}
              value={object.lineHeight}
              onChange={(e) =>
                handleChange({ lineHeight: Number(e.target.value) } as Partial<CanvasObject>)
              }
              data-testid="line-height-input"
            />
          </div>
        </>
      )}

      {/* Stroke color */}
      {hasStroke && (
        <div className="shape-prop-section">
          <label className="shape-prop-label" htmlFor="stroke-color">
            Stroke Color
          </label>
          <input
            id="stroke-color"
            type="color"
            className="shape-prop-color-input"
            value={(object as { stroke?: string }).stroke ?? '#000000'}
            onChange={(e) => handleChange({ stroke: e.target.value } as Partial<CanvasObject>)}
            data-testid="stroke-color-input"
          />
        </div>
      )}

      {/* Stroke weight */}
      {hasStroke && (
        <div className="shape-prop-section">
          <label className="shape-prop-label" htmlFor="stroke-width">
            Stroke Weight
          </label>
          <input
            id="stroke-width"
            type="number"
            className="shape-prop-number-input"
            min={0}
            max={50}
            step={0.5}
            value={(object as { strokeWidth?: number }).strokeWidth ?? 2}
            onChange={(e) =>
              handleChange({ strokeWidth: Number(e.target.value) } as Partial<CanvasObject>)
            }
            data-testid="stroke-width-input"
          />
        </div>
      )}

      {/* Fill color */}
      {hasFill && (
        <div className="shape-prop-section">
          <label className="shape-prop-label" htmlFor="fill-color">
            Fill Color
          </label>
          <div className="shape-prop-fill-row">
            <input
              id="fill-color"
              type="color"
              className="shape-prop-color-input"
              value={
                (object as { fill?: string }).fill === 'transparent'
                  ? '#ffffff'
                  : ((object as { fill?: string }).fill ?? '#ffffff')
              }
              onChange={(e) => handleChange({ fill: e.target.value } as Partial<CanvasObject>)}
              data-testid="fill-color-input"
            />
            <label className="shape-prop-fill-toggle" title="Toggle fill">
              <input
                type="checkbox"
                checked={(object as { fill?: string }).fill !== 'transparent'}
                onChange={(e) =>
                  handleChange({
                    fill: e.target.checked
                      ? '#ffffff'
                      : 'transparent',
                  } as Partial<CanvasObject>)
                }
                data-testid="fill-toggle"
              />
              Fill
            </label>
          </div>
        </div>
      )}

      {/* Line style (only for line objects) */}
      {isLine && object.type === 'line' && (
        <div className="shape-prop-section">
          <label className="shape-prop-label" htmlFor="line-style">
            Line Style
          </label>
          <select
            id="line-style"
            className="shape-prop-select"
            value={object.lineStyle}
            onChange={(e) => {
              const style = e.target.value as LineStyle;
              handleChange({
                lineStyle: style,
                dash: DASH_PRESETS[style],
              } as Partial<CanvasObject>);
            }}
            data-testid="line-style-select"
          >
            <option value="solid">Solid</option>
            <option value="dashed">Dashed</option>
            <option value="dotted">Dotted</option>
          </select>
        </div>
      )}

      {/* Opacity */}
      <div className="shape-prop-section">
        <label className="shape-prop-label" htmlFor="opacity">
          Opacity
        </label>
        <input
          id="opacity"
          type="range"
          className="shape-prop-range-input"
          min={0}
          max={1}
          step={0.05}
          value={object.opacity}
          onChange={(e) => handleChange({ opacity: Number(e.target.value) })}
          data-testid="opacity-input"
        />
        <span className="shape-prop-range-value">
          {Math.round(object.opacity * 100)}%
        </span>
      </div>

      {/* Rotation */}
      <div className="shape-prop-section">
        <label className="shape-prop-label" htmlFor="rotation">
          Rotation
        </label>
        <div className="shape-prop-dims">
          <span>{Math.round(object.rotation)}°</span>
        </div>
      </div>

      {/* Convert to Paths (text objects only) */}
      {isText && onConvertToPath && (
        <div className="shape-prop-section">
          <button
            type="button"
            className="shape-prop-convert-btn"
            onClick={handleConvertToPath}
            disabled={converting}
            data-testid="convert-to-paths-btn"
          >
            {converting ? 'Converting…' : 'Convert to Paths'}
          </button>
        </div>
      )}
    </div>
  );
}
