import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { randomBytes } from 'crypto';
import { z } from 'zod';
import { adfToText } from '../../../dist/utils/adf-converter.js';

const generateId = () => randomBytes(16).toString('hex');

const SuggestedChangeSchema = z.object({
  field: z.enum(['summary', 'description', 'labels'])
    .describe('Which ticket field this change applies to'),
  
  original: z.string()
    .describe('The original value of the field'),
  
  suggested: z.string()
    .describe('The suggested new value for the field'),
  
  reasoning: z.string()
    .describe('Explanation of why this change improves the ticket')
});

// Shared validation schema (used by both AI agent output and node output)
const ValidationSchema = z.object({
  canProceed: z.boolean()
    .describe('Whether the ticket has enough information to proceed with implementation'),
  
  status: z.enum(['valid', 'insufficient_context', 'unclear_requirements', 'invalid_ticket', 'needs_clarification'])
    .describe('Status indicating why we can or cannot proceed'),
  
  reasoning: z.string()
    .describe('Explanation of the validation decision - why the ticket is valid or what is missing'),
  
  blockers: z.array(z.string())
    .nullable()
    .describe('Specific blockers preventing implementation (if canProceed is false), null if none')
});

const AnalysisOutputSchema = z.object({
  validation: ValidationSchema
    .describe('Validation of whether ticket is ready for implementation'),
  
  suggestedChanges: z.array(SuggestedChangeSchema)
    .describe('Array of suggested changes to ticket fields'),
  
  technicalAnalysis: z.object({
    requirements: z.array(z.string())
      .describe('Key requirements extracted from the ticket'),
    
    affectedFiles: z.array(z.string())
      .nullable()
      .describe('Files that will likely need changes (null if unknown). Each path MUST start with a top-level repo directory name (e.g. "my-api/dist/...")'),
    
    complexity: z.enum(['simple', 'medium', 'complex', 'unknown'])
      .describe('Estimated complexity of the implementation'),
    
    risks: z.array(z.string())
      .nullable()
      .describe('Potential risks or challenges, null if none')
  }).describe('Technical analysis of the implementation'),
  
  overallSummary: z.string()
    .describe('Brief summary of the analysis findings'),
  
  implementationPlan: z.string()
    .describe('Markdown implementation plan for the code generation agent. Include: 1) A brief summary of what to build, 2) Which top-level repositories need changes — MUST be the exact directory names from Repository Information, NEVER internal sub-modules or Maven modules, 3) An ordered TODO checklist where every file path starts with a top-level repo name (e.g. "In my-api/dist/.../Foo.java" NOT "src/.../Foo.java"), 4) Key technical details like function signatures, patterns to follow, and gotchas. This will be passed directly to the coding agent as its instructions.')
});

// Node output schema (what this node returns to state)
const AnalyzeTicketNodeOutputSchema = z.object({
  success: z.boolean(),
  analysis: z.object({
    raw: z.string(),
    structured: z.object({
      validation: ValidationSchema,
      suggestedChanges: z.array(z.any()),
      technicalAnalysis: z.any(),
      overallSummary: z.string(),
      implementationPlan: z.string()
    }),
    timestamp: z.string()
  }),
  validation: ValidationSchema  // Exposed at root for easy access by conditional logic
});

export const analyzeTicketNode = {
  name: 'analyze_ticket',
  outputSchema: AnalyzeTicketNodeOutputSchema,
  
  execute: async (context) => {
    console.log('\n🔍 Analyzing ticket with Cursor Agent...');
    
    // Extract from context (new unified signature)
    const { state, invokeAgent } = context;
    const { workspace, ticketContext, repos, model } = state.getAll();
    const aiModel = model || ticketContext.model || 'auto';
    
    // Prepare description text
    let descriptionText = ticketContext.description || 'No description provided';
    if (typeof descriptionText === 'object') {
      descriptionText = adfToText(descriptionText);
      console.log('⚠️  Description is in ADF format, converted to plain text');
    }
    
    // Build prompt values for template rendering
    const promptValues = {
      ticketKey: ticketContext.ticketKey,
      ticketSummary: ticketContext.summary,
      ticketDescription: descriptionText,
      ticketType: ticketContext.type || 'Task',
      ticketPriority: ticketContext.priority || 'Medium',
      ticketLabels: Array.isArray(ticketContext.labels) ? ticketContext.labels.join(', ') : (ticketContext.labels || 'None'),
      ticketComponents: Array.isArray(ticketContext.components) 
        ? ticketContext.components.map(c => c.name || c).join(', ') || 'None'
        : (ticketContext.components || 'None'),
      additionalContext: ticketContext.additionalContext || null,
      repositories: Array.isArray(repos) ? repos.map(r => ({
        name: r.name,
        ...detectProjectInfo(join(workspace, r.name))
      })) : []
    };
    
    const images = (ticketContext.imageAttachments || [])
      .filter(att => att.base64)
      .map(att => ({ mimeType: att.mimeType, base64: att.base64, filename: att.filename }));

    if (images.length > 0) {
      console.log(`🖼️  Including ${images.length} image attachment(s) for vision analysis`);
    }

    console.log(`🚀 Running AI Agent to analyze ticket with model: ${aiModel}...`);
    
    // Use context.invokeAgent - template rendering handled by framework
    const validatedOutput = await invokeAgent(promptValues, { 
      model: aiModel,
      schema: AnalysisOutputSchema,
      images
    });
    
    // Handle both structured response and raw response scenarios
    const output = validatedOutput?.structured || validatedOutput;
    
    const suggestedChanges = (output?.suggestedChanges || []).map(change => ({
      id: generateId(),
      ...change,
      status: 'pending'
    }));
    
    const analysis = {
      raw: JSON.stringify(output),
      structured: {
        validation: output?.validation || { canProceed: false, status: 'invalid_ticket', reasoning: 'Failed to parse validation' },
        suggestedChanges,
        technicalAnalysis: output?.technicalAnalysis || { requirements: [], complexity: 'unknown' },
        overallSummary: output?.overallSummary || 'Analysis failed',
        implementationPlan: output?.implementationPlan || 'No plan generated'
      },
      timestamp: new Date().toISOString()
    };
    
    console.log(`✅ Parsed ${suggestedChanges.length} suggested changes`);
    
    if (!output?.validation?.canProceed) {
      console.log(`⚠️  Ticket validation failed: ${output?.validation?.status || 'unknown'}`);
      console.log(`   Reasoning: ${output?.validation?.reasoning || 'No reasoning provided'}`);
      if (output?.validation?.blockers) {
        console.log(`   Blockers:`);
        output.validation.blockers.forEach(blocker => {
          console.log(`     - ${blocker}`);
        });
      }
    }
    
    return {
      success: true,
      analysis,
      validation: output?.validation || { canProceed: false, status: 'invalid_ticket', reasoning: 'No validation data' }
    };
  }
};

function detectProjectInfo(repoPath) {
  const info = {
    framework: 'Unknown',
    language: 'Unknown',
    packageManager: 'Unknown'
  };
  
  if (existsSync(join(repoPath, 'package.json'))) {
    info.language = 'JavaScript/TypeScript';
    const pkg = JSON.parse(readFileSync(join(repoPath, 'package.json'), 'utf-8'));
    
    if (pkg.dependencies?.react) info.framework = 'React';
    if (pkg.dependencies?.next) info.framework = 'Next.js';
    if (pkg.dependencies?.vue) info.framework = 'Vue';
    if (pkg.dependencies?.express) info.framework = 'Express';
    
    if (existsSync(join(repoPath, 'yarn.lock'))) info.packageManager = 'yarn';
    else if (existsSync(join(repoPath, 'pnpm-lock.yaml'))) info.packageManager = 'pnpm';
    else info.packageManager = 'npm';
  }
  
  return info;
}
