import { useState, useCallback, useRef } from 'react';
import type {
  WorkflowDSL,
  AgentOutput,
  WorkflowNodeType,
  NodeInstanceAgentOutput,
} from '../types';

function extractJson(text: string): string | null {
  const match = text.match(/```json\s*([\s\S]*?)```/);
  if (match) return match[1].trim();
  const jsonMatch = text.match(/\{[\s\S]*\}/);
  return jsonMatch ? jsonMatch[0] : null;
}

function validateAgentOutput(parsed: any): parsed is AgentOutput {
  return (
    parsed &&
    parsed.dsl &&
    Array.isArray(parsed.dsl.nodes) &&
    Array.isArray(parsed.dsl.edges)
  );
}

function validateNodeInstanceOutput(
  parsed: any,
): parsed is NodeInstanceAgentOutput {
  return (
    parsed &&
    typeof parsed.name === 'string' &&
    parsed.name.length > 0 &&
    typeof parsed.config === 'object' &&
    parsed.config !== null
  );
}

/**
 * Send initial prompt to Agent, then listen for ALL subsequent agent messages
 * until the provided validator accepts the parsed JSON. Returns via AbortSignal.
 */
function sendPromptAndWait<T>(
  prompt: string,
  validator: (parsed: any) => parsed is T,
  signal?: AbortSignal,
): Promise<T> {
  return new Promise((resolve, reject) => {
    const requestId = `wf_${Date.now()}_${Math.random().toString(36).slice(2)}`;

    function handleMessage(e: MessageEvent) {
      const data = e.data;
      if (!data || data.requestId !== requestId) return;

      if (data.type === 'WORKFLOW_AGENT_MESSAGE') {
        const jsonStr = extractJson(data.response);
        if (jsonStr) {
          try {
            const parsed = JSON.parse(jsonStr);
            if (validator(parsed)) {
              cleanup();
              window.parent.postMessage(
                { type: 'WORKFLOW_AGENT_DONE', requestId },
                '*',
              );
              resolve(parsed);
            }
          } catch {
            // Not valid JSON yet, keep waiting
          }
        }
      } else if (data.type === 'WORKFLOW_AGENT_ERROR') {
        cleanup();
        reject(new Error(data.error || 'Agent request failed'));
      }
    }

    function cleanup() {
      window.removeEventListener('message', handleMessage);
      signal?.removeEventListener('abort', handleAbort);
    }

    function handleAbort() {
      cleanup();
      window.parent.postMessage(
        { type: 'WORKFLOW_AGENT_DONE', requestId },
        '*',
      );
      reject(new Error('ABORTED'));
    }

    if (signal?.aborted) {
      reject(new Error('ABORTED'));
      return;
    }
    signal?.addEventListener('abort', handleAbort);

    window.addEventListener('message', handleMessage);
    window.parent.postMessage(
      { type: 'WORKFLOW_AGENT_REQUEST', requestId, prompt },
      '*',
    );
  });
}

interface UseWorkflowAgentOptions {
  nodeTypes: WorkflowNodeType[];
}

export function useWorkflowAgent({ nodeTypes }: UseWorkflowAgentOptions) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);

  const buildSystemContext = useCallback(() => {
    const nodeTypeSummary = nodeTypes
      .map(
        (nt) =>
          `- ${nt.type} (${nt.category}): ${nt.description}. Config: ${JSON.stringify(nt.configSchema)}`,
      )
      .join('\n');

    return `You are a workflow DSL generator. Output ONLY a JSON object wrapped in \`\`\`json code block.

Available node types:
${nodeTypeSummary}

DSL format:
{
  "dsl": { "nodes": [...], "edges": [...] },
  "changeSummary": "brief description of changes"
}

Each node: { "id": "uuid", "type": "node_type", "label": "Display Name", "position": {"x": number, "y": number}, "config": {...} }
Each edge: { "id": "uuid", "source": "nodeId", "target": "nodeId", "sourceHandle?": "string", "label?": "string" }

Position nodes using: x = vertical order (increment by 250 for each sequential step), y = horizontal lane (0 for single-path flows; use different y values spaced by 200 only for parallel branches).`;
  }, [nodeTypes]);

  const abort = useCallback(() => {
    abortControllerRef.current?.abort();
    abortControllerRef.current = null;
    setLoading(false);
  }, []);

  const generate = useCallback(
    async (params: {
      action: 'create' | 'edit_node' | 'edit_workflow' | 'create_node_instance';
      prompt: string;
      currentDsl?: WorkflowDSL;
      targetNodeId?: string;
      nodeType?: WorkflowNodeType;
      scenarioHint?: string;
    }): Promise<AgentOutput | NodeInstanceAgentOutput | null> => {
      // Abort any previous pending listener
      abortControllerRef.current?.abort();

      const controller = new AbortController();
      abortControllerRef.current = controller;

      setLoading(true);
      setError(null);

      try {
        let userPrompt = '';
        const systemContext = buildSystemContext();

        if (params.action === 'create') {
          userPrompt = `${systemContext}\n\nCreate a workflow: ${params.prompt}`;
        } else if (params.action === 'edit_workflow') {
          userPrompt = `${systemContext}\n\nCurrent DSL:\n\`\`\`json\n${JSON.stringify(params.currentDsl, null, 2)}\n\`\`\`\n\nModify the workflow: ${params.prompt}\n\nReturn the COMPLETE updated DSL.`;
        } else if (params.action === 'edit_node') {
          userPrompt = `${systemContext}\n\nCurrent DSL:\n\`\`\`json\n${JSON.stringify(params.currentDsl, null, 2)}\n\`\`\`\n\nModify ONLY the node with id "${params.targetNodeId}": ${params.prompt}\n\nReturn the COMPLETE updated DSL with only that node changed.`;
        } else if (params.action === 'create_node_instance') {
          const nt = params.nodeType!;
          const nodeInstanceContext = `You are a node instance config generator. Output ONLY a JSON object wrapped in \`\`\`json code block.

Node type: ${nt.type} (${nt.category}) - ${nt.description}

configSchema (your output "config" must conform to this):
${JSON.stringify(nt.configSchema, null, 2)}

Business scenario: ${params.scenarioHint || 'Generate appropriate configuration for this node type.'}

Output format (strict):
{
  "name": "A descriptive instance name",
  "description": "What this instance does",
  "config": { ... fields matching configSchema ... }
}`;
          userPrompt = `${nodeInstanceContext}\n\nUser request: ${params.prompt}`;
        }

        if (params.action === 'create_node_instance') {
          userPrompt = `${userPrompt} \n\n你先理解下这个需求，然后找我澄清需求（非技术方案），每次问我一个问题（如果有多个问题请分多次询问），我全部确认后再行动。切记：仅最终结果以json形式返回，其他非结果数据不以json形式返回。`;
          const result = await sendPromptAndWait<NodeInstanceAgentOutput>(
            userPrompt,
            validateNodeInstanceOutput,
            controller.signal,
          );
          return result as any;
        } else {
          userPrompt = `${userPrompt} \n\n你先理解下这个需求，然后找我澄清需求（非技术方案），每个node的参数可以不用在这个阶段来确认，每次问我一个问题（如果有多个问题请分多次询问），我全部确认后再行动。切记：仅最终结果以json形式返回，其他非结果数据不以json形式返回。`;
          const result = await sendPromptAndWait<AgentOutput>(
            userPrompt,
            validateAgentOutput,
            controller.signal,
          );
          return result;
        }
      } catch (e: any) {
        if (e?.message === 'ABORTED') {
          return null;
        }
        const msg = e?.message ?? 'Agent request failed';
        setError(msg);
        return null;
      } finally {
        setLoading(false);
      }
    },
    [buildSystemContext],
  );

  return { generate, abort, loading, error };
}
