import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { IconLibraryPicker } from "../../../app/IconLibraryPicker";
import { ariaSelected } from "../../../lib/aria";
import { technicalInputProps } from "../../../lib/inputBehavior";
import { invokeCommand, isTauriRuntime } from "../../../lib/tauri";
import { ToggleSwitch } from "../../settings/ToggleSwitch";
import { useDashboardStore } from "../state/dashboardStore";
import { parseNotesSettingsJson } from "../widgets/builtin/notes/NotesWidget";
import { ACCENT_PALETTE } from "../registry/palette";
import { ColorPalettePicker, isHexColor } from "../../../app/ui/ColorPalettePicker";
import { parseScriptBodyForEditor, updateScriptBodySourceJson } from "./scriptSourceEditor";
import {
  dashboardWidgetSecretOwnerId,
  isWidgetSecretRef,
  parseWidgetSettingsValuesJson,
  settingsValuesWithDefaults,
  validateWidgetSettingsSchemaJson,
} from "../schema";
import type {
  AccentName, DashboardWidgetInstance, IconName,
  WidgetSettingsField, WidgetSettingsSchema,
} from "../types";
import {
  defaultBodyOpacityForInstance,
  defaultWidgetPresentationForPreset,
  effectiveBodyOpacity,
  ICON_NAMES,
  WIDGET_PRESETS,
} from "../types";

export interface CustomizePopoverProps {
  instance: DashboardWidgetInstance;
  anchorRect: DOMRect;
  onClose: () => void;
}

type SectionKey = "common" | "widget" | "advanced";

const POPOVER_MARGIN = 8;
const POPOVER_WIDTH = 440;
const APP_LAUNCHER_SETTINGS_SCHEMA: WidgetSettingsSchema = {
  fields: [
    {
      key: "showFileExtensions",
      type: "boolean",
      label: "appLauncher.showFileExtensions",
      defaultValue: false,
    },
  ],
};

const NOTES_SETTINGS_SCHEMA: WidgetSettingsSchema = {
  fields: [
    {
      key: "rotationDegrees",
      type: "number",
      label: "dashboard.notesRotationDegrees",
      min: -3,
      max: 3,
      step: 0.1,
      defaultValue: -0.6,
    },
    {
      key: "foldDepth",
      type: "number",
      label: "dashboard.notesFoldDepth",
      min: 0,
      max: 1,
      step: 0.05,
      defaultValue: 0.5,
    },
    {
      key: "foldCorner",
      type: "select",
      label: "dashboard.notesFoldCorner",
      defaultValue: "topRight",
      options: [
        { label: "dashboard.notesFoldCornerOption.topRight", value: "topRight" },
        { label: "dashboard.notesFoldCornerOption.topLeft", value: "topLeft" },
        { label: "dashboard.notesFoldCornerOption.bottomRight", value: "bottomRight" },
        { label: "dashboard.notesFoldCornerOption.bottomLeft", value: "bottomLeft" },
      ],
    },
    {
      key: "markdownEnabled",
      type: "boolean",
      label: "dashboard.notesMarkdownEnabled",
      defaultValue: true,
    },
  ],
};

export function CustomizePopover({ instance, anchorRect, onClose }: CustomizePopoverProps) {
  const { t } = useTranslation();
  const customWidgets = useDashboardStore((s) => s.customWidgets);
  const ref = useRef<HTMLDivElement | null>(null);
  const [position, setPosition] = useState<{ top: number; left: number; visible: boolean }>({
    top: anchorRect.bottom + 6,
    left: anchorRect.left,
    visible: false,
  });
  const [section, setSection] = useState<SectionKey>("common");

  const customSource =
    instance.kind !== "builtIn" ? customWidgets.find((c) => c.id === instance.sourceId) : undefined;
  const settingsSchema = useMemo(() => {
    if (customSource) {
      return parseSettingsSchema(customSource.settingsSchemaJson);
    }
    if (instance.kind === "builtIn" && instance.sourceId === "appLauncher") {
      return APP_LAUNCHER_SETTINGS_SCHEMA;
    }
    if (instance.kind === "builtIn" && instance.sourceId === "notes") {
      return NOTES_SETTINGS_SCHEMA;
    }
    return null;
  }, [customSource, instance.kind, instance.sourceId]);
  const hasWidgetSettings = settingsSchema !== null;
  const hasAdvanced = instance.kind !== "builtIn";

  const sections = useMemo(() => {
    const list: { key: SectionKey; label: string }[] = [
      { key: "common", label: t("dashboard.customizeSectionCommon") },
    ];
    if (hasWidgetSettings) {
      list.push({ key: "widget", label: t("dashboard.customizeSectionWidget") });
    }
    if (hasAdvanced) {
      list.push({ key: "advanced", label: t("dashboard.advanced") });
    }
    return list;
  }, [hasWidgetSettings, hasAdvanced, t]);

  useEffect(() => {
    if (!sections.some((s) => s.key === section)) {
      setSection("common");
    }
  }, [section, sections]);

  useEffect(() => {
    function onDoc(e: MouseEvent) {
      if (ref.current && !ref.current.contains(e.target as Node)) onClose();
    }
    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") onClose();
    }
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDoc);
      document.removeEventListener("keydown", onKey);
    };
  }, [onClose]);

  useLayoutEffect(() => {
    const node = ref.current;
    if (!node) return;
    const rect = node.getBoundingClientRect();
    const height = rect.height;
    const width = rect.width;
    const below = anchorRect.bottom + 6;
    const above = anchorRect.top - height - 6;
    const fitsBelow = below + height + POPOVER_MARGIN <= window.innerHeight;
    const top = fitsBelow ? below : Math.max(POPOVER_MARGIN, above);
    let left = Math.min(anchorRect.left, window.innerWidth - width - POPOVER_MARGIN);
    left = Math.max(POPOVER_MARGIN, left);
    setPosition({ top, left, visible: true });
  }, [anchorRect, section]);

  return (
    <>
      <div className="dw-customize-dismiss-layer" aria-hidden="true" onMouseDown={onClose} />
      <div
        ref={ref}
        className="dw-customize"
        style={{
          top: position.top,
          left: position.left,
          width: POPOVER_WIDTH,
          visibility: position.visible ? "visible" : "hidden",
        }}
      >
        <nav className="dw-customize-tabs" role="tablist" aria-label={t("dashboard.customize")}>
          {sections.map((s) => (
            <button
              key={s.key}
              type="button"
              role="tab"
              {...ariaSelected(section === s.key)}
              className={section === s.key ? "active" : ""}
              onClick={() => setSection(s.key)}
            >
              {s.label}
            </button>
          ))}
        </nav>
        <div className="dw-customize-pane" role="tabpanel">
          {section === "common" ? <CommonSection instance={instance} /> : null}
          {section === "widget" ? (
            <WidgetSection schema={settingsSchema} instance={instance} />
          ) : null}
          {section === "advanced" ? <AdvancedSection instance={instance} /> : null}
        </div>
      </div>
    </>
  );
}

function CommonSection({ instance }: { instance: DashboardWidgetInstance }) {
  const { t } = useTranslation();
  const updateInstance = useDashboardStore((s) => s.updateInstance);

  return (
    <>
      <section>
        <h4>{t("dashboard.titleLabel")}</h4>
        <input
          defaultValue={instance.customTitle ?? ""}
          placeholder={t("dashboard.titlePlaceholder")}
          onBlur={(e) => {
            const value = e.target.value.trim();
            updateInstance(instance.id, { customTitle: value.length === 0 ? null : value });
          }}
        />
      </section>

      <section>
        <h4>{t("dashboard.presetLabel")}</h4>
        <div className="dw-preset-picker">
          {WIDGET_PRESETS.map((p) => (
            <button
              key={p}
              className={instance.preset === p ? "active" : ""}
              onClick={() => updateInstance(instance.id, {
                preset: p,
                ...defaultWidgetPresentationForPreset(p),
              })}
            >
              {t(`dashboard.preset.${p}`)}
            </button>
          ))}
        </div>
      </section>

      {instance.preset === "ambient" ? (
        <section>
          <label className="dw-field dw-field-row">
            <span>{t("dashboard.glassBackground")}</span>
            <ToggleSwitch
              checked={instance.glass === true}
              onChange={(checked) => updateInstance(instance.id, { glass: checked })}
            />
          </label>
          <label className="dw-field dw-field-row">
            <span>{t("dashboard.hideTitle")}</span>
            <ToggleSwitch
              checked={instance.hideTitle === true}
              onChange={(checked) => updateInstance(instance.id, { hideTitle: checked })}
            />
          </label>
        </section>
      ) : null}

      <CanvasOpacityField instance={instance} />


      <section>
        <h4>{t("dashboard.accent")}</h4>
        <div className="dw-accent-picker">
          {ACCENT_PALETTE.map((p) => {
            const label = p.name === "default" ? t("dashboard.accentDefault") : p.name;
            return (
              <button
                key={p.name}
                className={instance.accentName === p.name ? "active" : ""}
                style={{ background: p.color }}
                title={label}
                aria-label={label}
                onClick={() => updateInstance(instance.id, { accentName: p.name as AccentName })}
              />
            );
          })}
          <ColorPalettePicker
            className="dw-custom-color-picker"
            onChange={(accentName) => updateInstance(instance.id, { accentName })}
            value={isHexColor(instance.accentName) ? instance.accentName : null}
          />
        </div>
      </section>

      <section>
        <h4>{t("dashboard.icon")}</h4>
        <IconLibraryPicker
          className="dw-icon-library-picker"
          iconNames={ICON_NAMES}
          onSelect={(iconName) => {
            if (iconName) {
              updateInstance(instance.id, { iconName: iconName as IconName });
            }
          }}
          value={instance.iconName}
        />
      </section>
    </>
  );
}

function CanvasOpacityField({ instance }: { instance: DashboardWidgetInstance }) {
  const { t } = useTranslation();
  const updateInstance = useDashboardStore((s) => s.updateInstance);
  const value = effectiveBodyOpacity(instance);
  const fallback = defaultBodyOpacityForInstance(instance);

  return (
    <section>
      <label className="dw-field">
        <span>
          {t("dashboard.canvasOpacity")} <span className="dw-muted">({value}%)</span>
        </span>
        <input
          type="range"
          min={0}
          max={100}
          step={1}
          value={value}
          onChange={(e) => {
            const next = Number(e.currentTarget.value);
            void updateInstance(instance.id, {
              bodyOpacity: next === fallback ? null : next,
            });
          }}
        />
      </label>
    </section>
  );
}

function WidgetSection({
  schema,
  instance,
}: {
  schema: WidgetSettingsSchema | null;
  instance: DashboardWidgetInstance;
}) {
  const { t } = useTranslation();
  const updateInstance = useDashboardStore((s) => s.updateInstance);
  const settingsValues = useMemo(
    () => (schema ? parseSettingsValues(schema, instance.settingsValuesJson) : {}),
    [schema, instance.settingsValuesJson],
  );

  if (!schema) {
    return (
      <section>
        <h4>{t("dashboard.widgetSettings")}</h4>
        <p className="dw-muted">{t("dashboard.widgetSettingsInvalid")}</p>
      </section>
    );
  }

  return (
    <section>
      <h4>{t("dashboard.widgetSettings")}</h4>
      {schema.fields.length > 0 ? (
        <WidgetSettingsFields
          schema={schema}
          values={settingsValues}
          instanceId={instance.id}
          onChange={(key, value) => {
            const parsed = parseWidgetSettingsValuesJson(instance.settingsValuesJson);
            const base = parsed.ok ? parsed.value : {};
            const next = { ...base, [key]: value };
            const normalized = instance.kind === "builtIn" && instance.sourceId === "notes"
              ? parseNotesSettingsJson(JSON.stringify(next))
              : next;
            void updateInstance(instance.id, { settingsValuesJson: JSON.stringify(normalized) });
          }}
        />
      ) : (
        <p className="dw-muted">{t("dashboard.widgetSettingsEmpty")}</p>
      )}
    </section>
  );
}

function AdvancedSection({ instance }: { instance: DashboardWidgetInstance }) {
  const customWidgets = useDashboardStore((s) => s.customWidgets);
  const updateCustomWidget = useDashboardStore((s) => s.updateCustomWidget);
  const customSource =
    instance.kind !== "builtIn" ? customWidgets.find((c) => c.id === instance.sourceId) : undefined;

  if (!customSource) return null;

  if (instance.kind === "script") {
    return (
      <ScriptAdvanced
        bodyJson={customSource.bodyJson}
        onUpdate={async (next) => {
          await updateCustomWidget(customSource.id, { bodyJson: next });
          const saved = useDashboardStore
            .getState()
            .customWidgets
            .find((widget) => widget.id === customSource.id);
          if (saved?.bodyJson !== next) {
            throw new Error(useDashboardStore.getState().lastError ?? "");
          }
        }}
      />
    );
  }
  return null;
}

function parseSettingsSchema(settingsSchemaJson: string): WidgetSettingsSchema | null {
  const parsed = validateWidgetSettingsSchemaJson(settingsSchemaJson);
  return parsed.ok ? parsed.value : null;
}

function parseSettingsValues(
  schema: WidgetSettingsSchema,
  settingsValuesJson: string,
): Record<string, unknown> {
  const parsed = parseWidgetSettingsValuesJson(settingsValuesJson);
  return settingsValuesWithDefaults(schema, parsed.ok ? parsed.value : {});
}

function WidgetSettingsFields({
  schema,
  values,
  instanceId,
  onChange,
}: {
  schema: WidgetSettingsSchema;
  values: Record<string, unknown>;
  instanceId: string;
  onChange: (key: string, value: unknown) => void;
}) {
  return (
    <div className="dw-stack-fields">
      {schema.fields.map((field) => (
        <WidgetSettingsFieldControl
          field={field}
          key={field.key}
          value={values[field.key]}
          instanceId={instanceId}
          onChange={(value) => onChange(field.key, value)}
        />
      ))}
    </div>
  );
}

function WidgetSettingsFieldControl({
  field,
  value,
  instanceId,
  onChange,
}: {
  field: WidgetSettingsField;
  value: unknown;
  instanceId: string;
  onChange: (value: unknown) => void;
}) {
  const { t } = useTranslation();
  const [secretDraft, setSecretDraft] = useState("");
  const [secretError, setSecretError] = useState("");

  const label = field.label.includes(".") ? t(field.label) : field.label;

  if (field.type === "boolean") {
    return (
      <label className="dw-field dw-field-row">
        <span>{label}</span>
        <ToggleSwitch
          checked={value === true}
          onChange={(checked) => onChange(checked)}
        />
      </label>
    );
  }

  if (field.type === "select") {
    return (
      <label className="dw-field">
        <span>{label}</span>
        <select value={typeof value === "string" ? value : ""} onChange={(event) => onChange(event.target.value)}>
          {field.options.map((option) => (
            <option key={option.value} value={option.value}>
              {option.label.includes(".") ? t(option.label) : option.label}
            </option>
          ))}
        </select>
      </label>
    );
  }

  if (field.type === "number") {
    return (
      <label className="dw-field">
        <span>{label}</span>
        <input
          type="number"
          min={field.min}
          max={field.max}
          step={field.step ?? 1}
          value={typeof value === "number" || value === "" ? value : ""}
          onChange={(event) => onChange(event.target.value === "" ? "" : Number(event.target.value))}
        />
      </label>
    );
  }

  if (field.type === "secret") {
    const secretRef = isWidgetSecretRef(value) ? value : null;
    const ownerId = dashboardWidgetSecretOwnerId(instanceId, field.key);

    async function saveSecret() {
      const secret = secretDraft.trim();
      if (!secret) return;
      setSecretError("");
      try {
        if (isTauriRuntime()) {
          await invokeCommand("store_secret", {
            request: { kind: "widgetSecret", ownerId, secret },
          });
        }
        onChange({
          type: "secretRef",
          ownerId,
          hasSecret: true,
          updatedAt: new Date().toISOString(),
        });
        setSecretDraft("");
      } catch (error) {
        setSecretError(error instanceof Error ? error.message : String(error));
      }
    }

    async function clearSecret() {
      setSecretError("");
      try {
        if (isTauriRuntime()) {
          await invokeCommand("delete_secret", {
            request: { kind: "widgetSecret", ownerId },
          });
        }
        onChange(null);
        setSecretDraft("");
      } catch (error) {
        setSecretError(error instanceof Error ? error.message : String(error));
      }
    }

    return (
      <label className="dw-field">
        <span>{label}</span>
        {secretRef ? <small className="dw-muted">{t("dashboard.secretStored")}</small> : null}
        <input
          type="password"
          placeholder={field.placeholder ?? t("dashboard.secretPlaceholder")}
          value={secretDraft}
          onChange={(event) => setSecretDraft(event.target.value)}
          onBlur={() => { void saveSecret(); }}
          onKeyDown={(event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              void saveSecret();
            }
          }}
        />
        {secretRef ? (
          <button type="button" className="dw-secondary-button" onClick={() => { void clearSecret(); }}>
            {t("dashboard.secretClear")}
          </button>
        ) : null}
        {secretError ? <small className="dw-muted">{secretError}</small> : null}
      </label>
    );
  }

  return (
    <label className="dw-field">
      <span>{label}</span>
      <input
        type="text"
        placeholder={field.placeholder}
        value={typeof value === "string" ? value : ""}
        onChange={(event) => onChange(event.target.value)}
      />
    </label>
  );
}

function ScriptAdvanced({
  bodyJson,
  onUpdate,
}: {
  bodyJson: string;
  onUpdate: (next: string) => Promise<void>;
}) {
  const { t } = useTranslation();
  const parsed = useMemo(() => parseScriptBodyForEditor(bodyJson), [bodyJson]);
  const [isEditingSource, setIsEditingSource] = useState(false);
  const [sourceDraft, setSourceDraft] = useState("");
  const [sourceSaveError, setSourceSaveError] = useState("");

  useEffect(() => {
    setIsEditingSource(false);
    setSourceDraft(parsed?.source ?? "");
    setSourceSaveError("");
  }, [bodyJson, parsed?.source]);

  if (!parsed) {
    return <p className="dw-muted">{t("dashboard.scriptInvalidBody")}</p>;
  }

  async function saveSource() {
    setSourceSaveError("");
    try {
      await onUpdate(updateScriptBodySourceJson(bodyJson, sourceDraft));
      setIsEditingSource(false);
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      setSourceSaveError(message || t("dashboard.invalidScriptWidgetBody"));
    }
  }

  function updateBodyJson(next: string) {
    setSourceSaveError("");
    void onUpdate(next).catch((error) => {
      const message = error instanceof Error ? error.message : String(error);
      setSourceSaveError(message || t("dashboard.invalidScriptWidgetBody"));
    });
  }

  return (
    <div className="dw-stack-fields">
      <label className="dw-field dw-field-row">
        <span>{t("dashboard.scriptNetwork")}</span>
        <ToggleSwitch
          checked={parsed.permissions.network}
          onChange={(checked) => {
            const next = { ...parsed, permissions: { ...parsed.permissions, network: checked } };
            updateBodyJson(JSON.stringify(next));
          }}
        />
      </label>
      <label className="dw-field">
        <span>{t("dashboard.scriptPollSeconds")}</span>
        <input
          type="number"
          min={1}
          value={parsed.permissions.pollSeconds ?? ""}
          onChange={(e) => {
            const value = e.target.value === "" ? undefined : Number(e.target.value);
            const next = { ...parsed, permissions: { ...parsed.permissions, pollSeconds: value } };
            updateBodyJson(JSON.stringify(next));
          }}
        />
      </label>
      <details>
        <summary>{t("dashboard.scriptViewSource")}</summary>
        <div className="dw-source-toolbar">
          {isEditingSource ? (
            <>
              <button
                type="button"
                className="dw-secondary-button"
                onClick={() => { void saveSource(); }}
              >
                {t("common.save")}
              </button>
              <button
                type="button"
                className="dw-secondary-button"
                onClick={() => {
                  setSourceDraft(parsed.source);
                  setSourceSaveError("");
                  setIsEditingSource(false);
                }}
              >
                {t("common.cancel")}
              </button>
            </>
          ) : (
            <button
              type="button"
              className="dw-secondary-button"
              onClick={() => {
                setSourceDraft(parsed.source);
                setSourceSaveError("");
                setIsEditingSource(true);
              }}
            >
              {t("common.edit")}
            </button>
          )}
        </div>
        {isEditingSource ? (
          <textarea
            className="dw-source-editor"
            value={sourceDraft}
            {...technicalInputProps}
            onChange={(event) => setSourceDraft(event.target.value)}
          />
        ) : (
          <pre className="dw-source-view">{parsed.source}</pre>
        )}
        {sourceSaveError ? <p className="dw-muted">{sourceSaveError}</p> : null}
      </details>
    </div>
  );
}
