/**
 * Workflow Parser
 * Parses YAML workflow files into typed Workflow objects
 */

import * as yaml from 'js-yaml';
import * as fs from 'fs';
import * as path from 'path';
import { ZodError } from 'zod';
import { Workflow } from '../types/workflow';
import { WorkflowSchema } from './workflow-schema';
import { ExtendedWorkflowSchema, ExtendedWorkflow } from './extended-workflow-schema';
import { ReferenceResolver } from './reference-resolver';
import { SchemaErrorFormatter } from './schema-error-formatter';

/**
 * Workflow parse result with optional extended schema
 */
export interface WorkflowParseResult {
  workflow: Workflow;
  extendedWorkflow?: ExtendedWorkflow;
}

export class WorkflowParser {
  private referenceResolver: ReferenceResolver;

  constructor() {
    this.referenceResolver = new ReferenceResolver();
  }

  /**
   * Parse a workflow file from disk
   */
  async parseFile(filePath: string): Promise<WorkflowParseResult> {
    const absolutePath = path.resolve(filePath);

    if (!fs.existsSync(absolutePath)) {
      throw new Error(`Workflow file not found: ${absolutePath}`);
    }

    const content = fs.readFileSync(absolutePath, 'utf-8');
    return this.parseString(content, absolutePath);
  }

  /**
   * Parse a workflow from a YAML string
   * Detects and parses both event-driven and extended workflow schemas
   */
  async parseString(yamlContent: string, source: string = 'string'): Promise<WorkflowParseResult> {
    try {
      const parsed = yaml.load(yamlContent);

      if (!parsed) {
        throw new Error('Empty workflow file');
      }

      // Resolve $ref fields
      const resolved = await this.referenceResolver.resolve(parsed, {
        basePath: source === 'string' ? process.cwd() : source,
      });

      // Detect which schema to use based on presence of extended fields
      const hasExtendedFields =
        (resolved as any).technology !== undefined ||
        (resolved as any).models !== undefined ||
        (resolved as any).events !== undefined ||
        (resolved as any).actors !== undefined ||
        (resolved as any).components !== undefined ||
        (resolved as any).workflows !== undefined;

      let extendedWorkflow: ExtendedWorkflow | undefined;

      if (hasExtendedFields) {
        // Parse with extended schema
        try {
          extendedWorkflow = ExtendedWorkflowSchema.parse(resolved) as ExtendedWorkflow;
        } catch (error) {
          if (error instanceof ZodError) {
            const formattedError = SchemaErrorFormatter.format(error, source);
            throw new Error(formattedError);
          }
          throw error;
        }
      }

      // Also parse with legacy schema for backwards compatibility
      const workflow = WorkflowSchema.parse(resolved) as Workflow;

      return {
        workflow,
        extendedWorkflow,
      };
    } catch (error) {
      if (error instanceof yaml.YAMLException) {
        const formattedError = SchemaErrorFormatter.formatSimple(
          error.message,
          'YAML Syntax Error',
          'Check your YAML syntax - common issues include incorrect indentation, missing colons, or unquoted strings with special characters'
        );
        throw new Error(formattedError);
      }
      if (error instanceof ZodError) {
        const formattedError = SchemaErrorFormatter.format(error, source);
        throw new Error(formattedError);
      }
      throw error;
    }
  }

  /**
   * Parse multiple workflow files from a directory
   */
  async parseDirectory(dirPath: string): Promise<WorkflowParseResult[]> {
    const absolutePath = path.resolve(dirPath);

    if (!fs.existsSync(absolutePath)) {
      throw new Error(`Directory not found: ${absolutePath}`);
    }

    const files = fs.readdirSync(absolutePath)
      .filter(file => file.endsWith('.workflow.yaml') || file.endsWith('.workflow.yml'));

    return Promise.all(
      files.map(file => {
        const filePath = path.join(absolutePath, file);
        return this.parseFile(filePath);
      })
    );
  }

  /**
   * Get all files referenced by a workflow file (for dependency tracking)
   */
  getReferencedFiles(filePath: string): string[] {
    const absolutePath = path.resolve(filePath);

    if (!fs.existsSync(absolutePath)) {
      throw new Error(`Workflow file not found: ${absolutePath}`);
    }

    const content = fs.readFileSync(absolutePath, 'utf-8');
    const parsed = yaml.load(content);

    if (!parsed) {
      return [];
    }

    return this.referenceResolver.getAllReferencedFiles(parsed, absolutePath);
  }
}
