import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  CreateDateColumn,
  UpdateDateColumn,
  BeforeInsert,
  BeforeUpdate
} from 'typeorm';
import { IsJSON } from 'class-validator';

/**
 * JSON Schema for template structure validation
 */
export interface ProjectTemplateSchema {
  // Basic project properties
  name?: string;
  description?: string;
  status?: 'active' | 'on_hold' | 'completed' | 'archived';
  priority?: 'low' | 'medium' | 'high' | 'critical';
  
  // BMad workflow configuration
  bmadWorkflowType?: 'greenfield' | 'brownfield' | 'maintenance';
  
  // Date configuration
  startDateOffset?: number; // Days from creation date
  durationDays?: number; // Expected duration in days
  
  // Hierarchical structure
  structure?: {
    children?: ProjectTemplateNode[];
  };
  
  // Task templates
  tasks?: TaskTemplate[];
  
  // Metadata and custom fields
  metadata?: Record<string, any>;
  customFields?: CustomFieldDefinition[];
  
  // Dependencies between child projects
  dependencies?: ProjectDependency[];
}

export interface ProjectTemplateNode {
  name: string;
  description?: string;
  priority?: 'low' | 'medium' | 'high' | 'critical';
  durationDays?: number;
  children?: ProjectTemplateNode[];
  tasks?: TaskTemplate[];
  metadata?: Record<string, any>;
}

export interface TaskTemplate {
  title: string;
  description?: string;
  priority?: 'low' | 'medium' | 'high' | 'urgent';
  estimatedHours?: number;
  assigneeRole?: string;
  tags?: string[];
  dependencies?: string[]; // References to other task IDs
  subtasks?: TaskTemplate[];
}

export interface CustomFieldDefinition {
  name: string;
  type: 'text' | 'number' | 'date' | 'boolean' | 'select' | 'multiselect' | 'json';
  label: string;
  required?: boolean;
  defaultValue?: any;
  options?: Array<{ value: string; label: string }>; // For select/multiselect
  validation?: {
    min?: number;
    max?: number;
    pattern?: string;
    customValidator?: string; // JavaScript expression
  };
}

export interface ProjectDependency {
  from: string; // Path or ID reference
  to: string; // Path or ID reference
  type: 'blocks' | 'requires' | 'relates_to';
  lag?: number; // Days of lag/lead time
}

/**
 * Project Template Entity
 * Stores reusable project templates with JSON schema validation
 */
@Entity('project_templates')
export class ProjectTemplateEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ 
    type: 'varchar', 
    length: 255,
    unique: true
  })
  name: string;

  @Column({ 
    type: 'text',
    nullable: true
  })
  description: string;

  @Column({
    type: 'varchar',
    length: 50,
    default: 'project'
  })
  category: 'project' | 'workflow' | 'sprint' | 'feature' | 'custom';

  @Column({
    type: 'jsonb',
    comment: 'JSON schema defining the template structure'
  })
  @IsJSON()
  schema: ProjectTemplateSchema;

  @Column({
    type: 'jsonb',
    nullable: true,
    comment: 'JSON Schema validation rules for template data'
  })
  validationSchema: any;

  @Column({
    type: 'simple-array',
    nullable: true,
    comment: 'Tags for categorizing and searching templates'
  })
  tags: string[];

  @Column({
    type: 'varchar',
    length: 20,
    default: 'draft'
  })
  status: 'draft' | 'published' | 'deprecated';

  @Column({
    type: 'varchar',
    length: 20,
    nullable: true
  })
  version: string;

  @Column({
    type: 'uuid',
    nullable: true,
    name: 'parent_template_id',
    comment: 'Reference to parent template for inheritance'
  })
  parentTemplateId: string | null;

  @Column({
    type: 'boolean',
    default: false,
    comment: 'Whether this template can be used as a base for other templates'
  })
  isBaseTemplate: boolean;

  @Column({
    type: 'boolean',
    default: true,
    comment: 'Whether regular users can use this template'
  })
  isPublic: boolean;

  @Column({
    type: 'simple-json',
    nullable: true,
    comment: 'Usage statistics and metadata'
  })
  usageStats: {
    useCount: number;
    lastUsedAt?: Date;
    avgCompletionDays?: number;
    successRate?: number;
  } | null;

  @Column({
    type: 'simple-json',
    nullable: true,
    comment: 'Preview data for template selection UI'
  })
  preview: {
    thumbnailUrl?: string;
    previewData?: any;
    estimatedDuration?: number;
    complexity?: 'low' | 'medium' | 'high';
  } | null;

  @Column({
    type: 'uuid',
    nullable: true,
    name: 'created_by'
  })
  createdBy: string | null;

  @Column({
    type: 'uuid',
    nullable: true,
    name: 'updated_by'
  })
  updatedBy: string | null;

  @CreateDateColumn({ name: 'date_created' })
  dateCreated: Date;

  @UpdateDateColumn({ name: 'date_updated' })
  dateUpdated: Date;

  /**
   * Validate template before insert
   */
  @BeforeInsert()
  validateBeforeInsert() {
    if (!this.schema) {
      throw new Error('Template schema is required');
    }

    // Set default version if not provided
    if (!this.version) {
      this.version = '1.0.0';
    }

    // Initialize usage stats
    if (!this.usageStats) {
      this.usageStats = {
        useCount: 0
      };
    }
  }

  /**
   * Update version on schema changes
   */
  @BeforeUpdate()
  updateVersion() {
    // This is a simplified version update
    // In production, you might want more sophisticated versioning
    if (this.version) {
      const parts = this.version.split('.');
      const patch = parseInt(parts[2] || '0', 10) + 1;
      this.version = `${parts[0]}.${parts[1]}.${patch}`;
    }
  }

  /**
   * Check if template has children structure
   */
  hasHierarchy(): boolean {
    return !!(this.schema?.structure?.children && this.schema.structure.children.length > 0);
  }

  /**
   * Get total estimated duration
   */
  getEstimatedDuration(): number {
    let duration = this.schema?.durationDays || 0;
    
    // Recursively calculate duration from children
    const calculateChildDuration = (nodes: ProjectTemplateNode[] = []): number => {
      return nodes.reduce((total, node) => {
        const nodeDuration = node.durationDays || 0;
        const childrenDuration = node.children ? calculateChildDuration(node.children) : 0;
        return total + Math.max(nodeDuration, childrenDuration);
      }, 0);
    };

    if (this.schema?.structure?.children) {
      duration = Math.max(duration, calculateChildDuration(this.schema.structure.children));
    }

    return duration;
  }

  /**
   * Count total tasks in template
   */
  getTotalTaskCount(): number {
    let count = this.schema?.tasks?.length || 0;

    // Count tasks in structure
    const countTasksInNode = (node: ProjectTemplateNode): number => {
      let nodeCount = node.tasks?.length || 0;
      if (node.children) {
        nodeCount += node.children.reduce((sum, child) => sum + countTasksInNode(child), 0);
      }
      // Count subtasks
      if (node.tasks) {
        nodeCount += node.tasks.reduce((sum, task) => {
          const countSubtasks = (t: TaskTemplate): number => {
            let subCount = t.subtasks?.length || 0;
            if (t.subtasks) {
              subCount += t.subtasks.reduce((s, st) => s + countSubtasks(st), 0);
            }
            return subCount;
          };
          return sum + countSubtasks(task);
        }, 0);
      }
      return nodeCount;
    };

    if (this.schema?.structure?.children) {
      count += this.schema.structure.children.reduce(
        (sum, child) => sum + countTasksInNode(child), 
        0
      );
    }

    return count;
  }

  /**
   * Merge with parent template if inheritance is used
   */
  async mergeWithParent(parentTemplate: ProjectTemplateEntity): Promise<ProjectTemplateSchema> {
    if (!this.parentTemplateId || !parentTemplate) {
      return this.schema;
    }

    // Deep merge parent schema with current schema
    // Current schema properties override parent properties
    const mergeSchemas = (parent: any, current: any): any => {
      const result = { ...parent };
      
      for (const key in current) {
        if (current[key] === null || current[key] === undefined) {
          continue;
        }
        
        if (typeof current[key] === 'object' && !Array.isArray(current[key])) {
          result[key] = mergeSchemas(parent[key] || {}, current[key]);
        } else {
          result[key] = current[key];
        }
      }
      
      return result;
    };

    return mergeSchemas(parentTemplate.schema, this.schema);
  }
}