from typing import Dict, List, Optional
from datetime import datetime
import difflib
import uuid
import logging

logger = logging.getLogger(__name__)

class BackupManager:
    """Configuration backup and version control"""
    
    def __init__(self, db):
        self.db = db
    
    async def create_backup(self, device_id: str, config: str, 
                           backup_type: str = 'manual', 
                           created_by: Optional[str] = None,
                           org_id: str = None) -> Dict:
        """Create configuration backup"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            raise Exception("Device not found")
        
        backup = {
            'id': str(uuid.uuid4()),
            'device_id': device_id,
            'device_ip': device['ip'],
            'device_hostname': device.get('hostname', device['ip']),
            'config': config,
            'config_size': len(config),
            'backup_type': backup_type,
            'timestamp': datetime.utcnow(),
            'created_by': created_by,
            'version': await self._get_next_version(device_id),
        }
        if org_id:
            backup['organization_id'] = org_id
        
        await self.db.backups.insert_one(backup)
        
        # Update device with latest backup info
        await self.db.devices.update_one(
            {'id': device_id},
            {'$set': {
                'last_backup_at': backup['timestamp'],
                'last_backup_id': backup['id']
            }}
        )
        
        logger.info(f"Backup created for device {device_id}, version {backup['version']}")
        
        return {
            'id': backup['id'],
            'version': backup['version'],
            'timestamp': backup['timestamp'],
            'size': backup['config_size']
        }
    
    async def get_backups(self, device_id: str, limit: int = 50, org_id: str = None) -> List[Dict]:
        """Get backup history for device"""
        query = {'device_id': device_id}
        if org_id:
            query['organization_id'] = org_id
        backups = await self.db.backups.find(
            query
        ).sort('timestamp', -1).limit(limit).to_list(limit)
        
        return [
            {
                'id': b['id'],
                'version': b['version'],
                'timestamp': b['timestamp'],
                'backup_type': b['backup_type'],
                'size': b['config_size'],
                'created_by': b.get('created_by', 'system')
            }
            for b in backups
        ]
    
    async def get_backup(self, backup_id: str, org_id: str = None) -> Optional[Dict]:
        """Get specific backup"""
        query = {'id': backup_id}
        if org_id:
            query['organization_id'] = org_id
        backup = await self.db.backups.find_one(query)
        return backup
    
    async def compare_backups(self, backup_id1: str, backup_id2: str, org_id: str = None) -> Dict:
        """Compare two backups scoped to organization"""
        backup1 = await self.get_backup(backup_id1, org_id=org_id)
        backup2 = await self.get_backup(backup_id2, org_id=org_id)
        
        if not backup1 or not backup2:
            raise Exception("Backup not found")
        
        config1_lines = backup1['config'].splitlines()
        config2_lines = backup2['config'].splitlines()
        
        # Generate diff
        diff = list(difflib.unified_diff(
            config1_lines,
            config2_lines,
            fromfile=f"Version {backup1['version']}",
            tofile=f"Version {backup2['version']}",
            lineterm=''
        ))
        
        # Count changes
        additions = len([line for line in diff if line.startswith('+')])
        deletions = len([line for line in diff if line.startswith('-')])
        
        return {
            'backup1': {
                'id': backup1['id'],
                'version': backup1['version'],
                'timestamp': backup1['timestamp']
            },
            'backup2': {
                'id': backup2['id'],
                'version': backup2['version'],
                'timestamp': backup2['timestamp']
            },
            'diff': '\n'.join(diff),
            'changes': {
                'additions': additions,
                'deletions': deletions,
                'total': additions + deletions
            }
        }
    
    async def restore_backup(self, backup_id: str, restore_method: str = 'preview', org_id: str = None) -> Dict:
        """Restore configuration from backup scoped to organization"""
        backup = await self.get_backup(backup_id, org_id=org_id)
        if not backup:
            raise Exception("Backup not found")
        
        if restore_method == 'preview':
            return {
                'backup_id': backup_id,
                'device_id': backup['device_id'],
                'version': backup['version'],
                'config_preview': backup['config'][:500] + '...' if len(backup['config']) > 500 else backup['config'],
                'full_size': backup['config_size'],
                'timestamp': backup['timestamp']
            }
        
        # For actual restore, return config to be applied
        return {
            'backup_id': backup_id,
            'device_id': backup['device_id'],
            'config': backup['config'],
            'version': backup['version']
        }
    
    async def delete_old_backups(self, device_id: str, keep_last: int = 10, org_id: str = None):
        """Delete old backups, keeping only the last N, scoped to organization"""
        query = {'device_id': device_id}
        if org_id:
            query['organization_id'] = org_id
        
        backups = await self.db.backups.find(query).sort('timestamp', -1).to_list(1000)
        
        if len(backups) <= keep_last:
            return 0
        
        backups_to_delete = backups[keep_last:]
        delete_ids = [b['id'] for b in backups_to_delete]
        
        delete_query = {'id': {'$in': delete_ids}}
        if org_id:
            delete_query['organization_id'] = org_id
        result = await self.db.backups.delete_many(delete_query)
        
        logger.info(f"Deleted {result.deleted_count} old backups for device {device_id}")
        return result.deleted_count
    
    async def _get_next_version(self, device_id: str) -> int:
        """Get next version number for device"""
        last_backup = await self.db.backups.find_one(
            {'device_id': device_id},
            sort=[('version', -1)]
        )
        
        if last_backup and 'version' in last_backup:
            return last_backup['version'] + 1
        return 1
    
    async def schedule_auto_backup(self, device_id: str, schedule: str = 'daily', org_id: str = None):
        """Schedule automatic backups scoped to organization"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            raise Exception("Device not found")
        
        await self.db.devices.update_one(
            query,
            {'$set': {
                'auto_backup_enabled': True,
                'auto_backup_schedule': schedule
            }}
        )
        
        return {'success': True, 'schedule': schedule}
