import Ajv, { JSONSchemaType } from 'ajv';
import addFormats from 'ajv-formats';
import { JSONSchema7 } from 'json-schema';
import { ValidationResult } from './types.js';

/**
 * Schema validation utility using Ajv
 */
export class SchemaValidator {
  private ajv: Ajv;

  constructor() {
    this.ajv = new Ajv({
      allErrors: true,
      removeAdditional: false,
      useDefaults: true,
      coerceTypes: true,
      strict: false
    });

    // Add format validation (date, email, uri, etc.)
    addFormats(this.ajv);

    // Add custom formats if needed
    this.addCustomFormats();
  }

  /**
   * Validate data against a JSON schema
   */
  validate(schema: JSONSchema7, data: any): ValidationResult {
    try {
      const validate = this.ajv.compile(schema);
      const valid = validate(data);

      if (valid) {
        return { valid: true, errors: [] };
      }

      const errors = (validate.errors || []).map(error => ({
        path: error.instancePath || 'root',
        message: error.message || 'Validation failed',
        value: error.data
      }));

      return { valid: false, errors };
    } catch (error) {
      return {
        valid: false,
        errors: [{
          path: 'schema',
          message: `Schema compilation error: ${error instanceof Error ? error.message : 'Unknown error'}`,
          value: schema
        }]
      };
    }
  }

  /**
   * Validate and return cleaned data
   */
  validateAndClean<T>(schema: JSONSchema7, data: any): { valid: boolean; data?: T; errors?: ValidationResult['errors'] } {
    const result = this.validate(schema, data);
    
    if (result.valid) {
      return { valid: true, data: data as T };
    }

    return { valid: false, errors: result.errors };
  }

  /**
   * Create a validation function for a specific schema
   */
  createValidator(schema: JSONSchema7) {
    const compiledValidator = this.ajv.compile(schema);
    
    return (data: any): ValidationResult => {
      const valid = compiledValidator(data);
      
      if (valid) {
        return { valid: true, errors: [] };
      }

      const errors = (compiledValidator.errors || []).map(error => ({
        path: error.instancePath || 'root',
        message: error.message || 'Validation failed',
        value: error.data
      }));

      return { valid: false, errors };
    };
  }

  /**
   * Add custom format validators
   */
  private addCustomFormats(): void {
    // Unix timestamp format
    this.ajv.addFormat('unix-timestamp', {
      type: 'number',
      validate: (data: number) => {
        return Number.isInteger(data) && data > 0 && data < 4000000000; // reasonable timestamp range
      }
    });

    // Story points format (Fibonacci-like sequence)
    this.ajv.addFormat('story-points', {
      type: 'number',
      validate: (data: number) => {
        const validPoints = [0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89];
        return validPoints.includes(data);
      }
    });

    // Priority levels
    this.ajv.addFormat('priority', {
      type: 'string',
      validate: (data: string) => {
        return ['low', 'medium', 'high', 'critical'].includes(data);
      }
    });

    // Status values for various entities
    this.ajv.addFormat('story-status', {
      type: 'string',
      validate: (data: string) => {
        return ['todo', 'in_progress', 'in_review', 'done', 'backlog'].includes(data);
      }
    });

    this.ajv.addFormat('sprint-status', {
      type: 'string',
      validate: (data: string) => {
        return ['planning', 'active', 'completed', 'cancelled'].includes(data);
      }
    });

    this.ajv.addFormat('approval-status', {
      type: 'string',
      validate: (data: string) => {
        return ['pending', 'approved', 'rejected', 'expired'].includes(data);
      }
    });

    // Documentation status
    this.ajv.addFormat('doc-status', {
      type: 'string',
      validate: (data: string) => {
        return ['pending', 'in_progress', 'approved', 'outdated'].includes(data);
      }
    });
  }
}

// Global validator instance
let globalValidator: SchemaValidator | null = null;

export function getValidator(): SchemaValidator {
  if (!globalValidator) {
    globalValidator = new SchemaValidator();
  }
  return globalValidator;
}

/**
 * Common schema definitions for reuse
 */
export const CommonSchemas = {
  id: {
    type: 'string' as const,
    minLength: 1,
    maxLength: 255,
    pattern: '^[a-zA-Z0-9_-]+$'
  },

  projectId: {
    type: 'string' as const,
    minLength: 1,
    maxLength: 255,
    description: 'Project identifier'
  },

  timestamp: {
    type: 'integer' as const,
    format: 'unix-timestamp' as const,
    description: 'Unix timestamp'
  },

  priority: {
    type: 'string' as const,
    format: 'priority' as const,
    default: 'medium',
    description: 'Priority level'
  },

  tags: {
    type: 'array' as const,
    items: {
      type: 'string' as const,
      minLength: 1,
      maxLength: 50
    },
    default: [],
    description: 'Array of tags'
  },

  storyPoints: {
    type: 'integer' as const,
    format: 'story-points' as const,
    default: 0,
    description: 'Story points using Fibonacci sequence'
  },

  status: {
    story: {
      type: 'string' as const,
      format: 'story-status' as const,
      default: 'todo',
      description: 'Story status'
    },
    sprint: {
      type: 'string' as const,
      format: 'sprint-status' as const,
      default: 'planning',
      description: 'Sprint status'
    },
    approval: {
      type: 'string' as const,
      format: 'approval-status' as const,
      default: 'pending',
      description: 'Approval status'
    }
  },

  documentation: {
    url: {
      type: 'string' as const,
      format: 'uri' as const,
      description: 'Document URL'
    },
    status: {
      type: 'string' as const,
      format: 'doc-status' as const,
      default: 'pending',
      description: 'Documentation status'
    }
  }
};

/**
 * Utility function to create error responses
 */
export function createValidationError(errors: ValidationResult['errors']): {
  code: string;
  message: string;
  details: Record<string, any>;
  suggestions: string[];
  recoverable: boolean;
  category: 'validation';
} {
  const errorMessages = errors.map(e => `${e.path}: ${e.message}`);
  
  return {
    code: 'VALIDATION_ERROR',
    message: `Input validation failed: ${errorMessages.join('; ')}`,
    details: {
      validationErrors: errors,
      errorCount: errors.length
    },
    suggestions: [
      'Check the input parameters against the tool schema',
      'Ensure all required fields are provided',
      'Verify data types match the expected format',
      'Review field constraints (min/max length, allowed values)'
    ],
    recoverable: true,
    category: 'validation' as const
  };
}