/**
 * Alpine.js Widget Components for Schema Forms
 *
 * Centralized widget component definitions for dynamic form rendering.
 * Each widget receives explicit parameters instead of context object for better traceability.
 */
document.addEventListener("alpine:init", () => {
  // Base widget factory with common functionality
  const baseWidget = (
    fieldName,
    data,
    schema,
    uiSchema,
    mode,
    debugErrors,
    context = null  // NEW: context for error namespacing
  ) => ({
    fieldName,
    data,
    schema,
    uiSchema,
    mode,
    debugErrors,
    context,  // Store context for use in templates

    // Field schema access
    get fieldSchema() {
      return this.schema?.properties?.[this.fieldName] || {};
    },

    // Common metadata
    get label() {
      // Check uiSchema first (RJSF format)
      if (this.uiSchema?.[this.fieldName]?.["ui:label"]) {
        return this.uiSchema[this.fieldName]["ui:label"];
      }
      // Fall back to schema title
      if (this.fieldSchema.title) {
        return this.fieldSchema.title;
      }
      // Default: format field name
      return this.fieldName
        .replace(/_/g, " ")
        .replace(/\b\w/g, (c) => c.toUpperCase());
    },
    get helpText() {
      return this.fieldSchema.description || "";
    },

    // Field-level unit extraction from uiSchema (RJSF format)
    get unit() {
      return this.uiSchema?.[this.fieldName]?.["ui:unit"] || null;
    },

    // Mode checks
    get isViewMode() {
      return this.mode === "view";
    },
    get isEditMode() {
      return this.mode === "edit";
    },
    get isRequired() {
      return (this.schema.required || []).indexOf(this.fieldName) > -1
    }
  });

  // Text widget
  Alpine.data(
    "textWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
    }),
  );

  // Textarea widget
  Alpine.data(
    "textareaWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
    }),
  );

  // Number widget with unit support
  Alpine.data(
    "numberWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName];
      },
      set value(v) {
        this.data[this.fieldName] =
          v === "" || v === null ? null : parseFloat(v);
      },
      get hasValue() {
        return (
          this.value !== null && this.value !== undefined && this.value !== ""
        );
      },
      // Format value with unit for view mode
      get displayValue() {
        if (!this.hasValue) return "—";
        return this.unit ? `${this.value} ${this.unit}` : String(this.value);
      },
    }),
  );

  // Select widget
  Alpine.data(
    "selectWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
      get multiple() {
        return !!(this.fieldSchema.items && this.fieldSchema.items.enum);

      },
      get options() {
        if (this.fieldSchema.enum) {
          return this.fieldSchema.enum;
        } else if (this.fieldSchema.items && this.fieldSchema.items.enum) {
          return this.fieldSchema.items.enum;
        } else {
          return [];
        }
      },
    }),
  );

  // Checkbox widget
  Alpine.data(
    "checkboxWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get checked() {
        return !!this.data[this.fieldName];
      },
      set checked(v) {
        this.data[this.fieldName] = v;
      },
    }),
  );

  // Interval widget with unit support
  Alpine.data(
    "intervalWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get start() {
        return this.data[this.fieldName]?.start ?? null;
      },
      set start(v) {
        if (!this.data[this.fieldName]) this.data[this.fieldName] = {};
        this.data[this.fieldName].start =
          v === "" || v === null ? null : parseFloat(v);
      },
      get end() {
        return this.data[this.fieldName]?.end ?? null;
      },
      set end(v) {
        if (!this.data[this.fieldName]) this.data[this.fieldName] = {};
        this.data[this.fieldName].end =
          v === "" || v === null ? null : parseFloat(v);
      },
      // Format interval with unit for view mode
      get displayValue() {
        const s = this.start,
          e = this.end;
        const unitStr = this.unit ? ` ${this.unit}` : "";

        if (s !== null && e !== null) return `${s} – ${e}${unitStr}`;
        if (s !== null) return `≥ ${s}${unitStr}`;
        if (e !== null) return `≤ ${e}${unitStr}`;
        return "—";
      },

      get isSameValue() {
        return this.start !== null && this.start === this.end;
      },

      // Validation: start must be <= end (client-side)
      get hasValidationError() {
        if (this.isViewMode) return false;
        const s = this.start;
        const e = this.end;
        // Only validate if both values are provided
        if (
          s !== null &&
          e !== null &&
          typeof s === "number" &&
          typeof e === "number"
        ) {
          return s > e;
        }
        return false;
      },
    }),
  );

  // PubMed link widget
  Alpine.data(
    "pubmedWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
      get pubmedUrl() {
        return this.value
          ? `https://pubmed.ncbi.nlm.nih.gov/${this.value}`
          : null;
      },
    }),
  );

    // PubMed link widget
  Alpine.data(
    "doiWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
      get doiUrl() {
        return this.value
          ? `https://dx.doi.org/${this.value}`
          : null;
      },
    }),
  );

  // Compound link widget
  Alpine.data(
    "compoundWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      get value() {
        return this.data[this.fieldName] || "";
      },
      set value(v) {
        this.data[this.fieldName] = v;
      },
    }),
  );

  // TimeSeries table widget
  Alpine.data(
    "timeseriesTableWidget",
    (fieldName, data, schema, uiSchema, mode, debugErrors, context = null) => ({
      ...baseWidget(
        fieldName,
        data,
        schema,
        uiSchema,
        mode,
        debugErrors,
        context,
      ),

      chartInstance: null,

      // Getter/setter for measurements array
      get measurements() {
        return this.data[this.fieldName] || [];
      },
      set measurements(v) {
        this.data[this.fieldName] = v;
      },

      // Get description from sibling field
      get description() {
        return this.data?.description || "";
      },

      // Get method from sibling field
      get method() {
        return this.data?.method || "";
      },

      // Computed property for chart options
      get chartOptions() {
        return {
          measurements: this.measurements,
          xAxisLabel: this.data?.x_axis_label || "Time",
          yAxisLabel: this.data?.y_axis_label || "Value",
          xAxisUnit: this.data?.x_axis_unit || "",
          yAxisUnit: this.data?.y_axis_unit || "",
        };
      },

      // Add new measurement
      addMeasurement() {
        if (!this.data[this.fieldName]) {
          this.data[this.fieldName] = [];
        }
        this.data[this.fieldName].push({
          timestamp: null,
          value: null,
          error: null,
          note: "",
        });
      },

      // Remove measurement by index
      removeMeasurement(index) {
        if (
          this.data[this.fieldName] &&
          Array.isArray(this.data[this.fieldName])
        ) {
          this.data[this.fieldName].splice(index, 1);
        }
      },

      // Update specific measurement field
      updateMeasurement(index, field, value) {
        if (this.data[this.fieldName] && this.data[this.fieldName][index]) {
          if (field === "timestamp" || field === "value" || field === "error") {
            // Parse all numeric fields (timestamp is days as float)
            this.data[this.fieldName][index][field] =
              value === "" || value === null ? null : parseFloat(value);
          } else {
            // Store other fields as-is
            this.data[this.fieldName][index][field] = value;
          }
        }
      },

      // Format timestamp for display (timestamp is numeric days as float)
      formatTimestamp(timestamp) {
        return timestamp ?? "";
      },

      // Sort by timestamp (numeric days)
      sortByTimestamp() {
        if (
          this.data[this.fieldName] &&
          Array.isArray(this.data[this.fieldName])
        ) {
          this.data[this.fieldName].sort((a, b) => {
            const tsA = a.timestamp ?? Infinity;
            const tsB = b.timestamp ?? Infinity;
            return tsA - tsB;
          });
        }
      },

      // Chart lifecycle methods (delegates to TimeSeriesChart utility)
      initChart() {
        if (!this.isViewMode || !window.Chart || !window.TimeSeriesChart)
          return;

        const canvas = this.$refs?.chartCanvas;
        if (!canvas) return;

        this.destroyChart();

        if (this.measurements.length === 0) return;

        this.chartInstance = window.TimeSeriesChart.create(
          canvas,
          this.chartOptions,
        );
      },

      updateChart() {
        if (!this.chartInstance || !this.isViewMode) return;
        window.TimeSeriesChart.update(
          this.chartInstance,
          this.measurements,
          this.chartOptions,
        );
      },

      destroyChart() {
        if (this.chartInstance) {
          window.TimeSeriesChart.destroy(this.chartInstance);
          this.chartInstance = null;
        }
      },

      // Alpine lifecycle hooks
      init() {
        if (this.isViewMode && window.Chart) {
          // Use $nextTick to ensure DOM is ready
          this.$nextTick(() => {
            this.initChart();
          });

          // Watch measurements array for changes and update chart
          this.$watch("data." + this.fieldName, () => {
            if (this.chartInstance) {
              this.updateChart();
            }
          });
        }
      },
    }),
  );
});
