'use client';

import * as React from 'react';
import { cn } from '../../lib/utils';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import { StatusBadge } from '../badge/StatusBadge';
import { EmptyState } from '../states/EmptyState';
import { NODE_STATUS_BADGE } from './exec-status';
import type { JsonSchema, NodeExecView, NodeTypeDef, WfNode } from './types';

export interface NodeInspectorProps {
  /** The selected node, or null when nothing is selected. */
  node: WfNode | null | undefined;
  /** Node-type def supplying the parameter schema. */
  nodeType?: NodeTypeDef;
  /** Emitted with the node id + full next parameter object on any change. */
  onParametersChange?: (nodeId: string, parameters: Record<string, unknown>) => void;
  /** When present, adds read-only Input/Output exec tabs. */
  execution?: NodeExecView;
  className?: string;
}

function fieldId(nodeId: string, key: string): string {
  return `nodeparam-${nodeId}-${key}`;
}

/** Field label with a required-marker asterisk when the param is required. */
function FieldLabel({
  id,
  label,
  required,
}: {
  id: string;
  label: string;
  required?: boolean;
}) {
  return (
    <label htmlFor={id} className="text-xs font-medium text-foreground">
      {label}
      {required && (
        <span className="ml-0.5 text-destructive" aria-hidden="true">
          *
        </span>
      )}
    </label>
  );
}

function SchemaField({
  nodeId,
  name,
  schema,
  value,
  onChange,
  required,
}: {
  nodeId: string;
  name: string;
  schema: JsonSchema;
  value: unknown;
  onChange: (next: unknown) => void;
  required?: boolean;
}) {
  const id = fieldId(nodeId, name);
  const label = schema.title ?? name;

  if (schema.enum && schema.enum.length > 0) {
    return (
      <div className="flex flex-col gap-1">
        <FieldLabel id={id} label={label} required={required} />
        <select
          id={id}
          aria-required={required || undefined}
          value={String(value ?? '')}
          onChange={(e) => onChange(e.target.value)}
          className="rounded-md border border-border bg-background px-2 py-1.5 text-sm outline-none focus:ring-2 focus:ring-primary"
        >
          {schema.enum.map((opt) => (
            <option key={String(opt)} value={String(opt)}>
              {String(opt)}
            </option>
          ))}
        </select>
      </div>
    );
  }

  if (schema.type === 'boolean') {
    return (
      <div className="flex items-center gap-2">
        <input
          id={id}
          aria-required={required || undefined}
          type="checkbox"
          checked={Boolean(value)}
          onChange={(e) => onChange(e.target.checked)}
          className="h-4 w-4 rounded border-border"
        />
        <FieldLabel id={id} label={label} required={required} />
      </div>
    );
  }

  if (schema.type === 'number' || schema.type === 'integer') {
    return (
      <div className="flex flex-col gap-1">
        <FieldLabel id={id} label={label} required={required} />
        <input
          id={id}
          aria-required={required || undefined}
          type="number"
          value={value === undefined || value === null ? '' : Number(value)}
          onChange={(e) =>
            onChange(e.target.value === '' ? undefined : Number(e.target.value))
          }
          className="rounded-md border border-border bg-background px-2 py-1.5 text-sm outline-none focus:ring-2 focus:ring-primary"
        />
      </div>
    );
  }

  // default: string
  return (
    <div className="flex flex-col gap-1">
      <FieldLabel id={id} label={label} required={required} />
      <input
        id={id}
        aria-required={required || undefined}
        type="text"
        value={value === undefined || value === null ? '' : String(value)}
        onChange={(e) => onChange(e.target.value)}
        className="rounded-md border border-border bg-background px-2 py-1.5 text-sm outline-none focus:ring-2 focus:ring-primary"
      />
      {schema.description && (
        <p className="text-[11px] text-muted-foreground">{schema.description}</p>
      )}
    </div>
  );
}

function JsonBlock({ value }: { value: Record<string, unknown> | null | undefined }) {
  const isEmpty = value == null || Object.keys(value).length === 0;
  if (isEmpty) {
    return <p className="text-xs italic text-muted-foreground">No data.</p>;
  }
  return (
    <pre className="overflow-auto rounded-md border border-border bg-muted/40 p-2 text-xs leading-relaxed">
      {JSON.stringify(value, null, 2)}
    </pre>
  );
}

function ParamForm({
  node,
  schema,
  onParametersChange,
}: {
  node: WfNode;
  schema: JsonSchema | undefined;
  onParametersChange?: NodeInspectorProps['onParametersChange'];
}) {
  const params = (node.parameters ?? {}) as Record<string, unknown>;
  const properties = schema?.properties ?? {};
  const keys = Object.keys(properties);
  const requiredKeys = new Set(schema?.required ?? []);

  const handleChange = (key: string, next: unknown) => {
    const updated = { ...params, [key]: next };
    onParametersChange?.(node.id, updated);
  };

  if (keys.length === 0) {
    return (
      <p className="text-xs italic text-muted-foreground">
        This node type has no configurable parameters.
      </p>
    );
  }

  return (
    <div className="flex flex-col gap-3">
      {keys.map((key) => (
        <SchemaField
          key={key}
          nodeId={node.id}
          name={key}
          schema={properties[key]}
          value={params[key]}
          required={requiredKeys.has(key)}
          onChange={(next) => handleChange(key, next)}
        />
      ))}
    </div>
  );
}

/**
 * Editor inspector panel for the selected node. Renders a JSON-Schema-driven
 * read/write form from the node-type's `parameterSchema` (string/number/
 * boolean/enum fields), emitting the full next `parameters` object via
 * `onParametersChange`. When an {@link NodeExecView} is supplied, adds
 * read-only Input/Output tabs alongside the parameters tab.
 */
export function NodeInspector({
  node,
  nodeType,
  onParametersChange,
  execution,
  className,
}: NodeInspectorProps) {
  if (!node) {
    return (
      <EmptyState
        title="No node selected"
        description="Select a node on the canvas to edit its parameters."
        className={className}
      />
    );
  }

  const header = (
    <header className="flex flex-col gap-1 border-b border-border px-4 py-3">
      <div className="flex items-center gap-2">
        <h3 className="truncate text-sm font-semibold text-foreground">
          {node.name}
        </h3>
        {execution && (
          <StatusBadge
            status={execution.status}
            config={NODE_STATUS_BADGE}
            size="sm"
          />
        )}
      </div>
      <span className="text-xs text-muted-foreground">{node.type}</span>
    </header>
  );

  const paramForm = (
    <div className="overflow-auto p-4">
      <ParamForm
        node={node}
        schema={nodeType?.parameterSchema}
        onParametersChange={onParametersChange}
      />
    </div>
  );

  if (!execution) {
    return (
      <div className={cn('flex h-full min-h-0 flex-col', className)}>
        {header}
        {paramForm}
      </div>
    );
  }

  return (
    <div className={cn('flex h-full min-h-0 flex-col', className)}>
      {header}
      <Tabs defaultValue="params" className="flex min-h-0 flex-1 flex-col">
        <TabsList className="mx-4 mt-3 self-start">
          <TabsTrigger value="params">Parameters</TabsTrigger>
          <TabsTrigger value="input">Input</TabsTrigger>
          <TabsTrigger value="output">Output</TabsTrigger>
        </TabsList>
        <TabsContent value="params" className="min-h-0 flex-1">
          {paramForm}
        </TabsContent>
        <TabsContent value="input" className="min-h-0 flex-1 overflow-auto p-4">
          <JsonBlock value={execution.inputData} />
        </TabsContent>
        <TabsContent value="output" className="min-h-0 flex-1 overflow-auto p-4">
          <JsonBlock value={execution.outputData} />
        </TabsContent>
      </Tabs>
    </div>
  );
}
