"""
NovaLang Annotation Processor
Handles custom database annotations and generates SQL schema
"""

import re
import json
from typing import Dict, List, Any, Optional
from dataclasses import dataclass

@dataclass
class ColumnInfo:
    name: str
    type: str
    length: Optional[int] = None
    nullable: bool = True
    unique: bool = False
    default_value: Optional[str] = None
    precision: Optional[int] = None
    scale: Optional[int] = None

@dataclass
class TableInfo:
    name: str
    schema: Optional[str] = None
    columns: List[ColumnInfo] = None
    primary_keys: List[str] = None
    foreign_keys: List[Dict] = None
    indexes: List[Dict] = None
    constraints: List[str] = None

class NovaLangAnnotationProcessor:
    def __init__(self):
        self.tables = {}
        self.migrations = []
        self.type_mappings = {
            'string': 'VARCHAR',
            'number': 'BIGINT',
            'boolean': 'BOOLEAN',
            'date': 'TIMESTAMP',
            'decimal': 'DECIMAL'
        }
    
    def process_file(self, file_content: str) -> Dict[str, Any]:
        """Process a .nova file and extract annotation information"""
        lines = file_content.split('\n')
        current_class = None
        annotations = []
        
        for i, line in enumerate(lines):
            line = line.strip()
            
            # Detect annotations
            if line.startswith('@'):
                annotation = self.parse_annotation(line)
                annotations.append(annotation)
            
            # Detect class definition
            elif line.startswith('class ') and annotations:
                class_name = line.split()[1].split('{')[0]
                current_class = self.process_class_annotations(class_name, annotations)
                annotations = []
            
            # Detect property with annotations
            elif ':' in line and current_class and annotations:
                property_info = self.process_property_annotations(line, annotations)
                if property_info:
                    current_class['properties'].append(property_info)
                annotations = []
        
        return self.tables
    
    def parse_annotation(self, annotation_line: str) -> Dict[str, Any]:
        """Parse annotation syntax like @Table(name: "customers", schema: "ecommerce")"""
        # Remove @ and extract annotation name and parameters
        annotation_line = annotation_line[1:]  # Remove @
        
        if '(' in annotation_line:
            name = annotation_line.split('(')[0]
            params_str = annotation_line.split('(')[1].rstrip(')')
            params = self.parse_annotation_params(params_str)
        else:
            name = annotation_line
            params = {}
        
        return {'name': name, 'params': params}
    
    def parse_annotation_params(self, params_str: str) -> Dict[str, Any]:
        """Parse annotation parameters like name: "customers", length: 50"""
        params = {}
        
        # Simple parser for key: value pairs
        pairs = re.findall(r'(\w+):\s*([^,)]+)', params_str)
        
        for key, value in pairs:
            # Remove quotes and convert types
            value = value.strip().strip('"\'')
            
            # Try to convert to appropriate type
            if value.isdigit():
                value = int(value)
            elif value.lower() in ['true', 'false']:
                value = value.lower() == 'true'
            elif value.startswith('[') and value.endswith(']'):
                # Handle arrays
                value = [item.strip().strip('"\'') for item in value[1:-1].split(',')]
            
            params[key] = value
        
        return params
    
    def process_class_annotations(self, class_name: str, annotations: List[Dict]) -> Dict[str, Any]:
        """Process class-level annotations like @Table, @DatabaseEntity"""
        table_info = {
            'class_name': class_name,
            'table_name': class_name.lower(),
            'schema': None,
            'properties': [],
            'indexes': [],
            'constraints': [],
            'foreign_keys': []
        }
        
        for annotation in annotations:
            if annotation['name'] == 'Table':
                params = annotation['params']
                table_info['table_name'] = params.get('name', class_name.lower())
                table_info['schema'] = params.get('schema')
            
            elif annotation['name'] == 'DatabaseEntity':
                table_info['is_entity'] = True
            
            elif annotation['name'] == 'Cacheable':
                table_info['cache_duration'] = annotation['params'].get('duration', '1h')
            
            elif annotation['name'] == 'CompositeIndex':
                columns = annotation['params'].get('columns', [])
                table_info['indexes'].append({
                    'type': 'composite',
                    'columns': columns
                })
        
        self.tables[class_name] = table_info
        return table_info
    
    def process_property_annotations(self, property_line: str, annotations: List[Dict]) -> Optional[Dict]:
        """Process property annotations like @Column, @PrimaryKey, etc."""
        # Extract property name and type
        parts = property_line.split(':')
        if len(parts) != 2:
            return None
        
        property_name = parts[0].strip()
        property_type = parts[1].strip()
        
        column_info = {
            'property_name': property_name,
            'column_name': property_name,
            'type': self.type_mappings.get(property_type, 'VARCHAR'),
            'nullable': True,
            'unique': False,
            'primary_key': False,
            'auto_increment': False,
            'foreign_key': None,
            'validation': [],
            'index': False
        }
        
        for annotation in annotations:
            if annotation['name'] == 'Column':
                params = annotation['params']
                column_info['column_name'] = params.get('name', property_name)
                column_info['nullable'] = params.get('nullable', True)
                column_info['unique'] = params.get('unique', False)
                column_info['default_value'] = params.get('defaultValue')
                
                if 'length' in params:
                    column_info['type'] = f"VARCHAR({params['length']})"
                if 'type' in params:
                    column_info['type'] = params['type']
                if 'precision' in params and 'scale' in params:
                    column_info['type'] = f"DECIMAL({params['precision']},{params['scale']})"
            
            elif annotation['name'] == 'PrimaryKey':
                column_info['primary_key'] = True
                column_info['nullable'] = False
            
            elif annotation['name'] == 'AutoIncrement':
                column_info['auto_increment'] = True
            
            elif annotation['name'] == 'ForeignKey':
                params = annotation['params']
                column_info['foreign_key'] = {
                    'table': params.get('table'),
                    'column': params.get('column', 'id'),
                    'on_delete': params.get('onDelete', 'RESTRICT')
                }
            
            elif annotation['name'] == 'Index':
                column_info['index'] = True
            
            elif annotation['name'] == 'Validation':
                column_info['validation'].append(annotation['params'])
            
            elif annotation['name'] == 'Timestamp':
                params = annotation['params']
                if params.get('onCreate'):
                    column_info['default_value'] = 'CURRENT_TIMESTAMP'
                if params.get('onUpdate'):
                    column_info['on_update'] = 'CURRENT_TIMESTAMP'
            
            elif annotation['name'] == 'Computed':
                column_info['computed'] = annotation['params'].get('formula')
        
        return column_info
    
    def generate_sql_schema(self) -> str:
        """Generate SQL CREATE TABLE statements from processed annotations"""
        sql_statements = []
        
        # Generate schemas
        schemas = set()
        for table_info in self.tables.values():
            if table_info.get('schema'):
                schemas.add(table_info['schema'])
        
        for schema in schemas:
            sql_statements.append(f"CREATE SCHEMA IF NOT EXISTS {schema};")
        
        # Generate tables
        for table_info in self.tables.values():
            sql = self.generate_table_sql(table_info)
            sql_statements.append(sql)
        
        return '\n\n'.join(sql_statements)
    
    def generate_table_sql(self, table_info: Dict) -> str:
        """Generate CREATE TABLE SQL for a single table"""
        table_name = table_info['table_name']
        if table_info.get('schema'):
            table_name = f"{table_info['schema']}.{table_name}"
        
        columns = []
        primary_keys = []
        foreign_keys = []
        indexes = []
        
        # Process columns
        for prop in table_info['properties']:
            column_def = self.generate_column_definition(prop)
            columns.append(column_def)
            
            if prop['primary_key']:
                primary_keys.append(prop['column_name'])
            
            if prop['foreign_key']:
                fk_def = self.generate_foreign_key_definition(prop)
                foreign_keys.append(fk_def)
            
            if prop['index'] and not prop['primary_key']:
                index_name = f"idx_{prop['column_name']}"
                indexes.append(f"INDEX {index_name} ({prop['column_name']})")
        
        # Combine all definitions
        all_definitions = columns
        
        if primary_keys:
            all_definitions.append(f"PRIMARY KEY ({', '.join(primary_keys)})")
        
        all_definitions.extend(foreign_keys)
        all_definitions.extend(indexes)
        
        # Generate table constraints from validation
        constraints = self.generate_table_constraints(table_info)
        all_definitions.extend(constraints)
        
        sql = f"CREATE TABLE {table_name} (\n"
        sql += ",\n".join(f"    {definition}" for definition in all_definitions)
        sql += "\n);"
        
        return sql
    
    def generate_column_definition(self, prop: Dict) -> str:
        """Generate column definition SQL"""
        column_name = prop['column_name']
        column_type = prop['type']
        
        definition = f"{column_name} {column_type}"
        
        if prop['auto_increment']:
            definition += " AUTO_INCREMENT"
        
        if not prop['nullable']:
            definition += " NOT NULL"
        
        if prop['unique']:
            definition += " UNIQUE"
        
        if prop.get('default_value'):
            default_val = prop['default_value']
            if default_val == 'CURRENT_TIMESTAMP':
                definition += f" DEFAULT {default_val}"
            else:
                definition += f" DEFAULT '{default_val}'"
        
        if prop.get('on_update'):
            definition += f" ON UPDATE {prop['on_update']}"
        
        if prop.get('computed'):
            definition += f" AS ({prop['computed']}) STORED"
        
        return definition
    
    def generate_foreign_key_definition(self, prop: Dict) -> str:
        """Generate foreign key constraint SQL"""
        fk = prop['foreign_key']
        constraint_name = f"fk_{prop['column_name']}"
        
        return (f"CONSTRAINT {constraint_name} FOREIGN KEY ({prop['column_name']}) "
                f"REFERENCES {fk['table']}({fk['column']}) "
                f"ON DELETE {fk['on_delete']}")
    
    def generate_table_constraints(self, table_info: Dict) -> List[str]:
        """Generate CHECK constraints from validation annotations"""
        constraints = []
        
        for prop in table_info['properties']:
            for validation in prop['validation']:
                if 'min' in validation:
                    constraints.append(f"CHECK ({prop['column_name']} >= {validation['min']})")
                if 'max' in validation:
                    constraints.append(f"CHECK ({prop['column_name']} <= {validation['max']})")
                if 'values' in validation:
                    values = "', '".join(validation['values'])
                    constraints.append(f"CHECK ({prop['column_name']} IN ('{values}'))")
        
        return constraints
    
    def generate_repository_class(self, class_name: str) -> str:
        """Generate repository class code for a domain entity"""
        table_info = self.tables.get(class_name)
        if not table_info:
            return ""
        
        repo_class = f"""
@Repository
class {class_name}Repository {{
    
    function findAll() {{
        print "📋 {class_name}Repository.findAll()"
        let sql = "SELECT * FROM {table_info['table_name']}"
        return Database.query(sql)
    }}
    
    function findById(id) {{
        print "🔍 {class_name}Repository.findById(" + id + ")"
        let sql = "SELECT * FROM {table_info['table_name']} WHERE id = ?"
        return Database.queryOne(sql, [id])
    }}
    
    function save(entity) {{
        print "💾 {class_name}Repository.save()"
        
        // Validate entity
        let errors = entity.validate()
        if (errors) {{
            throw new Error("Validation failed: " + errors.join(", "))
        }}
        
        if (entity.id === 0) {{
            return this.insert(entity)
        }} else {{
            return this.update(entity)
        }}
    }}
    
    function insert(entity) {{
        let sql = "INSERT INTO {table_info['table_name']} (...) VALUES (...)"
        let result = Database.execute(sql, [...])
        entity.id = result.insertId
        return entity
    }}
    
    function update(entity) {{
        let sql = "UPDATE {table_info['table_name']} SET ... WHERE id = ?"
        Database.execute(sql, [..., entity.id])
        return entity
    }}
    
    function deleteById(id) {{
        print "🗑️ {class_name}Repository.deleteById(" + id + ")"
        let sql = "DELETE FROM {table_info['table_name']} WHERE id = ?"
        return Database.execute(sql, [id])
    }}
}}
"""
        return repo_class

# Integration with NovaLang interpreter
def process_nova_file(file_path: str) -> Dict[str, Any]:
    """Process a .nova file and return annotation information"""
    processor = NovaLangAnnotationProcessor()
    
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    
    return processor.process_file(content)

def generate_database_schema(nova_files: List[str]) -> str:
    """Generate complete database schema from multiple .nova files"""
    processor = NovaLangAnnotationProcessor()
    
    for file_path in nova_files:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        processor.process_file(content)
    
    return processor.generate_sql_schema()

# Example usage
if __name__ == "__main__":
    # Process Customer.nova file
    result = process_nova_file("Customer.nova")
    print("Processed annotations:", json.dumps(result, indent=2))
    
    # Generate SQL schema
    schema_sql = generate_database_schema(["Customer.nova", "DatabaseModels.nova"])
    print("\nGenerated SQL Schema:")
    print(schema_sql)
