/**
 * Inline dropdown to select a data source (node + path) - no popup.
 * Form nodes (trigger.form / input.form): only payload.<fieldName> paths (no duplicate tree).
 */

import React from 'react';
import { createRef, isRef, isValue, createValue, type DataRef } from './dataRef';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
import { useLanguage } from '../../../../providers/language/LanguageContext';

/** How to build path options for StatischKontextSelect / RefSourceSelect. */
export type PathPickMode = 'default' | 'clickup_task_id' | 'exclude_forms';

/** Only task IDs from ClickUp nodes — single path (taskId === clickupTask.id at runtime). */
function buildClickUpTaskIdPaths(): Array<{ path: (string | number)[]; pathLabel: string }> {
  return [{ path: ['taskId'], pathLabel: 'Aufgaben-ID' }];
}

/** Curated paths for clickup.* outputs — avoids huge documentData / payload trees. */
function buildClickUpOutputPaths(preview: unknown): Array<{ path: (string | number)[]; pathLabel: string }> {
  const paths: Array<{ path: (string | number)[]; pathLabel: string }> = [
    { path: ['taskId'], pathLabel: 'Aufgaben-ID' },
    { path: ['clickupTask', 'name'], pathLabel: 'clickupTask.name' },
    { path: ['success'], pathLabel: 'success' },
    { path: ['error'], pathLabel: 'error' },
    { path: ['documents', 0, 'documentName'], pathLabel: 'documents[0].documentName' },
  ];
  if (preview && typeof preview === 'object') {
    const p = preview as Record<string, unknown>;
    const ct = p.clickupTask;
    if (ct && typeof ct === 'object' && !Array.isArray(ct)) {
      const o = ct as Record<string, unknown>;
      for (const k of Object.keys(o)) {
        if (k === 'id' || k === 'name') continue;
        const v = o[k];
        if (v != null && typeof v !== 'object') {
          paths.push({ path: ['clickupTask', k], pathLabel: `clickupTask.${k}` });
        }
        if (k === 'status' && v && typeof v === 'object') {
          paths.push({
            path: ['clickupTask', 'status', 'status'],
            pathLabel: 'clickupTask.status.status',
          });
        }
      }
    }
  }
  return paths;
}

function buildPickablePaths(
  obj: unknown,
  basePath: (string | number)[] = []
): Array<{ path: (string | number)[]; pathLabel: string }> {
  const pathLabel = basePath.length ? basePath.map(String).join('.') : '';
  if (obj == null || typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
    return [{ path: [...basePath], pathLabel }];
  }
  if (Array.isArray(obj)) {
    const result: Array<{ path: (string | number)[]; pathLabel: string }> = [{ path: [...basePath], pathLabel }];
    for (let i = 0; i < Math.min(obj.length, 10); i++) {
      result.push(...buildPickablePaths(obj[i], [...basePath, i]));
    }
    return result;
  }
  if (typeof obj === 'object') {
    const result: Array<{ path: (string | number)[]; pathLabel: string }> = [{ path: [...basePath], pathLabel }];
    for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
      result.push(...buildPickablePaths(v, [...basePath, k]));
    }
    return result;
  }
  return [{ path: [...basePath], pathLabel }];
}

/** Nur Formular-Felder: ein Eintrag pro Feld unter payload.<name> — kein rekursives Durchwandern. */
function buildFormSchemaPayloadPaths(params: Record<string, unknown>): Array<{
  path: (string | number)[];
  pathLabel: string;
}> {
  const raw = params.formFields ?? params.fields;
  if (!Array.isArray(raw)) return [];
  const out: Array<{ path: (string | number)[]; pathLabel: string }> = [];
  for (let i = 0; i < raw.length; i++) {
    const row = raw[i];
    if (!row || typeof row !== 'object') continue;
    const name = String((row as Record<string, unknown>).name ?? `field${i + 1}`).trim();
    if (!name) continue;
    out.push({ path: ['payload', name], pathLabel: `payload.${name}` });
  }
  return out;
}

function buildLoopCurrentItemPaths(preview: unknown): Array<{ path: (string | number)[]; pathLabel: string }> {
  const paths: Array<{ path: (string | number)[]; pathLabel: string }> = [
    { path: ['currentItem'], pathLabel: 'currentItem' },
    { path: ['currentIndex'], pathLabel: 'currentIndex' },
    { path: ['count'], pathLabel: 'count' },
  ];
  if (preview && typeof preview === 'object') {
    const ci = (preview as Record<string, unknown>).currentItem;
    if (ci && typeof ci === 'object' && !Array.isArray(ci)) {
      for (const [k, v] of Object.entries(ci as Record<string, unknown>)) {
        paths.push(...buildPickablePaths(v, ['currentItem', k]));
      }
    }
  }
  return paths;
}

function buildAiPromptPaths(preview: unknown): Array<{ path: (string | number)[]; pathLabel: string }> {
  const paths = buildPickablePaths(preview);
  if (preview && typeof preview === 'object') {
    const rd = (preview as Record<string, unknown>).responseData;
    if (rd && typeof rd === 'object' && !Array.isArray(rd)) {
      for (const k of Object.keys(rd as Record<string, unknown>)) {
        const p = { path: ['responseData', k], pathLabel: `responseData.${k}` };
        if (!paths.some((x) => x.pathLabel === p.pathLabel)) paths.push(p);
      }
    }
  }
  return paths;
}

export function pickPathsForNode(
  node: { type?: string; parameters?: Record<string, unknown> } | undefined,
  preview: unknown,
  mode: PathPickMode = 'default'
): Array<{ path: (string | number)[]; pathLabel: string }> {
  if (!node) return buildPickablePaths(preview);
  const nt = node.type ?? '';
  if (mode === 'clickup_task_id') {
    if (nt.startsWith('clickup.')) {
      return buildClickUpTaskIdPaths();
    }
    return [];
  }
  if (nt === 'trigger.form' || nt === 'input.form') {
    return buildFormSchemaPayloadPaths(node.parameters ?? {});
  }
  if (node.type === 'input.upload') {
    return buildPickablePathsForUpload();
  }
  if (nt.startsWith('clickup.')) {
    return buildClickUpOutputPaths(preview);
  }
  if (nt === 'flow.loop') {
    return buildLoopCurrentItemPaths(preview);
  }
  if (nt === 'ai.prompt') {
    return buildAiPromptPaths(preview);
  }
  return buildPickablePaths(preview);
}

/** Für input.upload: nur relevante Pfade für If/Else – MIME-Type, Dateiname, Datei vorhanden. */
function buildPickablePathsForUpload(): Array<{ path: (string | number)[]; pathLabel: string }> {
  return [
    { path: [], pathLabel: '' },
    { path: ['file'], pathLabel: 'file' },
    { path: ['file', 'mimeType'], pathLabel: 'file.mimeType' },
    { path: ['file', 'fileName'], pathLabel: 'file.fileName' },
    { path: ['files'], pathLabel: 'files' },
    { path: ['fileIds'], pathLabel: 'fileIds' },
  ];
}

export function refToOptionValue(ref: DataRef): string {
  return JSON.stringify(ref);
}

function _pathLabelForDisplay(pathLabel: string, translate: (key: string) => string): string {
  if (pathLabel === 'Aufgaben-ID') return translate('Aufgaben-ID');
  return pathLabel;
}

export function optionValueToRef(s: string): DataRef | null {
  try {
    const o = JSON.parse(s) as unknown;
    if (o && typeof o === 'object' && (o as DataRef).type === 'ref' && typeof (o as DataRef).nodeId === 'string') {
      return o as DataRef;
    }
  } catch {
    /* ignore */
  }
  return null;
}

/** Option value for „Statisch (manuell)“ in StatischKontextSelect. */
export const STATIC_SOURCE_VALUE = '__static__';

function parseHybridLocal(value: unknown): { ref: DataRef | null; staticStr: string } {
  if (isRef(value)) return { ref: value, staticStr: '' };
  if (isValue(value)) {
    const v = value.value;
    if (v === null || v === undefined) return { ref: null, staticStr: '' };
    return { ref: null, staticStr: String(v) };
  }
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
    return { ref: null, staticStr: String(value) };
  }
  return { ref: null, staticStr: '' };
}

/** Aktueller Wert des Quellen-Dropdowns: '' | STATIC_SOURCE_VALUE | ref-JSON. */
export function getStaticContextSelectValue(value: unknown): string {
  if (isRef(value)) return refToOptionValue(value);
  if (value === undefined || value === null) return '';
  return STATIC_SOURCE_VALUE;
}

/** Statische Eingabe (Text, Checkbox, ClickUp-Option, …) nur bei „Statisch“ oder ohne vorgelagerte Nodes. */
export function shouldShowStaticControl(value: unknown, hasSources: boolean): boolean {
  if (!hasSources) return true;
  if (isRef(value)) return false;
  return getStaticContextSelectValue(value) === STATIC_SOURCE_VALUE;
}

interface StatischKontextSelectProps {
  value: unknown;
  onChange: (v: unknown) => void;
  placeholder?: string;
  /** Label für die manuelle Option (Default: Statisch). */
  staticLabel?: string;
  /** default: full tree; clickup_task_id: only taskId from ClickUp nodes; exclude_forms: skip form nodes. */
  pathPickMode?: PathPickMode;
}

/**
 * Ein Dropdown: zuerst „Quelle wählen“, dann „Statisch“, dann Kontextpfade.
 * Bei Kontext-Ref kein paralleles Textfeld (nur in HybridStaticRefField / ClickUp bei shouldShowStaticControl).
 */
export const StatischKontextSelect: React.FC<StatischKontextSelectProps> = ({
  value,
  onChange,
  placeholder,
  staticLabel,
  pathPickMode = 'default',
}) => {
  const { t } = useLanguage();
  const dataFlow = useAutomation2DataFlow();
  if (!dataFlow) return null;

  const sourceIds = dataFlow.getAvailableSourceIds();
  const options: Array<{ ref: DataRef; label: string }> = [];

  for (const nodeId of sourceIds) {
    const node = dataFlow.nodes.find((n) => n.id === nodeId);
    if (node?.type === 'trigger.manual') continue;
    if (
      pathPickMode === 'exclude_forms' &&
      (node?.type === 'input.form' || node?.type === 'trigger.form')
    ) {
      continue;
    }
    const preview = dataFlow.nodeOutputsPreview[nodeId];
    const nodeLabel = node ? dataFlow.getNodeLabel(node) : nodeId;
    const paths = pickPathsForNode(node, preview, pathPickMode);
    for (const p of paths) {
      const pathLabelUi = _pathLabelForDisplay(p.pathLabel, t);
      const displayLabel = pathLabelUi ? `${nodeLabel} → ${pathLabelUi}` : nodeLabel;
      options.push({
        ref: createRef(nodeId, p.path),
        label: displayLabel,
      });
    }
  }

  const currentSelect = isRef(value)
    ? refToOptionValue(value)
    : value === undefined || value === null
      ? ''
      : STATIC_SOURCE_VALUE;

  return (
    <select
      value={currentSelect}
      onChange={(e) => {
        const v = e.target.value;
        if (v === '') {
          onChange(createValue(''));
          return;
        }
        if (v === STATIC_SOURCE_VALUE) {
          const { staticStr } = parseHybridLocal(value);
          onChange(createValue(isRef(value) ? '' : staticStr));
          return;
        }
        const ref = optionValueToRef(v);
        if (ref) onChange(ref);
      }}
    >
      <option value="">{placeholder ?? t('— Quelle wählen —')}</option>
      <option value={STATIC_SOURCE_VALUE}>{staticLabel ?? t('Statisch')}</option>
      {options.map((o) => (
        <option key={refToOptionValue(o.ref)} value={refToOptionValue(o.ref)}>
          {o.label}
        </option>
      ))}
    </select>
  );
};

interface RefSourceSelectProps {
  value: DataRef | null;
  onChange: (ref: DataRef | null) => void;
  placeholder?: string;
  pathPickMode?: PathPickMode;
}

/** Nur Kontext-Referenzen (ohne Statisch) — für If/Else, Switch, DynamicValueField. */
export const RefSourceSelect: React.FC<RefSourceSelectProps> = ({
  value,
  onChange,
  placeholder,
  pathPickMode = 'default',
}) => {
  const { t } = useLanguage();
  const dataFlow = useAutomation2DataFlow();
  if (!dataFlow) return null;

  const sourceIds = dataFlow.getAvailableSourceIds();
  const options: Array<{ ref: DataRef; label: string }> = [];

  for (const nodeId of sourceIds) {
    const node = dataFlow.nodes.find((n) => n.id === nodeId);
    if (node?.type === 'trigger.manual') continue;
    if (
      pathPickMode === 'exclude_forms' &&
      (node?.type === 'input.form' || node?.type === 'trigger.form')
    ) {
      continue;
    }
    const preview = dataFlow.nodeOutputsPreview[nodeId];
    const nodeLabel = node ? dataFlow.getNodeLabel(node) : nodeId;
    const paths = pickPathsForNode(node, preview, pathPickMode);
    for (const p of paths) {
      const pathLabelUi = _pathLabelForDisplay(p.pathLabel, t);
      const displayLabel = pathLabelUi ? `${nodeLabel} → ${pathLabelUi}` : nodeLabel;
      options.push({
        ref: createRef(nodeId, p.path),
        label: displayLabel,
      });
    }
  }

  const currentValue = value ? refToOptionValue(value) : '';

  return (
    <select
      value={currentValue}
      onChange={(e) => {
        const v = e.target.value;
        if (!v) {
          onChange(null);
          return;
        }
        const ref = optionValueToRef(v);
        if (ref) onChange(ref);
      }}
    >
      <option value="">{placeholder ?? t('Datenquelle wählen…')}</option>
      {options.map((o) => (
        <option key={refToOptionValue(o.ref)} value={refToOptionValue(o.ref)}>
          {o.label}
        </option>
      ))}
    </select>
  );
};

/** Inferred field type for operator selection and value input */
export type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'email' | 'file' | 'unknown';

function getFormFieldType(
  node: { parameters?: Record<string, unknown>; type?: string },
  path: (string | number)[]
): FieldType | null {
  const params = node.parameters ?? {};
  const raw = params.formFields ?? params.fields;
  if (!Array.isArray(raw)) return null;
  const isFormPayload =
    (node.type === 'trigger.form' || node.type === 'input.form') && path[0] === 'payload';
  const fieldName =
    isFormPayload && path.length >= 2
      ? String(path[1])
      : path.length >= 1
        ? String(path[0])
        : null;
  if (!fieldName) return null;
  const field = raw.find((f: unknown) => f && typeof f === 'object' && (f as Record<string, unknown>).name === fieldName);
  if (!field || typeof field !== 'object') return null;
  const rawFieldType = String((field as Record<string, unknown>).type ?? 'text').toLowerCase();
  if (rawFieldType === 'number') return 'number';
  if (rawFieldType === 'email') return 'email';
  if (rawFieldType === 'date' || rawFieldType === 'datetime') return 'date';
  if (rawFieldType === 'boolean' || rawFieldType === 'checkbox') return 'boolean';
  return 'string';
}

function getNodeOutputFieldType(
  node: { type?: string },
  path: (string | number)[]
): FieldType | null {
  if (node.type === 'input.upload') {
    if (path.length === 0 || (path.length === 1 && path[0] === 'file')) return 'file';
    if (path[0] === 'file' && path[1] === 'mimeType') return 'string';
    if (path[0] === 'file' && path[1] === 'fileName') return 'string';
    if (path.length === 1 && (path[0] === 'files' || path[0] === 'fileIds')) return 'file';
  }
  if ((node.type?.startsWith('sharepoint.') || node.type?.startsWith('email.')) && path.includes('file')) {
    const last = path[path.length - 1];
    if (last === 'mimeType' || last === 'fileName') return 'string';
    return 'file';
  }
  return null;
}

/** Infer field type from ref: form schema, node output shape, or preview value. */
export function getFieldType(
  ref: DataRef | null,
  nodes: Array<{ id: string; parameters?: Record<string, unknown>; type?: string }>,
  nodeOutputsPreview: Record<string, unknown>
): FieldType {
  if (!ref) return 'unknown';
  const node = nodes.find((n) => n.id === ref.nodeId);
  if (node) {
    const fromForm = getFormFieldType(node, ref.path);
    if (fromForm) return fromForm;
    const fromNode = getNodeOutputFieldType(node, ref.path);
    if (fromNode) return fromNode;
  }
  const root = nodeOutputsPreview[ref.nodeId];
  if (root === undefined) return 'unknown';
  let current: unknown = root;
  for (const seg of ref.path) {
    if (current == null) return 'unknown';
    const key = typeof seg === 'number' ? String(seg) : seg;
    if (Array.isArray(current) && /^\d+$/.test(key)) {
      current = current[parseInt(key, 10)];
    } else if (typeof current === 'object' && key in current) {
      current = (current as Record<string, unknown>)[key];
    } else return 'unknown';
  }
  if (typeof current === 'string') return 'string';
  if (typeof current === 'number') return 'number';
  if (typeof current === 'boolean') return 'boolean';
  if (current && typeof current === 'object' && 'url' in (current as object)) return 'file';
  return 'unknown';
}
