<template>
  <div class="api-node-configuration">
    <!-- Step 1: Basic configuration -->
    <div class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">1</span>
        </div>
        <h4 class="font-medium">Basic Configuration</h4>
      </div>

      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <!-- HTTP Method -->
        <div>
          <label for="apiMethod" class="block text-sm font-medium text-gray-700 mb-1">HTTP Method</label>
          <select
            id="apiMethod"
            v-model="localNodeData.apiMethod"
            class="w-full p-2 border rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 text-sm"
            @change="saveChanges"
          >
            <option value="GET">GET</option>
            <option value="POST">POST</option>
            <option value="PUT">PUT</option>
            <option value="PATCH">PATCH</option>
            <option value="DELETE">DELETE</option>
            <option value="HEAD">HEAD</option>
            <option value="OPTIONS">OPTIONS</option>
          </select>
          <p class="mt-1 text-xs text-gray-500">
            The HTTP method determines how the API call interacts with the endpoint.
          </p>
        </div>
        
        <!-- API URL -->
        <div>
          <label for="apiUrl" class="block text-sm font-medium text-gray-700 mb-1">API URL</label>
          <input
            id="apiUrl"
            v-model="localNodeData.apiUrl"
            type="text"
            class="w-full p-2 border rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 text-sm"
            placeholder="https://example.com/api/endpoint"
            @blur="saveChanges"
          />
          <p class="mt-1 text-xs text-gray-500">
            Use variables with <code class="bg-gray-100 px-1">{variableName}</code> syntax, e.g.: <code class="bg-gray-100 px-1">https://api.example.com/users/{userId}</code>
          </p>
        </div>
      </div>
    </div>
    
    <!-- Step 2: Authorization -->
    <div class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">2</span>
        </div>
        <h4 class="font-medium">Authorization</h4>
      </div>
      <div class="mb-2">
        <label class="block text-sm font-medium text-gray-700 mb-1">Type</label>
        <select v-if="localNodeData.authorization" v-model="localNodeData.authorization.type" class="w-48 p-2 border rounded text-sm" @change="saveChanges">
          <option v-for="type in AUTH_TYPES" :key="type.value" :value="type.value">{{ type.label }}</option>
        </select>
      </div>
      <div v-if="localNodeData.authorization && localNodeData.authorization.type" v-for="field in getAuthFields()" :key="field.key" class="mb-2">
        <label class="block text-xs font-medium text-gray-600 mb-1">{{ field.label }}</label>
        <input v-if="field.type !== 'select'" :type="field.type" v-model="localNodeData.authorization[field.key]" :placeholder="field.placeholder" class="w-full px-2 py-1 border rounded text-sm" @input="saveChanges" />
        <select v-else v-model="localNodeData.authorization[field.key]" class="w-full px-2 py-1 border rounded text-sm" @change="saveChanges">
          <option v-for="opt in field.options" :key="opt.value" :value="opt.value">{{ opt.label }}</option>
        </select>
      </div>
    </div>
    
    <!-- Step 3: Params (GET, DELETE, etc.) -->
    <div v-if="showParams" class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">3</span>
        </div>
        <h4 class="font-medium">Query Parameters</h4>
      </div>
      <KeyValueTable
        v-model="localNodeData.params"
        :variables="processVariables"
        :allow-variable-insert="true"
        placeholder-key="Param Key"
        placeholder-value="Param Value"
        @update:modelValue="saveChanges"
      />
    </div>
    
    <!-- Step 4: Headers -->
    <div class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">4</span>
        </div>
        <h4 class="font-medium">Headers</h4>
      </div>
      <KeyValueTable
        v-model="localNodeData.headers"
        :variables="processVariables"
        :allow-variable-insert="true"
        placeholder-key="Header Name"
        placeholder-value="Header Value"
        @update:modelValue="saveChanges"
      />
    </div>
    
    <!-- Step 5: Body (POST/PUT/PATCH) -->
    <div v-if="showRequestBody && localNodeData.body && bodyType" class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">5</span>
        </div>
        <h4 class="font-medium">Request Body</h4>
      </div>
      <div class="mb-2">
        <label class="block text-sm font-medium text-gray-700 mb-1">Body Type</label>
        <select v-model="localNodeData.body.type" class="w-64 p-2 border rounded text-sm" @change="saveChanges">
          <option v-for="type in BODY_TYPES" :key="type.value" :value="type.value">{{ type.label }}</option>
        </select>
      </div>
      <div v-if="['form-data', 'x-www-form-urlencoded'].includes(bodyType)">
        <KeyValueTable
          :model-value="getBodyData()"
          :variables="processVariables"
          :allow-variable-insert="true"
          placeholder-key="Field Name"
          placeholder-value="Field Value"
          @update:modelValue="setBodyData"
        />
      </div>
      <div v-else-if="bodyType === 'raw'">
        <textarea v-model="localNodeData.body.data" rows="6" class="w-full p-2 border rounded font-mono text-sm" placeholder="Raw body (JSON, XML, etc.)" @input="saveChanges"></textarea>
        <VariableBrowser
          :available-variables="processVariables"
          :allow-create="false"
          :placeholder="'Insert variable...'"
          @update:modelValue="insertVariableToRaw"
          class="w-48 mt-2"
        />
        <div v-if="rawBodyJsonWarning && rawBodyHasVariable" class="mt-2 text-xs text-red-600 flex items-center gap-1">
          <Icon name="material-symbols:warning" class="w-4 h-4" />
          {{ rawBodyJsonWarning }}
        </div>
      </div>
      <div v-else-if="bodyType === 'binary'">
        <input type="file" @change="handleFileUpload" />
        <div v-if="localNodeData.body.data" class="mt-2 text-xs text-gray-600">Selected: {{ localNodeData.body.data }}</div>
      </div>
      <div v-else class="text-xs text-gray-500">No body will be sent.</div>
    </div>
    
    <!-- Step 6: Response Handling -->
    <div class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">6</span>
        </div>
        <h4 class="font-medium">Response Handling</h4>
      </div>
      
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <!-- Output Variable -->
        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Output Variable</label>
          <div class="flex gap-2">
            <VariableBrowser
              v-model="localNodeData.outputVariable"
              :availableVariables="availableVariables"
              :allowCreate="true"
              :allowObjectPath="false"
              @change="saveChanges"
            />
          </div>
          <p class="mt-1 text-xs text-gray-500">
            API response will be stored in this global variable for use in later steps
          </p>
        </div>
        
        <!-- Error Variable -->
        <div>
          <label class="block text-sm font-medium text-gray-700 mb-1">Error Variable</label>
          <div class="flex gap-2">
            <VariableBrowser
              v-model="localNodeData.errorVariable"
              :availableVariables="availableVariables"
              :allowCreate="true"
              :allowObjectPath="false"
              @change="saveChanges"
            />
          </div>
          <p class="mt-1 text-xs text-gray-500">
            Any API errors will be stored in this variable for error handling
          </p>
        </div>
      </div>
      
      <!-- Continue on Error -->
      <div class="mt-4">
        <label class="flex items-center">
          <input
            type="checkbox"
            v-model="localNodeData.continueOnError"
            class="form-checkbox h-4 w-4 text-indigo-600 focus:ring-indigo-500"
            @change="saveChanges"
          />
          <span class="ml-2 text-sm">Continue workflow execution even if API call fails</span>
        </label>
        <p class="mt-1 pl-6 text-xs text-gray-500">
          When enabled, the process will continue to the next step even if this API call fails
        </p>
      </div>
    </div>
    
    <!-- Step 7: Test API Call -->
    <div class="mb-6 bg-gray-50 p-4 rounded-md border border-gray-200">
      <div class="flex items-center mb-3">
        <div class="w-6 h-6 rounded-full bg-indigo-100 flex items-center justify-center mr-2">
          <span class="text-xs font-semibold text-indigo-600">7</span>
        </div>
        <h4 class="font-medium">Test API Call</h4>
      </div>
      
      <p class="text-sm text-gray-600 mb-3">
        Test your API configuration to verify it works as expected before saving
      </p>
      
      <div class="flex items-center gap-4">
        <RsButton @click="testApiCall" variant="primary" :disabled="!localNodeData.apiUrl || isLoading">
          <Icon name="material-symbols:send" class="mr-1" />
          {{ isLoading ? 'Testing...' : 'Test API Call' }}
        </RsButton>
        <div v-if="isLoading" class="text-gray-600 text-sm flex items-center">
          <Icon name="material-symbols:sync" class="animate-spin mr-2" />
          Testing API endpoint...
        </div>
      </div>

      <!-- API Test Results -->
      <div v-if="testResult" class="mt-4 p-4 rounded-md border"
        :class="testResult.success ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200'"
      >
        <div class="flex items-start justify-between">
          <div class="flex items-center">
            <Icon 
              :name="testResult.success ? 'material-symbols:check-circle' : 'material-symbols:error'" 
              :class="testResult.success ? 'text-green-500' : 'text-red-500'"
              class="w-5 h-5 mr-2"
            />
            <span :class="testResult.success ? 'text-green-700' : 'text-red-700'" class="font-medium">
              {{ testResult.success ? 'API Call Successful' : 'API Call Failed' }}
            </span>
          </div>
          <button @click="testResult = null" class="text-gray-400 hover:text-gray-600">
            <Icon name="material-symbols:close" class="w-4 h-4" />
          </button>
        </div>

        <!-- Success Response -->
        <div v-if="testResult.success" class="mt-3">
          <div class="text-sm text-gray-600 mb-2">Response stored in variable: <code class="bg-green-100 px-1">{{ localNodeData.outputVariable }}</code></div>
          <div class="bg-white border border-green-100 rounded p-3 max-h-60 overflow-auto">
            <pre class="text-xs font-mono whitespace-pre-wrap">{{ JSON.stringify(testResult.data, null, 2) }}</pre>
          </div>
        </div>

        <!-- Error Response -->
        <div v-else class="mt-3">
          <div class="text-sm text-red-600 mb-2">Error stored in variable: <code class="bg-red-100 px-1">{{ localNodeData.errorVariable }}</code></div>
          <div class="bg-white border border-red-100 rounded p-3 max-h-60 overflow-auto">
            <pre class="text-xs font-mono whitespace-pre-wrap text-red-600">{{ JSON.stringify(testResult.error, null, 2) }}</pre>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, nextTick } from 'vue';
import { useProcessBuilderStore } from '@/stores/processBuilder';
import VariableBrowser from './VariableBrowser.vue';
import KeyValueTable from './KeyValueTable.vue';

const AUTH_TYPES = [
  { value: 'none', label: 'None' },
  { value: 'bearer', label: 'Bearer Token' },
  { value: 'basic', label: 'Basic Auth' },
  { value: 'apiKey', label: 'API Key' }
];

const BODY_TYPES = [
  { value: 'none', label: 'None' },
  { value: 'form-data', label: 'form-data' },
  { value: 'x-www-form-urlencoded', label: 'x-www-form-urlencoded' },
  { value: 'raw', label: 'raw' },
  { value: 'binary', label: 'binary' }
];

const props = defineProps({
  nodeData: {
    type: Object,
    required: true
  },
  availableVariables: {
    type: Array,
    default: () => []
  }
});

const emit = defineEmits(['update']);

// Get the process store for creating variables
const processStore = useProcessBuilderStore();

// Local state for node data - create a deep copy to avoid mutation issues
const localNodeData = ref(migrateNodeData(props.nodeData));

// Computed for showing request body based on method
const showRequestBody = computed(() => {
  return ['POST', 'PUT', 'PATCH'].includes(localNodeData.value.apiMethod);
});

// Get available variables for dropdowns
const availableVariables = computed(() => {
  // Only use global variables, matching VariableManager implementation
  const globalVars = props.availableVariables?.map(v => ({
    name: v.name || 'unnamed',
    label: `${v.name} (${v.type || 'string'})`,
    type: v.type || 'string',
    value: v.value
  })) || [];

  return globalVars;
});

// Get process variables for substitution
const processVariables = computed(() => {
  // Use processStore.currentProcess.variables (object) and convert to array
  const varsObj = processStore.currentProcess?.variables || {};
  return Object.values(varsObj);
});

// Watch for changes from parent props
watch(() => props.nodeData, (newNodeData) => {
  if (newNodeData) localNodeData.value = migrateNodeData(newNodeData);
}, { immediate: true, deep: true });

// Function to create a new global variable
function createGlobalVariable(name, description = '') {
  if (!name) return;
  
  const newVariable = {
    name,
    type: 'object',
    scope: 'global',
    description: description || `API response from ${localNodeData.value.label}`
  };

  // Add the variable using the store's addVariable method
  processStore.addProcessVariable(newVariable);
  
  // Force a refresh of the component
  nextTick(() => {
    saveChanges();
  });
}

// Save changes by emitting them to parent
function saveChanges() {
  // Create a clean copy of the data to avoid reactivity issues
  const nodeDataCopy = JSON.parse(JSON.stringify(localNodeData.value));
  
  // Ensure variables exist before saving
  if (nodeDataCopy.outputVariable) {
    const existingVar = processStore.getProcessVariable(nodeDataCopy.outputVariable);
    if (!existingVar) {
      processStore.addProcessVariable({
        name: nodeDataCopy.outputVariable,
        type: 'object',
        scope: 'global',
        value: null,
        description: `API response from ${nodeDataCopy.label}`
      });
    }
  }
  
  if (nodeDataCopy.errorVariable) {
    const existingVar = processStore.getProcessVariable(nodeDataCopy.errorVariable);
    if (!existingVar) {
      processStore.addProcessVariable({
        name: nodeDataCopy.errorVariable,
        type: 'object',
        scope: 'global',
        value: null,
        description: `API error from ${nodeDataCopy.label}`
      });
    }
  }
  
  // Emit the updated data to parent
  emit('update', nodeDataCopy);
}

// Add these refs for handling the API test state
const isLoading = ref(false);
const testResult = ref(null);

// Update the testApiCall function
async function testApiCall() {
  if (!localNodeData.value.apiUrl) return;
  
  isLoading.value = true;
  testResult.value = null;
  
  try {
    // Get process variables for substitution
    const processVariables = {
      global: {},
      process: {}
    };
    
    // Extract variables from available variables
    if (props.availableVariables) {
      props.availableVariables.forEach(v => {
        processVariables.global[v.name] = v.value;
      });
    }
    
    // Call the test API endpoint
    const response = await fetch('/api/process/test-api-node', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        nodeConfig: {
          apiMethod: localNodeData.value.apiMethod,
          apiUrl: localNodeData.value.apiUrl,
          requestBody: localNodeData.value.requestBody,
          headers: JSON.parse(localNodeData.value.headers || '{}'),
          outputVariable: localNodeData.value.outputVariable,
          errorVariable: localNodeData.value.errorVariable,
          continueOnError: localNodeData.value.continueOnError
        },
        processVariables
      })
    });
    
    const result = await response.json();
    
    // Store the test result
    testResult.value = {
      success: response.ok && !result.error,
      data: result.data,
      error: result.error || (response.ok ? null : { message: 'API request failed' })
    };
    
    // Update variables in the store
    if (testResult.value.success && localNodeData.value.outputVariable) {
      processStore.updateProcessVariable(
        localNodeData.value.outputVariable,
        { value: result.data }
      );
    } else if (!testResult.value.success && localNodeData.value.errorVariable) {
      processStore.updateProcessVariable(
        localNodeData.value.errorVariable,
        { value: testResult.value.error }
      );
    }
  } catch (error) {
    console.error('API test error:', error);
    testResult.value = {
      success: false,
      error: {
        message: error.message || 'An error occurred while testing the API call',
        details: error.toString()
      }
    };
    
    // Store error in variable
    if (localNodeData.value.errorVariable) {
      processStore.updateProcessVariable(
        localNodeData.value.errorVariable,
        { value: testResult.value.error }
      );
    }
  } finally {
    isLoading.value = false;
  }
}

// Function to insert a variable at cursor position or append to end
function insertVariable(variableName, field) {
  if (!variableName) return;
  
  const varName = `{${variableName}}`;
  const textarea = document.getElementById(field);
  
  if (textarea.selectionStart || textarea.selectionStart === 0) {
    const startPos = textarea.selectionStart;
    const endPos = textarea.selectionEnd;
    
    localNodeData.value[field] = 
      textarea.value.substring(0, startPos) +
      varName +
      textarea.value.substring(endPos);
      
    // Reset cursor position after variable
    textarea.selectionStart = startPos + varName.length;
    textarea.selectionEnd = startPos + varName.length;
  } else {
    localNodeData.value[field] += varName;
  }
  
  saveChanges();
}

// Function to format JSON in a field
function formatJson(field) {
  try {
    const value = localNodeData.value[field];
    if (!value) return;
    
    const parsed = JSON.parse(value);
    localNodeData.value[field] = JSON.stringify(parsed, null, 2);
    saveChanges();
  } catch (error) {
    console.warn(`Failed to format JSON for ${field}:`, error);
  }
}

// Function to preview values with actual variable values
function getPreviewWithValues(field) {
  try {
    let text = localNodeData.value[field];
    if (!text) return '';
    
    // Replace all variables with their current values
    const variablePattern = /{([^}]+)}/g;
    text = text.replace(variablePattern, (match, varName) => {
      const variable = availableVariables.value.find(v => v.name === varName);
      if (variable) {
        // Handle different value types
        if (variable.value === null || variable.value === undefined) {
          return `"<${varName}>"`;  // Show variable name as placeholder
        }
        // Return the actual value or stringify complex objects
        return typeof variable.value === 'string' ? `"${variable.value}"` : JSON.stringify(variable.value);
      }
      return `"<${varName}>"`;  // Show variable name as placeholder for missing variables
    });
    
    // Try to format as JSON if possible
    try {
      const parsed = JSON.parse(text);
      return JSON.stringify(parsed, null, 2);
    } catch {
      return text;
    }
  } catch (error) {
    console.warn(`Preview error for ${field}:`, error);
    return localNodeData.value[field] || '';
  }
}

// --- Authorization Fields ---
function getAuthFields() {
  switch (localNodeData.value.authorization.type) {
    case 'bearer':
      return [
        { key: 'token', label: 'Token', type: 'text', placeholder: 'Bearer token', value: localNodeData.value.authorization.token || '' }
      ];
    case 'basic':
      return [
        { key: 'username', label: 'Username', type: 'text', placeholder: 'Username', value: localNodeData.value.authorization.username || '' },
        { key: 'password', label: 'Password', type: 'password', placeholder: 'Password', value: localNodeData.value.authorization.password || '' }
      ];
    case 'apiKey':
      return [
        { key: 'key', label: 'Key Name', type: 'text', placeholder: 'e.g. X-API-Key', value: localNodeData.value.authorization.key || '' },
        { key: 'value', label: 'Key Value', type: 'text', placeholder: 'API Key Value', value: localNodeData.value.authorization.value || '' },
        { key: 'in', label: 'Add To', type: 'select', options: [
          { value: 'header', label: 'Header' },
          { value: 'query', label: 'Query Param' }
        ], value: localNodeData.value.authorization.in || 'header' }
      ];
    default:
      return [];
  }
}
function updateAuthField(field, value) {
  localNodeData.value.authorization[field] = value;
  saveChanges();
}

// --- Body Data Handling ---
function getBodyData() {
  if (['form-data', 'x-www-form-urlencoded'].includes(localNodeData.value.body.type)) {
    return Array.isArray(localNodeData.value.body.data) ? localNodeData.value.body.data : [];
  }
  if (localNodeData.value.body.type === 'raw') {
    return typeof localNodeData.value.body.data === 'string' ? localNodeData.value.body.data : '';
  }
  if (localNodeData.value.body.type === 'binary') {
    return localNodeData.value.body.data || null;
  }
  return [];
}
function setBodyData(val) {
  if (['form-data', 'x-www-form-urlencoded'].includes(localNodeData.value.body.type)) {
    localNodeData.value.body.data = val;
  } else if (localNodeData.value.body.type === 'raw') {
    localNodeData.value.body.data = val;
  } else if (localNodeData.value.body.type === 'binary') {
    localNodeData.value.body.data = val;
  }
  saveChanges();
}

// --- File Upload for Binary ---
function handleFileUpload(e) {
  const file = e.target.files[0];
  if (file) {
    localNodeData.value.body.data = file.name;
    saveChanges();
  }
}

// --- JSON Validation for Raw Body ---
const rawBodyIsJson = computed(() => {
  if (bodyType.value !== 'raw') return false;
  const val = localNodeData.value.body?.data || '';
  // Only check if it looks like JSON
  if (!val.trim().startsWith('{') && !val.trim().startsWith('[')) return false;
  try {
    // Replace {variable} with a string to allow parsing
    const replaced = val.replace(/\{[a-zA-Z0-9_]+\}/g, '"VAR"');
    JSON.parse(replaced);
    return true;
  } catch {
    return false;
  }
});
const rawBodyHasVariable = computed(() => {
  if (bodyType.value !== 'raw') return false;
  const val = localNodeData.value.body?.data || '';
  return /\{[a-zA-Z0-9_]+\}/.test(val);
});
const rawBodyJsonWarning = computed(() => {
  if (bodyType.value !== 'raw') return '';
  const val = localNodeData.value.body?.data || '';
  if (!val.trim().startsWith('{') && !val.trim().startsWith('[')) return '';
  try {
    // Replace both quoted and unquoted {variable} with "VAR"
    let replaced = val
      .replace(/"\{[a-zA-Z0-9_]+\}"/g, '"VAR"') // quoted
      .replace(/\{[a-zA-Z0-9_]+\}/g, '"VAR"');    // unquoted
    JSON.parse(replaced);
    return '';
  } catch {
    return 'Warning: Your request body is not valid JSON. Make sure variables are inside quotes.';
  }
});

// --- Variable Insertion Helper ---
function insertVariableToRaw(varName) {
  if (!varName) return;
  const textarea = document.querySelector('textarea[placeholder="Raw body (JSON, XML, etc.)"]');
  let val = localNodeData.value.body.data || '';
  let insertText = `{${varName}}`;
  // Detect if inside quotes
  let cursor = textarea ? textarea.selectionStart : val.length;
  let before = val.substring(0, cursor);
  let after = val.substring(cursor);
  // Check if body looks like JSON
  const isJson = val.trim().startsWith('{') || val.trim().startsWith('[');
  // Check if cursor is inside quotes
  let insideQuotes = false;
  if (isJson && textarea) {
    // Count quotes before cursor
    const quotesBefore = before.split('"').length - 1;
    insideQuotes = quotesBefore % 2 === 1;
  }
  if (isJson && !insideQuotes) {
    insertText = `"{${varName}}"`;
  }
  // Insert at cursor
  localNodeData.value.body.data = before + insertText + after;
  saveChanges();
  // Move cursor after inserted variable
  nextTick(() => {
    if (textarea) {
      const newPos = before.length + insertText.length;
      textarea.focus();
      textarea.selectionStart = textarea.selectionEnd = newPos;
    }
  });
}

// --- UI State ---
const showParams = computed(() => ['GET', 'DELETE', 'HEAD', 'OPTIONS'].includes(localNodeData.value.apiMethod));

// Defensive computed for bodyType and authType
const bodyType = computed(() => localNodeData.value.body && localNodeData.value.body.type ? localNodeData.value.body.type : 'none');
const authType = computed(() => localNodeData.value.authorization && localNodeData.value.authorization.type ? localNodeData.value.authorization.type : 'none');

function migrateNodeData(data) {
  const migrated = { ...data };
  // Headers
  if (typeof migrated.headers === 'string') {
    try {
      const obj = JSON.parse(migrated.headers);
      migrated.headers = Object.entries(obj).map(([key, value]) => ({ key, value }));
    } catch {
      migrated.headers = [];
    }
  } else if (!Array.isArray(migrated.headers)) {
    migrated.headers = [];
  }
  // Params
  if (!Array.isArray(migrated.params)) migrated.params = [];
  // Body
  if (!migrated.body || typeof migrated.body !== 'object') {
    migrated.body = { type: 'none', data: [] };
  } else {
    if (!migrated.body.type) migrated.body.type = 'none';
    if (migrated.body.data === undefined) migrated.body.data = migrated.body.type === 'raw' ? '' : [];
  }
  if (typeof data.requestBody === 'string' && (!migrated.body || migrated.body.type === 'none')) {
    migrated.body = { type: 'raw', data: data.requestBody };
  }
  // Auth
  if (!migrated.authorization || typeof migrated.authorization !== 'object') migrated.authorization = { type: 'none' };
  if (!migrated.authorization.type) migrated.authorization.type = 'none';
  return migrated;
}
</script>

<style scoped>
.form-checkbox {
  @apply text-indigo-600 focus:ring-indigo-500;
}

.form-select {
  @apply border rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 p-2;
}

code {
  font-family: monospace;
  border-radius: 0.25rem;
}
</style> 