/**
 * Weft Engine
 * LLM-powered code generation pipeline
 * 
 * The Ghost Factory that builds itself
 */

import { prisma } from '@/lib/db';
import { callLLM, parseJSONResponse, LLMProvider } from './llm';
import {
  ARCHITECTURE_PROMPT,
  PRISMA_GENERATOR_PROMPT,
  API_ROUTE_GENERATOR_PROMPT,
  UI_COMPONENT_GENERATOR_PROMPT,
} from './prompts';
import type { DeploymentPlatform } from '@/lib/dark-factory/deployment-targets';
import {
  generateElectronMain,
  generateElectronPreload,
  generateElectronBuilderConfig,
  generateElectronPackageJson,
  generatePWAManifest,
  generateServiceWorker,
  generateCapacitorConfig,
  generateTauriConfig,
  generateOfflinePage,
} from '@/lib/dark-factory/deployment-targets';

export interface WeftRequest {
  prompt: string;
  projectId?: string;
  requestId?: string; // If provided, use existing request instead of creating new one
  context?: {
    businessDomain?: string;
    existingEntities?: string[];
  };
  /** Target language/framework for code generation */
  language?: string;
  /** Target database for schema generation */
  database?: string;
  /** Expert mode: pre-edited architecture spec from user review */
  expertSpec?: {
    summary: string;
    entities: Array<{
      name: string;
      description: string;
      fields: Array<{ name: string; type: string; required: boolean; description: string }>;
      relations: Array<{ name: string; type: string; target: string; description: string }>;
    }>;
    endpoints: Array<{
      path: string;
      method: string;
      description: string;
      businessLogic: string[];
    }>;
    namingConventions?: {
      modelCase: string;
      fieldCase: string;
      endpointCase: string;
      fileNaming: string;
    };
  };
  /** Per-stage LLM model selection */
  llmConfig?: {
    architecture?: string;
    database?: string;
    api?: string;
    ui?: string;
  };
  /** Deployment targets for packaging */
  deploymentTargets?: DeploymentPlatform[];
}

export interface WeftArtifact {
  type: 'prisma_model' | 'api_route' | 'ui_component' | 'typescript_type' | 'deployment_config';
  path: string;
  content: string;
  description: string;
}

export interface WeftStage {
  name: string;
  status: 'pending' | 'running' | 'completed' | 'failed';
  startedAt?: Date;
  completedAt?: Date;
  output?: unknown;
  error?: string;
}

export interface WeftResult {
  taskId: string;
  status: 'completed' | 'failed' | 'awaiting_approval';
  artifacts: WeftArtifact[];
  stages: WeftStage[];
  architecture?: Architecture;
  error?: string;
}

export interface Architecture {
  summary: string;
  models: ModelDef[];
  endpoints: EndpointDef[];
  uiComponents: UIComponentDef[];
  integrations: {
    existingModels: string[];
    newRoutes: string[];
  };
}

interface ModelDef {
  name: string;
  description: string;
  fields: { name: string; type: string; required: boolean; description: string }[];
  relations: { name: string; type: string; target: string; description: string }[];
  indexes: string[];
  businessRules: string[];
}

interface EndpointDef {
  path: string;
  method: string;
  description: string;
  requestBody?: Record<string, string>;
  responseType: string;
  businessLogic: string[];
}

interface UIComponentDef {
  name: string;
  type: string;
  description: string;
  features: string[];
  dataSource: string;
}

export class WeftEngine {
  private taskId: string = '';
  private stages: WeftStage[] = [];
  private artifacts: WeftArtifact[] = [];
  private provider: LLMProvider = 'abacus';
  private targetLanguage: string = 'typescript-react';
  private targetDatabase: string = 'postgresql';
  private llmConfig: { architecture?: string; database?: string; api?: string; ui?: string } = {};
  private deploymentTargets: DeploymentPlatform[] = ['web'];

  async execute(request: WeftRequest): Promise<WeftResult> {
    // Store language/database for use in prompts
    this.targetLanguage = request.language || 'typescript-react';
    this.targetDatabase = request.database || 'postgresql';
    this.llmConfig = request.llmConfig || {};
    this.deploymentTargets = request.deploymentTargets || ['web'];
    
    // Create task record
    const task = await this.createTask(request);
    this.taskId = task.id;

    try {
      // Stage 1: Architecture Design (or use expert-provided spec)
      const architecture = await this.runStage('architecture', async () => {
        if (request.expertSpec) {
          // Expert mode: convert user-edited spec to Architecture format
          return this.convertExpertSpec(request);
        }
        return await this.designArchitecture(request);
      });

      if (!architecture || !architecture.models?.length) {
        throw new Error('Failed to design architecture. Please provide more details about what you want to build.');
      }

      // Stage 2: Generate Prisma Models
      await this.runStage('prisma_models', async () => {
        return await this.generatePrismaModels(architecture);
      });

      // Stage 3: Generate API Routes
      await this.runStage('api_routes', async () => {
        return await this.generateApiRoutes(architecture);
      });

      // Stage 4: Generate UI Components
      await this.runStage('ui_components', async () => {
        return await this.generateUIComponents(architecture);
      });

      // Stage 5: Generate Deployment Packaging (if targets beyond web)
      if (this.deploymentTargets.length > 1 || !this.deploymentTargets.includes('web')) {
        await this.runStage('deployment_packaging', async () => {
          return await this.generateDeploymentPackaging(request);
        });
      }

      // Stage 6: Save artifacts
      await this.runStage('finalize', async () => {
        return await this.saveArtifacts();
      });

      // Update task status
      await this.updateTaskStatus('awaiting_approval');

      return {
        taskId: this.taskId,
        status: 'awaiting_approval',
        artifacts: this.artifacts,
        stages: this.stages,
        architecture,
      };

    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      await this.updateTaskStatus('failed', errorMessage);

      return {
        taskId: this.taskId,
        status: 'failed',
        artifacts: [],
        stages: this.stages,
        error: errorMessage,
      };
    }
  }

  private async createTask(request: WeftRequest): Promise<{ id: string; requestId: string }> {
    let requestId: string = request.requestId || '';
    
    // If no requestId provided, create a new GenerationRequest
    if (!request.requestId) {
      const dbRequest = await prisma.generationRequest.create({
        data: {
          prompt: request.prompt,
          context: request.context ? JSON.parse(JSON.stringify(request.context)) : null,
          priority: 'normal',
          requestedBy: 'human',
          status: 'processing',
          projectId: request.projectId || null,
        },
      });
      requestId = dbRequest.id;
    } else {
      // Update existing request status
      await prisma.generationRequest.update({
        where: { id: requestId },
        data: { status: 'processing' },
      });
    }

    const task = await prisma.generationTask.create({
      data: {
        requestId: requestId,
        status: 'parsing',
        stages: [],
      },
    });

    return { id: task.id, requestId };
  }

  private async runStage<T>(
    stageName: string,
    fn: () => Promise<T>
  ): Promise<T> {
    const stage: WeftStage = {
      name: stageName,
      status: 'running',
      startedAt: new Date(),
    };
    this.stages.push(stage);

    try {
      const result = await fn();
      stage.status = 'completed';
      stage.completedAt = new Date();
      stage.output = result;

      await this.updateStages();
      return result;
    } catch (error) {
      stage.status = 'failed';
      stage.completedAt = new Date();
      stage.error = error instanceof Error ? error.message : 'Unknown error';

      await this.updateStages();
      throw error;
    }
  }

  private async designArchitecture(request: WeftRequest): Promise<Architecture> {
    const userPrompt = this.buildArchitecturePrompt(request);

    console.log('[Weft] Designing architecture for:', request.prompt.substring(0, 100));

    const archModel = this.llmConfig.architecture;
    const response = await callLLM(
      [
        { role: 'system', content: ARCHITECTURE_PROMPT },
        { role: 'user', content: userPrompt },
      ],
      { temperature: 0.3, maxTokens: 3000, jsonMode: true, model: archModel },
      this.provider
    );

    console.log('[Weft] LLM response length:', response.length);

    try {
      const architecture = parseJSONResponse<Architecture>(response);
      console.log('[Weft] Architecture parsed successfully:', {
        models: architecture.models?.length || 0,
        endpoints: architecture.endpoints?.length || 0,
        components: architecture.uiComponents?.length || 0,
      });
      return architecture;
    } catch (parseError) {
      console.error('[Weft] JSON parse failed, raw response:', response.substring(0, 500));
      throw new Error(`Architecture design failed: ${parseError instanceof Error ? parseError.message : 'Invalid response format'}`);
    }
  }

  private convertExpertSpec(request: WeftRequest): Architecture {
    const spec = request.expertSpec!;
    
    const models: ModelDef[] = spec.entities.map(entity => ({
      name: entity.name,
      description: entity.description,
      fields: entity.fields.map(f => ({
        name: f.name,
        type: f.type,
        required: f.required,
        description: f.description,
      })),
      relations: entity.relations.map(r => ({
        name: r.name,
        type: r.type,
        target: r.target,
        description: r.description,
      })),
      indexes: [],
      businessRules: [],
    }));

    const endpoints: EndpointDef[] = spec.endpoints.map(ep => ({
      path: ep.path,
      method: ep.method,
      description: ep.description,
      businessLogic: ep.businessLogic || [],
      responseType: 'JSON',
    }));

    // Generate basic UI components based on models
    const uiComponents: UIComponentDef[] = models.length > 0 ? [
      {
        name: `${models[0].name}Dashboard`,
        type: 'dashboard',
        description: `Dashboard view for managing ${models.map(m => m.name).join(', ')}`,
        features: ['List view', 'Create form', 'Detail view', 'Filters'],
        dataSource: endpoints[0]?.path || '/api/data',
      },
    ] : [];

    return {
      summary: spec.summary,
      models,
      endpoints,
      uiComponents,
      integrations: {
        existingModels: [],
        newRoutes: endpoints.map(e => e.path),
      },
    };
  }

  private buildArchitecturePrompt(request: WeftRequest): string {
    let prompt = `Design a complete module for this request:\n\n"${request.prompt}"`;

    // Include language and database preferences
    prompt += `\n\nTarget Language/Framework: ${this.targetLanguage}`;
    prompt += `\nTarget Database: ${this.targetDatabase}`;

    if (request.context?.businessDomain) {
      prompt += `\n\nBusiness Domain: ${request.context.businessDomain}`;
    }
    if (request.context?.existingEntities?.length) {
      prompt += `\n\nExisting entities to integrate with: ${request.context.existingEntities.join(', ')}`;
    }

    prompt += `\n\nBe thorough and think enterprise-grade. Include all necessary models, endpoints, and UI components.`;
    prompt += `\nGenerate code and schemas appropriate for the target language and database specified above.`;

    return prompt;
  }

  private async generatePrismaModels(architecture: Architecture): Promise<void> {
    for (const model of architecture.models) {
      const prompt = `Generate a database model based on this specification:\n\nTarget Language: ${this.targetLanguage}\nTarget Database: ${this.targetDatabase}\n\n${JSON.stringify(model, null, 2)}`;

      const dbSpecificPrompt = this.getDatabasePrompt();
      const dbModel = this.llmConfig.database;

      const response = await callLLM(
        [
          { role: 'system', content: dbSpecificPrompt },
          { role: 'user', content: prompt },
        ],
        { temperature: 0.2, maxTokens: 2000, model: dbModel },
        this.provider
      );

      // Extract prisma code from response
      const prismaCode = this.extractCodeBlock(response, 'prisma') || response;

      this.artifacts.push({
        type: 'prisma_model',
        path: `prisma/models/${model.name.toLowerCase()}.prisma`,
        content: prismaCode.trim(),
        description: model.description,
      });
    }
  }

  private async generateApiRoutes(architecture: Architecture): Promise<void> {
    // Group endpoints by base path
    const groupedEndpoints = this.groupEndpointsByPath(architecture.endpoints);

    const langSpecificPrompt = this.getApiRoutePrompt();
    const apiModel = this.llmConfig.api;

    for (const [basePath, endpoints] of Object.entries(groupedEndpoints)) {
      const prompt = `Generate API route(s) for these endpoints:\n\nTarget Language: ${this.targetLanguage}\nTarget Database: ${this.targetDatabase}\nBase Path: ${basePath}\n\nEndpoints:\n${JSON.stringify(endpoints, null, 2)}\n\nModels available:\n${architecture.models.map(m => m.name).join(', ')}`;

      const response = await callLLM(
        [
          { role: 'system', content: langSpecificPrompt },
          { role: 'user', content: prompt },
        ],
        { temperature: 0.2, maxTokens: 3000, model: apiModel },
        this.provider
      );

      const codeBlockLang = this.getCodeBlockLanguage();
      const code = this.extractCodeBlock(response, codeBlockLang) || 
                   this.extractCodeBlock(response, 'typescript') || response;
      const routePath = basePath.replace(/^\/api/, 'app/api');
      const fileExt = this.getFileExtension('api');

      this.artifacts.push({
        type: 'api_route',
        path: `${routePath}/route${fileExt}`,
        content: code.trim(),
        description: `API routes for ${basePath} (${this.targetLanguage})`,
      });
    }
  }

  private async generateUIComponents(architecture: Architecture): Promise<void> {
    const uiPrompt = this.getUIComponentPrompt();
    const uiModel = this.llmConfig.ui;

    for (const component of architecture.uiComponents) {
      const prompt = `Generate a UI component based on this specification:\n\nTarget Language: ${this.targetLanguage}\n\n${JSON.stringify(component, null, 2)}\n\nAvailable API endpoints:\n${architecture.endpoints.map(e => `${e.method} ${e.path}`).join('\n')}`;

      const response = await callLLM(
        [
          { role: 'system', content: uiPrompt },
          { role: 'user', content: prompt },
        ],
        { temperature: 0.3, maxTokens: 4000, model: uiModel },
        this.provider
      );

      const code = this.extractCodeBlock(response, 'tsx') || 
                   this.extractCodeBlock(response, 'typescript') || 
                   response;

      const fileName = this.toKebabCase(component.name);
      const pathPrefix = component.type === 'page' 
        ? 'app/ghost-control/operations' 
        : 'components/generated';

      this.artifacts.push({
        type: 'ui_component',
        path: `${pathPrefix}/${fileName}.tsx`,
        content: code.trim(),
        description: component.description,
      });
    }
  }

  private async generateDeploymentPackaging(request: WeftRequest): Promise<void> {
    const appName = this.extractAppName(request.prompt);
    const config = {
      targets: this.deploymentTargets,
      appName,
      version: '1.0.0',
      electronConfig: {
        windowWidth: 1280,
        windowHeight: 800,
        autoUpdater: true,
        trayIcon: false,
      },
      tauriConfig: {
        windowWidth: 1280,
        windowHeight: 800,
        systemTray: false,
      },
      pwaConfig: {
        themeColor: '#0C1222',
        backgroundColor: '#0C1222',
        display: 'standalone' as const,
        orientation: 'any' as const,
      },
    };

    for (const target of this.deploymentTargets) {
      if (target === 'web') continue; // Web is handled by default

      if (target === 'desktop-electron') {
        this.artifacts.push({
          type: 'deployment_config',
          path: 'electron/main.js',
          content: generateElectronMain(config),
          description: 'Electron main process entry point',
        });
        this.artifacts.push({
          type: 'deployment_config',
          path: 'electron/preload.js',
          content: generateElectronPreload(),
          description: 'Electron preload script for secure IPC',
        });
        this.artifacts.push({
          type: 'deployment_config',
          path: 'electron-builder.yml',
          content: generateElectronBuilderConfig(config),
          description: 'Electron Builder configuration for Windows/Mac/Linux installers',
        });
        this.artifacts.push({
          type: 'deployment_config',
          path: 'package.electron.json',
          content: generateElectronPackageJson(config),
          description: 'Package.json with Electron build scripts',
        });
      }

      if (target === 'desktop-tauri') {
        this.artifacts.push({
          type: 'deployment_config',
          path: 'src-tauri/tauri.conf.json',
          content: generateTauriConfig(config),
          description: 'Tauri configuration for lightweight native desktop app',
        });
      }

      if (target === 'mobile-pwa') {
        this.artifacts.push({
          type: 'deployment_config',
          path: 'public/manifest.json',
          content: generatePWAManifest(config),
          description: 'PWA manifest for installable mobile web app',
        });
        this.artifacts.push({
          type: 'deployment_config',
          path: 'public/sw.js',
          content: generateServiceWorker(config),
          description: 'Service Worker for offline support and caching',
        });
        this.artifacts.push({
          type: 'deployment_config',
          path: 'public/offline.html',
          content: generateOfflinePage(config),
          description: 'Offline fallback page for PWA',
        });
      }

      if (target === 'mobile-capacitor') {
        this.artifacts.push({
          type: 'deployment_config',
          path: 'capacitor.config.json',
          content: generateCapacitorConfig(config),
          description: 'Capacitor configuration for native iOS/Android apps',
        });
      }
    }
  }

  private extractAppName(prompt: string): string {
    // Try to extract app name from the prompt
    const patterns = [
      /(?:create|build|make|generate)\s+(?:a|an|the)\s+(.+?)(?:\s+(?:application|app|system|module|platform))/i,
      /(?:create|build|make|generate)\s+(.+?)(?:\s+(?:with|that|for|using))/i,
    ];
    for (const pattern of patterns) {
      const match = prompt.match(pattern);
      if (match && match[1].length < 50) {
        return match[1].trim().replace(/\b\w/g, c => c.toUpperCase());
      }
    }
    return 'Generated App';
  }

  private groupEndpointsByPath(endpoints: EndpointDef[]): Record<string, EndpointDef[]> {
    const grouped: Record<string, EndpointDef[]> = {};
    
    for (const endpoint of endpoints) {
      // Get base path (remove dynamic segments)
      const basePath = endpoint.path.replace(/\/\[.*?\]/g, '');
      if (!grouped[basePath]) {
        grouped[basePath] = [];
      }
      grouped[basePath].push(endpoint);
    }
    
    return grouped;
  }

  private extractCodeBlock(content: string, language: string): string | null {
    const regex = new RegExp('```' + language + '?\\n?([\\s\\S]*?)```', 'i');
    const match = content.match(regex);
    return match ? match[1].trim() : null;
  }

  private toKebabCase(str: string): string {
    return str
      .replace(/([a-z])([A-Z])/g, '$1-$2')
      .replace(/[\s_]+/g, '-')
      .toLowerCase();
  }

  /** Get language-specific database/model generation prompt */
  private getDatabasePrompt(): string {
    const dbName = this.targetDatabase;
    const lang = this.targetLanguage;

    if (lang === 'csharp') {
      return `You are a C# Entity Framework expert. Generate production-ready Entity Framework Core model classes.

Rules:
1. Use C# conventions (PascalCase properties, data annotations)
2. Include [Key] attribute for primary keys, use Guid or int for IDs
3. Include CreatedAt and UpdatedAt DateTime properties
4. Use navigation properties for relations
5. Add [Required], [MaxLength], [Column] attributes as appropriate
6. Target database: ${dbName === 'mssql' ? 'SQL Server' : dbName}
7. Include DbContext configuration if relevant

Respond with ONLY the C# code inside a csharp code block.`;
    }

    if (lang === 'python-fastapi') {
      return `You are a Python SQLAlchemy expert. Generate production-ready SQLAlchemy models for FastAPI.

Rules:
1. Use SQLAlchemy 2.0 declarative style with mapped_column
2. Include id (UUID), created_at, updated_at columns
3. Use proper relationship() for relations
4. Add __tablename__ in snake_case
5. Target database: ${dbName}
6. Include Pydantic schemas for API validation

Respond with ONLY the Python code inside a python code block.`;
    }

    if (lang === 'go') {
      return `You are a Go GORM expert. Generate production-ready GORM models.

Rules:
1. Use Go conventions (PascalCase exported fields)
2. Include gorm.Model for ID, CreatedAt, UpdatedAt, DeletedAt
3. Use gorm tags for column definitions
4. Add json tags for API serialization
5. Target database: ${dbName}
6. Include proper foreign key definitions

Respond with ONLY the Go code inside a go code block.`;
    }

    if (lang === 'java-spring') {
      return `You are a Java JPA/Hibernate expert. Generate production-ready JPA entity classes.

Rules:
1. Use @Entity, @Table, @Id, @GeneratedValue annotations
2. Include createdAt, updatedAt with @CreationTimestamp, @UpdateTimestamp
3. Use proper JPA relationships (@OneToMany, @ManyToOne, etc.)
4. Target database: ${dbName === 'mssql' ? 'SQL Server' : dbName}
5. Include Lombok annotations (@Data, @Builder) if appropriate
6. Add proper cascading and fetch types

Respond with ONLY the Java code inside a java code block.`;
    }

    // Default: Prisma (typescript-react)
    return PRISMA_GENERATOR_PROMPT + `\nTarget database provider: ${dbName === 'mssql' ? 'sqlserver' : dbName}`;
  }

  /** Get language-specific API route generation prompt */
  private getApiRoutePrompt(): string {
    const lang = this.targetLanguage;

    if (lang === 'csharp') {
      return `You are an ASP.NET Core API expert. Generate production-ready API controllers.

Rules:
1. Use ASP.NET Core 8 conventions with [ApiController] and [Route]
2. Use dependency injection for DbContext
3. Include proper error handling with try/catch
4. Use ActionResult<T> return types
5. Add input validation with [Required], [FromBody]
6. Include pagination for list endpoints
7. Return proper HTTP status codes with Ok(), NotFound(), BadRequest()

Respond with the complete C# code inside a csharp code block.`;
    }

    if (lang === 'python-fastapi') {
      return `You are a FastAPI expert. Generate production-ready API routes.

Rules:
1. Use FastAPI router with proper decorators (@router.get, @router.post, etc.)
2. Use Pydantic models for request/response validation
3. Include proper error handling with HTTPException
4. Use async/await with SQLAlchemy async session
5. Include pagination with skip/limit parameters
6. Return proper HTTP status codes

Respond with the complete Python code inside a python code block.`;
    }

    if (lang === 'go') {
      return `You are a Go Gin framework expert. Generate production-ready API handlers.

Rules:
1. Use Gin framework conventions (gin.Context, router groups)
2. Use GORM for database operations
3. Include proper error handling with c.JSON responses
4. Add input binding and validation
5. Include pagination for list endpoints
6. Return proper HTTP status codes

Respond with the complete Go code inside a go code block.`;
    }

    if (lang === 'java-spring') {
      return `You are a Spring Boot REST expert. Generate production-ready REST controllers.

Rules:
1. Use Spring Boot 3 conventions with @RestController
2. Use @Autowired or constructor injection for repositories
3. Include proper error handling with ResponseEntity
4. Use Spring Data JPA repositories
5. Include pagination with Pageable
6. Return proper HTTP status codes

Respond with the complete Java code inside a java code block.`;
    }

    // Default: Next.js TypeScript
    return API_ROUTE_GENERATOR_PROMPT;
  }

  /** Get language-specific UI component generation prompt */
  private getUIComponentPrompt(): string {
    const lang = this.targetLanguage;

    if (lang === 'csharp') {
      return `You are a Blazor/Razor expert. Generate production-ready Razor components or React components that could integrate with a C# backend. Use modern Tailwind CSS styling with dark theme. Include proper data fetching and error handling.

Respond with the complete code inside a tsx or razor code block.`;
    }

    // For non-React backends, still generate React components (universal UI)
    return UI_COMPONENT_GENERATOR_PROMPT;
  }

  /** Get the primary code block language for extraction */
  private getCodeBlockLanguage(): string {
    const mapping: Record<string, string> = {
      'typescript-react': 'typescript',
      'csharp': 'csharp',
      'python-fastapi': 'python',
      'go': 'go',
      'java-spring': 'java',
    };
    return mapping[this.targetLanguage] || 'typescript';
  }

  /** Get file extension for generated artifacts */
  private getFileExtension(artifactType: 'model' | 'api' | 'ui'): string {
    const extensions: Record<string, Record<string, string>> = {
      'typescript-react': { model: '.prisma', api: '.ts', ui: '.tsx' },
      'csharp': { model: '.cs', api: '.cs', ui: '.tsx' },
      'python-fastapi': { model: '.py', api: '.py', ui: '.tsx' },
      'go': { model: '.go', api: '.go', ui: '.tsx' },
      'java-spring': { model: '.java', api: '.java', ui: '.tsx' },
    };
    return extensions[this.targetLanguage]?.[artifactType] || '.ts';
  }

  private async saveArtifacts(): Promise<void> {
    const crypto = await import('crypto');

    for (const artifact of this.artifacts) {
      const checksum = crypto
        .createHash('sha256')
        .update(artifact.content)
        .digest('hex');

      await prisma.generatedArtifact.create({
        data: {
          taskId: this.taskId,
          type: artifact.type,
          path: artifact.path,
          content: artifact.content,
          checksum,
          version: 1,
        },
      });
    }
  }

  private async updateStages(): Promise<void> {
    await prisma.generationTask.update({
      where: { id: this.taskId },
      data: {
        stages: this.stages.map(s => ({
          name: s.name,
          status: s.status,
          startedAt: s.startedAt?.toISOString(),
          completedAt: s.completedAt?.toISOString(),
          error: s.error,
        })),
      },
    });
  }

  private async updateTaskStatus(status: string, error?: string): Promise<void> {
    await prisma.generationTask.update({
      where: { id: this.taskId },
      data: {
        status,
        error,
        completedAt: ['completed', 'failed', 'awaiting_approval'].includes(status) ? new Date() : undefined,
      },
    });
  }
}

// Export a convenience function
export async function runWeft(request: WeftRequest): Promise<WeftResult> {
  const engine = new WeftEngine();
  return engine.execute(request);
}
