import {
  createAction,
  Property,
  ActionContext,
  StoreScope,
  DynamicPropsValue,
} from '@activepieces/pieces-framework';
import { PauseType } from '@activepieces/shared';
import { QueueManager } from '../common/queue-manager';
import { MCPManager } from '../common/mcp-manager';

/**
 * Standalone helper function to execute a queued item.
 * This is called after the pause resumes.
 */
async function executeQueuedItem(context: ActionContext, queueItemId: string) {
  let debugInfo: any = { step: 'starting', queueItemId };
  
  try {
    debugInfo.step = 'getQueueItem';
    let item = await QueueManager.getQueueItem(context, queueItemId);
    if (!item) {
      throw new Error(`Queue item not found for ID: ${queueItemId} [Step: getQueueItem]`);
    }
    debugInfo.item = { queueId: item.queueId, status: item.status, releaseTime: item.releaseTime };

    /* Validate position / repause if needed */
    debugInfo.step = 'validateQueuePosition';
    const validation = await QueueManager.validateQueuePosition(
      context,
      queueItemId,
    );
    debugInfo.validation = validation;
    
    if (validation.shouldRepause && validation.newReleaseTime) {
      debugInfo.step = 'repause';
      debugInfo.newReleaseTime = validation.newReleaseTime;
      await context.run.pause({
        pauseMetadata: {
          type: PauseType.DELAY,
          resumeDateTime: new Date(validation.newReleaseTime).toUTCString(),
        },
      });
      // After pause, re-run this function
      return await executeQueuedItem(context, queueItemId);
    }
    if (!validation.isValid) {
      throw new Error(`Queue validation failed: ${validation.reason} [Step: validateQueuePosition] [Debug: ${JSON.stringify(debugInfo)}]`);
    }

    /* Execute */
    debugInfo.step = 'getQueueConfiguration';
    const queue = await QueueManager.getQueueConfiguration(context, item.queueId);
    if (!queue) {
      throw new Error(`Queue config missing for ID: ${item.queueId} [Step: getQueueConfiguration] [Debug: ${JSON.stringify(debugInfo)}]`);
    }
    debugInfo.queue = { id: queue.id, mcpToolName: queue.mcpToolName };

    debugInfo.step = 'updateItemStatus-processing';
    await QueueManager.updateItemStatus(context, queueItemId, 'processing');

    // Get MCP server URL from auth
    debugInfo.step = 'checkAuth';
    const authConfig = context.auth as { mcpServerUrl?: string };
    if (!authConfig.mcpServerUrl) {
      throw new Error(`MCP Server URL is required in auth configuration [Step: checkAuth] [Auth: ${JSON.stringify(authConfig)}] [Debug: ${JSON.stringify(debugInfo)}]`);
    }
    debugInfo.mcpServerUrl = authConfig.mcpServerUrl;

    const toolName = queue.mcpToolName;
    if (!toolName) {
      throw new Error(`Queue configuration missing tool name [Step: checkAuth] [Queue: ${JSON.stringify(queue)}] [Debug: ${JSON.stringify(debugInfo)}]`);
    }
    debugInfo.toolName = toolName;
    
    debugInfo.step = 'executeMCPAction';
    const config = { 
      mcpServerUrl: authConfig.mcpServerUrl
    };
    const result = await MCPManager.executeMCPAction(toolName, item.actionConfig, config);
    debugInfo.mcpResult = { success: result.success, hasError: !!result.error };

    if (result.success) {
      debugInfo.step = 'cleanup-success';
      await QueueManager.incrementUsageCounters(context, queue.id);
      await QueueManager.markExecutionComplete(context, queue.id, queueItemId);
      await QueueManager.updateItemStatus(context, queueItemId, 'completed');
      await QueueManager.cleanupQueueItem(
        context,
        queue.id,
        queueItemId,
        item.releaseTime,
      );
      const finalStatus = await QueueManager.getQueueStatus(context, queue.id);
      return {
        success: true,
        queueItemId,
        executedAt: new Date().toISOString(),
        result: result.result,
        status: finalStatus,
        debugInfo: debugInfo,
      };
    } else {
      debugInfo.step = 'cleanup-failure';
      debugInfo.mcpError = result.error;
      await QueueManager.markExecutionComplete(context, queue.id, queueItemId);
      await QueueManager.updateItemStatus(
        context,
        queueItemId,
        'failed',
        result.error,
      );
      await QueueManager.cleanupQueueItem(
        context,
        queue.id,
        queueItemId,
        item.releaseTime,
      );
      throw new Error(`MCP execution failed: ${result.error || 'Unknown error'} [Step: executeMCPAction] [Debug: ${JSON.stringify(debugInfo)}]`);
    }
  } catch (error) {
    // Add debug context to the original error for post-pause execution debugging
    if (error instanceof Error) {
      error.message = `${error.message} [DEBUG - LastStep: ${debugInfo.step}, Context: ${JSON.stringify(debugInfo)}]`;
    }
    throw error;
  }
}

/**
 * Helper to generate dynamic properties based on MCP tool schema
 */
async function generateDynamicProps(data: DynamicPropsValue): Promise<Record<string, any>> {
  const queueId = data.queueId as string | undefined;
  
  if (!queueId) return {};

  try {
    // Extract tool name from queue ID (format: mcp_toolname_queue)
    const toolNameMatch = queueId.match(/^mcp_(.+)_queue$/);
    if (!toolNameMatch) {
      throw new Error(`Invalid queue ID format: ${queueId}`);
    }
    
    const toolName = toolNameMatch[1];
    
    const authConfig = data.auth as { mcpServerUrl?: string };
    if (!authConfig.mcpServerUrl) {
      throw new Error('MCP Server URL not found in auth config');
    }

    const config = { mcpServerUrl: authConfig.mcpServerUrl };
    const tools = await MCPManager.listMCPTools(config);
    const tool = tools.find((t: any) => t.name === toolName);
    
    if (!tool) {
      throw new Error(`Tool not found: ${toolName}`);
    }
    
    if (!tool.inputSchema || !tool.inputSchema.properties) {
      throw new Error(`Tool has no input schema: ${toolName}`);
    }

    const dynamicProps: Record<string, any> = {};
    const schema = tool.inputSchema;
    const required = schema.required || [];

    // Generate properties based on the tool's input schema
    Object.keys(schema.properties).forEach(propName => {
      const propDef = schema.properties[propName];
      const isRequired = required.includes(propName);
      
      const displayName = propName.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
      const description = propDef.description || `${propName} parameter for ${tool.name}`;
      
      if (propDef.type === 'string') {
        dynamicProps[propName] = Property.ShortText({
          displayName,
          description,
          required: isRequired,
        });
      } else if (propDef.type === 'number' || propDef.type === 'integer') {
        dynamicProps[propName] = Property.Number({
          displayName,
          description,
          required: isRequired,
        });
      } else if (propDef.type === 'boolean') {
        dynamicProps[propName] = Property.Checkbox({
          displayName,
          description,
          required: isRequired,
        });
      } else if (propDef.type === 'array') {
        dynamicProps[propName] = Property.Array({
          displayName,
          description,
          required: isRequired,
        });
      } else {
        // For objects or unknown types, use JSON
        dynamicProps[propName] = Property.Json({
          displayName,
          description,
          required: isRequired,
        });
      }
    });

    return dynamicProps;
  } catch (error) {
    // Return empty object if dynamic props generation fails
    // This allows the form to load, error will surface during execution
    return {};
  }
}

export const addToQueue = createAction({
  name: 'add_to_queue',
  displayName: 'Add to Queue',
  description: 'Add an item to an existing queue and pause until execution time',
  props: {
    queueId: Property.Dropdown({
      displayName: 'Select Queue',
      description: 'Available MCP tool queues (run Create/Update Queue first to create queues)',
      required: true,
      refreshers: [],
      options: async ({ auth }) => {
        const authConfig = auth as { mcpServerUrl?: string };
        if (!authConfig.mcpServerUrl) {
          return { 
            options: [],
            placeholder: 'MCP Server URL required in auth configuration'
          };
        }
        
        try {
          const config = { mcpServerUrl: authConfig.mcpServerUrl };
          const tools = await MCPManager.listMCPTools(config);
          
          if (tools.length === 0) {
            return {
              options: [],
              placeholder: 'No MCP tools found'
            };
          }
          
          // Generate queue options based on available tools
          // The actual existence check will happen during execution
          const queueOptions = tools.map((tool: any) => ({
            label: `Queue: ${tool.name} - ${tool.description || 'No description'}`,
            value: `mcp_${tool.name}_queue`,
          }));
          
          return {
            options: queueOptions,
            placeholder: 'Select a tool queue'
          };
        } catch (error) {
          return {
            options: [],
            placeholder: `Failed to connect to MCP server: ${error instanceof Error ? error.message : String(error)}`
          };
        }
      },
    }),

    toolParameters: Property.DynamicProperties({
      displayName: 'Tool Parameters',
      description: 'Parameters for the MCP tool (generated from tool schema)',
      required: false,
      refreshers: ['queueId'],
      props: generateDynamicProps,
    }),
  },

  async run(context: ActionContext) {
    const {
      queueId,
      ...otherProps
    } = context.propsValue;

    if (!queueId) {
      throw new Error('Queue ID is required');
    }

    const queue = await QueueManager.getQueueConfiguration(
      context,
      queueId as string,
    );
    if (!queue) {
      throw new Error(
        `Queue "${queueId}" not found. Run Create/Update Queue first.`,
      );
    }

    const limits = await QueueManager.checkRateLimits(context, queueId as string);

    const { delayMs } = await QueueManager.calculateItemDelay(
      context,
      queue,
      limits,
    );

    // Extract the dynamic properties as action config (excluding queueId)
    const actionConfig = otherProps;

    const toolName = queue.mcpToolName;
    
    if (!toolName) {
      throw new Error('Queue configuration missing tool name');
    }

    const { queueItemId, releaseTime } = await QueueManager.addItemToQueue(
      context,
      queueId as string,
      toolName,
      actionConfig,
      delayMs,
    );

    await context.store.put(
      'current_queue_item_id',
      queueItemId,
      StoreScope.PROJECT,
    );

    await context.run.pause({
      pauseMetadata: {
        type: PauseType.DELAY,
        resumeDateTime: new Date(releaseTime).toUTCString(),
      },
    });

    /* ============ RESUME ============ */
    const storedQueueItemId = await context.store.get<string>(
      'current_queue_item_id',
      StoreScope.PROJECT,
    );
    if (!storedQueueItemId) {
      throw new Error('Could not find queue item ID after resume');
    }
    return await executeQueuedItem(context, storedQueueItemId);
  },
});
