"use client";

/**
 * 프로시저/함수 호출 노드 속성 편집
 */

import { useEffect, useState, useCallback } from "react";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Database, Workflow, RefreshCw, Loader2 } from "lucide-react";
import { useFlowEditorStore } from "@/lib/stores/flowEditorStore";
import {
  getFlowProcedures,
  getFlowProcedureParameters,
} from "@/lib/api/flow";
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
import type { ProcedureCallActionNodeData } from "@/types/node-editor";
import type { ProcedureListItem, ProcedureParameterInfo } from "@/types/flowExternalDb";

interface ExternalConnection {
  id: number;
  connection_name: string;
  db_type: string;
}

interface ProcedureCallActionPropertiesProps {
  nodeId: string;
  data: ProcedureCallActionNodeData;
}

export function ProcedureCallActionProperties({
  nodeId,
  data,
}: ProcedureCallActionPropertiesProps) {
  const { updateNode, nodes, edges } = useFlowEditorStore();

  const [displayName, setDisplayName] = useState(
    data.displayName || "프로시저 호출"
  );
  const [dbSource, setDbSource] = useState<"internal" | "external">(
    data.dbSource || "internal"
  );
  const [connectionId, setConnectionId] = useState<number | undefined>(
    data.connectionId
  );
  const [procedureName, setProcedureName] = useState(
    data.procedureName || ""
  );
  const [procedureSchema, setProcedureSchema] = useState(
    data.procedureSchema || "public"
  );
  const [callType, setCallType] = useState<"procedure" | "function">(
    data.callType || "function"
  );
  const [parameters, setParameters] = useState(data.parameters || []);

  const [connections, setConnections] = useState<ExternalConnection[]>([]);
  const [procedures, setProcedures] = useState<ProcedureListItem[]>([]);
  const [loadingProcedures, setLoadingProcedures] = useState(false);
  const [loadingParams, setLoadingParams] = useState(false);
  const [sourceFields, setSourceFields] = useState<
    Array<{ name: string; label?: string }>
  >([]);

  // 이전 노드에서 소스 필드 목록 수집 (재귀)
  useEffect(() => {
    const getUpstreamFields = (
      targetId: string,
      visited = new Set<string>()
    ): Array<{ name: string; label?: string }> => {
      if (visited.has(targetId)) return [];
      visited.add(targetId);

      const inEdges = edges.filter((e) => e.target === targetId);
      const parentNodes = nodes.filter((n) =>
        inEdges.some((e) => e.source === n.id)
      );
      const fields: Array<{ name: string; label?: string }> = [];

      for (const pNode of parentNodes) {
        if (
          pNode.type === "tableSource" ||
          pNode.type === "externalDBSource"
        ) {
          const nodeFields =
            (pNode.data as any).fields ||
            (pNode.data as any).outputFields ||
            [];
          if (Array.isArray(nodeFields)) {
            for (const f of nodeFields) {
              const name =
                typeof f === "string"
                  ? f
                  : f.name || f.columnName || f.field;
              if (name) {
                fields.push({
                  name,
                  label: f.label || f.columnLabel || name,
                });
              }
            }
          }
        } else if (pNode.type === "dataTransform") {
          const upper = getUpstreamFields(pNode.id, visited);
          fields.push(...upper);
          const transforms = (pNode.data as any).transformations;
          if (Array.isArray(transforms)) {
            for (const t of transforms) {
              if (t.targetField) {
                fields.push({
                  name: t.targetField,
                  label: t.targetFieldLabel || t.targetField,
                });
              }
            }
          }
        } else if (pNode.type === "formulaTransform") {
          const upper = getUpstreamFields(pNode.id, visited);
          fields.push(...upper);
          const transforms = (pNode.data as any).transformations;
          if (Array.isArray(transforms)) {
            for (const t of transforms) {
              if (t.outputField) {
                fields.push({
                  name: t.outputField,
                  label: t.outputFieldLabel || t.outputField,
                });
              }
            }
          }
        } else {
          fields.push(...getUpstreamFields(pNode.id, visited));
        }
      }

      return fields;
    };

    const collected = getUpstreamFields(nodeId);
    const unique = Array.from(
      new Map(collected.map((f) => [f.name, f])).values()
    );
    setSourceFields(unique);
  }, [nodeId, nodes, edges]);

  useEffect(() => {
    setDisplayName(data.displayName || "프로시저 호출");
    setDbSource(data.dbSource || "internal");
    setConnectionId(data.connectionId);
    setProcedureName(data.procedureName || "");
    setProcedureSchema(data.procedureSchema || "public");
    setCallType(data.callType || "function");
    setParameters(data.parameters || []);
  }, [data]);

  // 외부 DB 연결 목록 조회
  useEffect(() => {
    if (dbSource === "external") {
      ExternalDbConnectionAPI.getConnections({ is_active: "true" })
        .then((list) =>
          setConnections(
            list.map((c: any) => ({
              id: c.id,
              connection_name: c.connection_name,
              db_type: c.db_type,
            }))
          )
        )
        .catch(console.error);
    }
  }, [dbSource]);

  const updateNodeData = useCallback(
    (updates: Partial<ProcedureCallActionNodeData>) => {
      updateNode(nodeId, { ...data, ...updates });
    },
    [nodeId, data, updateNode]
  );

  // 프로시저 목록 조회
  const fetchProcedures = useCallback(async () => {
    if (dbSource === "external" && !connectionId) return;
    setLoadingProcedures(true);
    try {
      const res = await getFlowProcedures(
        dbSource,
        connectionId,
        procedureSchema || undefined
      );
      if (res.success && res.data) {
        setProcedures(res.data);
      }
    } catch (e) {
      console.error("프로시저 목록 조회 실패:", e);
    } finally {
      setLoadingProcedures(false);
    }
  }, [dbSource, connectionId, procedureSchema]);

  // dbSource/connectionId 변경 시 프로시저 목록 자동 조회
  useEffect(() => {
    if (dbSource === "internal" || (dbSource === "external" && connectionId)) {
      fetchProcedures();
    }
  }, [dbSource, connectionId, fetchProcedures]);

  // 프로시저 선택 시 파라미터 조회
  const handleProcedureSelect = useCallback(
    async (name: string) => {
      setProcedureName(name);

      const selected = procedures.find((p) => p.name === name);
      const newCallType =
        selected?.type === "PROCEDURE" ? "procedure" : "function";
      setCallType(newCallType);

      updateNodeData({
        procedureName: name,
        callType: newCallType,
        procedureSchema,
      });

      setLoadingParams(true);
      try {
        const res = await getFlowProcedureParameters(
          name,
          dbSource,
          connectionId,
          procedureSchema || undefined
        );
        if (res.success && res.data) {
          const newParams = res.data.map((p: ProcedureParameterInfo) => ({
            name: p.name,
            dataType: p.dataType,
            mode: p.mode,
            source: "record_field" as const,
            field: "",
            value: "",
          }));
          setParameters(newParams);
          updateNodeData({
            procedureName: name,
            callType: newCallType,
            procedureSchema,
            parameters: newParams,
          });
        }
      } catch (e) {
        console.error("파라미터 조회 실패:", e);
      } finally {
        setLoadingParams(false);
      }
    },
    [dbSource, connectionId, procedureSchema, procedures, updateNodeData]
  );

  const handleParamChange = (
    index: number,
    field: string,
    value: string
  ) => {
    const newParams = [...parameters];
    (newParams[index] as any)[field] = value;
    setParameters(newParams);
    updateNodeData({ parameters: newParams });
  };

  return (
    <div className="space-y-4 p-4">
      {/* 표시명 */}
      <div className="space-y-2">
        <Label className="text-xs font-medium">표시명</Label>
        <Input
          value={displayName}
          onChange={(e) => {
            setDisplayName(e.target.value);
            updateNodeData({ displayName: e.target.value });
          }}
          placeholder="프로시저 호출"
          className="h-8 text-sm"
        />
      </div>

      {/* DB 소스 */}
      <div className="space-y-2">
        <Label className="text-xs font-medium">DB 소스</Label>
        <Select
          value={dbSource}
          onValueChange={(v: "internal" | "external") => {
            setDbSource(v);
            setConnectionId(undefined);
            setProcedureName("");
            setParameters([]);
            setProcedures([]);
            updateNodeData({
              dbSource: v,
              connectionId: undefined,
              connectionName: undefined,
              procedureName: "",
              parameters: [],
            });
          }}
        >
          <SelectTrigger className="h-8 text-sm">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="internal">내부 DB (PostgreSQL)</SelectItem>
            <SelectItem value="external">외부 DB</SelectItem>
          </SelectContent>
        </Select>
      </div>

      {/* 외부 DB 연결 선택 */}
      {dbSource === "external" && (
        <div className="space-y-2">
          <Label className="text-xs font-medium">외부 DB 연결</Label>
          <Select
            value={connectionId?.toString() || ""}
            onValueChange={(v) => {
              const id = parseInt(v);
              setConnectionId(id);
              setProcedureName("");
              setParameters([]);
              const conn = connections.find((c) => c.id === id);
              updateNodeData({
                connectionId: id,
                connectionName: conn?.connection_name,
                procedureName: "",
                parameters: [],
              });
            }}
          >
            <SelectTrigger className="h-8 text-sm">
              <SelectValue placeholder="연결 선택" />
            </SelectTrigger>
            <SelectContent>
              {connections.map((c) => (
                <SelectItem key={c.id} value={c.id.toString()}>
                  {c.connection_name} ({c.db_type})
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
      )}

      {/* 스키마 */}
      <div className="space-y-2">
        <Label className="text-xs font-medium">스키마</Label>
        <div className="flex gap-2">
          <Input
            value={procedureSchema}
            onChange={(e) => setProcedureSchema(e.target.value)}
            onBlur={() => {
              updateNodeData({ procedureSchema });
              fetchProcedures();
            }}
            placeholder="public"
            className="h-8 text-sm"
          />
          <Button
            variant="outline"
            size="sm"
            className="h-8 w-8 shrink-0 p-0"
            onClick={fetchProcedures}
            disabled={loadingProcedures}
          >
            {loadingProcedures ? (
              <Loader2 className="h-3 w-3 animate-spin" />
            ) : (
              <RefreshCw className="h-3 w-3" />
            )}
          </Button>
        </div>
      </div>

      {/* 프로시저 선택 */}
      <div className="space-y-2">
        <Label className="text-xs font-medium">프로시저/함수 선택</Label>
        {loadingProcedures ? (
          <div className="flex items-center gap-2 rounded border p-2 text-xs text-muted-foreground">
            <Loader2 className="h-3 w-3 animate-spin" />
            목록 조회 중...
          </div>
        ) : (
          <Select
            value={procedureName}
            onValueChange={handleProcedureSelect}
          >
            <SelectTrigger className="h-8 text-sm">
              <SelectValue placeholder="프로시저 선택" />
            </SelectTrigger>
            <SelectContent>
              {procedures.map((p) => (
                <SelectItem key={`${p.schema}.${p.name}`} value={p.name}>
                  <div className="flex items-center gap-2">
                    <span
                      className={`rounded px-1 py-0.5 text-[10px] font-medium ${
                        p.type === "FUNCTION"
                          ? "bg-cyan-100 text-cyan-700"
                          : "bg-violet-100 text-violet-700"
                      }`}
                    >
                      {p.type === "FUNCTION" ? "FN" : "SP"}
                    </span>
                    <span className="font-mono text-xs">{p.name}</span>
                  </div>
                </SelectItem>
              ))}
              {procedures.length === 0 && (
                <SelectItem value="" disabled>
                  프로시저가 없습니다
                </SelectItem>
              )}
            </SelectContent>
          </Select>
        )}
      </div>

      {/* 호출 타입 */}
      {procedureName && (
        <div className="space-y-2">
          <Label className="text-xs font-medium">호출 타입</Label>
          <Select
            value={callType}
            onValueChange={(v: "procedure" | "function") => {
              setCallType(v);
              updateNodeData({ callType: v });
            }}
          >
            <SelectTrigger className="h-8 text-sm">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="function">SELECT (함수)</SelectItem>
              <SelectItem value="procedure">CALL (프로시저)</SelectItem>
            </SelectContent>
          </Select>
        </div>
      )}

      {/* 파라미터 매핑 */}
      {procedureName && parameters.length > 0 && (
        <div className="space-y-3">
          {loadingParams ? (
            <div className="flex items-center gap-2 rounded border p-2 text-xs text-muted-foreground">
              <Loader2 className="h-3 w-3 animate-spin" />
              파라미터 조회 중...
            </div>
          ) : (
            <>
              {/* IN 파라미터 */}
              {parameters.filter((p) => p.mode === "IN" || p.mode === "INOUT")
                .length > 0 && (
                <div className="space-y-2">
                  <Label className="flex items-center gap-1.5 text-xs font-medium">
                    <span className="rounded bg-primary/10 px-1.5 py-0.5 text-[10px] text-primary">
                      IN
                    </span>
                    입력 파라미터
                  </Label>
                  <div className="space-y-2">
                    {parameters.map((param, idx) => {
                      if (param.mode !== "IN" && param.mode !== "INOUT")
                        return null;
                      return (
                        <Card key={idx} className="bg-muted">
                          <CardContent className="space-y-2 p-3">
                            <div className="flex items-center justify-between">
                              <span className="font-mono text-xs font-medium">
                                {param.name}
                              </span>
                              <span className="rounded bg-muted/80 px-1.5 py-0.5 text-[10px]">
                                {param.dataType}
                              </span>
                            </div>
                            <Select
                              value={param.source}
                              onValueChange={(v) =>
                                handleParamChange(idx, "source", v)
                              }
                            >
                              <SelectTrigger className="h-7 text-xs">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                <SelectItem value="record_field">
                                  레코드 필드
                                </SelectItem>
                                <SelectItem value="static">고정값</SelectItem>
                                <SelectItem value="step_variable">
                                  스텝 변수
                                </SelectItem>
                              </SelectContent>
                            </Select>
                            {param.source === "record_field" &&
                              (sourceFields.length > 0 ? (
                                <Select
                                  value={param.field || ""}
                                  onValueChange={(v) =>
                                    handleParamChange(idx, "field", v)
                                  }
                                >
                                  <SelectTrigger className="h-7 text-xs">
                                    <SelectValue placeholder="필드 선택" />
                                  </SelectTrigger>
                                  <SelectContent>
                                    {sourceFields.map((f) => (
                                      <SelectItem
                                        key={f.name}
                                        value={f.name}
                                      >
                                        <span className="font-mono text-xs">
                                          {f.name}
                                        </span>
                                        {f.label && f.label !== f.name && (
                                          <span className="ml-1 text-[10px] text-muted-foreground/70">
                                            ({f.label})
                                          </span>
                                        )}
                                      </SelectItem>
                                    ))}
                                  </SelectContent>
                                </Select>
                              ) : (
                                <Input
                                  value={param.field || ""}
                                  onChange={(e) =>
                                    handleParamChange(
                                      idx,
                                      "field",
                                      e.target.value
                                    )
                                  }
                                  placeholder="컬럼명 (이전 노드를 먼저 연결하세요)"
                                  className="h-7 text-xs"
                                />
                              ))}
                            {param.source === "static" && (
                              <Input
                                value={param.value || ""}
                                onChange={(e) =>
                                  handleParamChange(
                                    idx,
                                    "value",
                                    e.target.value
                                  )
                                }
                                placeholder="고정값 입력"
                                className="h-7 text-xs"
                              />
                            )}
                            {param.source === "step_variable" && (
                              <Input
                                value={param.field || ""}
                                onChange={(e) =>
                                  handleParamChange(
                                    idx,
                                    "field",
                                    e.target.value
                                  )
                                }
                                placeholder="변수명"
                                className="h-7 text-xs"
                              />
                            )}
                          </CardContent>
                        </Card>
                      );
                    })}
                  </div>
                </div>
              )}

              {/* OUT 파라미터 (반환 필드) */}
              {parameters.filter((p) => p.mode === "OUT" || p.mode === "INOUT")
                .length > 0 && (
                <div className="space-y-2">
                  <Label className="flex items-center gap-1.5 text-xs font-medium">
                    <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] text-emerald-700">
                      OUT
                    </span>
                    반환 필드
                    <span className="text-[10px] font-normal text-muted-foreground/70">
                      (다음 노드에서 사용 가능)
                    </span>
                  </Label>
                  <div className="rounded-md border border-emerald-200 bg-emerald-50 p-2">
                    <div className="space-y-1">
                      {parameters
                        .filter(
                          (p) => p.mode === "OUT" || p.mode === "INOUT"
                        )
                        .map((param, idx) => (
                          <div
                            key={idx}
                            className="flex items-center justify-between rounded bg-white px-2 py-1.5"
                          >
                            <span className="font-mono text-xs font-medium text-emerald-700">
                              {param.name}
                            </span>
                            <span className="rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground">
                              {param.dataType}
                            </span>
                          </div>
                        ))}
                    </div>
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      )}

      {/* 안내 메시지 */}
      <Card className="bg-violet-50">
        <CardContent className="p-3 text-xs text-violet-700">
          <div className="mb-1 flex items-center gap-1 font-medium">
            <Workflow className="h-3 w-3" />
            프로시저 실행 안내
          </div>
          <p>
            이 노드에 연결된 이전 노드의 데이터가 프로시저의 입력 파라미터로
            전달됩니다. 프로시저 실행이 실패하면 전체 트랜잭션이 롤백됩니다.
          </p>
        </CardContent>
      </Card>
    </div>
  );
}
