"""
Main web routes for dashboard and device management
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from app.models import Device, LogEntry, MessageTemplate, AnsibleExecution, PlaybookExecution, WMTUpdateRequest, InventoryGroup, device_inventory_association
from config.database_config import get_db
from app.services.log_service import LogCompressionService
from datetime import datetime, timedelta
from pathlib import Path
from sqlalchemy import text, func
import logging
import yaml

# Create blueprint
main_bp = Blueprint('main', __name__)

# Initialize services
log_service = LogCompressionService()

@main_bp.route('/')
def index():
    """Redirect root to devices page"""
    return redirect(url_for('main.devices'))

@main_bp.route('/dashboard')
def dashboard():
    """Redirect /dashboard to devices"""
    return redirect(url_for('main.devices'))

@main_bp.route('/devices')
def devices():
    """Device management page"""
    try:
        with get_db().get_session() as session:
            devices = session.query(Device).order_by(Device.last_seen.desc()).all()

            # Log count per device
            device_log_counts = {}
            for device in devices:
                log_count = session.query(LogEntry).filter_by(device_id=device.id).count()
                device_log_counts[device.id] = log_count

            pending_count = session.query(WMTUpdateRequest).filter_by(status='pending').count()

            # Pending approval requests mapped by mac_address for per-device badge
            pending_requests = session.query(WMTUpdateRequest).filter_by(status='pending').all()
            pending_by_mac = {}
            for req in pending_requests:
                pending_by_mac[req.mac_address] = pending_by_mac.get(req.mac_address, 0) + 1

            return render_template('device_management.html',
                                 devices=devices,
                                 device_log_counts=device_log_counts,
                                 pending_count=pending_count,
                                 pending_by_mac=pending_by_mac)
    except Exception as e:
        logging.error(f"Error loading devices: {e}")
        flash(f'Error loading devices: {e}', 'error')
        return render_template('device_management.html', devices=[], device_log_counts={},
                               pending_count=0, pending_by_mac={})

@main_bp.route('/device/<int:device_id>')
def device_detail(device_id):
    """Device detail page with logs and stats"""
    try:
        with get_db().get_session() as session:
            device = session.query(Device).get(device_id)
            
            if not device:
                flash('Device not found', 'error')
                return redirect(url_for('main.devices'))
            
            # Get device logs (last 100)
            logs = session.query(LogEntry).filter_by(device_id=device_id).order_by(
                LogEntry.timestamp.desc()
            ).limit(100).all()
            
            # Get log statistics
            log_stats = {
                'total': len(logs),
                'by_severity': {},
                'last_24h': 0
            }
            
            last_24h = datetime.utcnow() - timedelta(hours=24)
            for log in logs:
                # Count by severity
                severity = log.severity
                log_stats['by_severity'][severity] = log_stats['by_severity'].get(severity, 0) + 1
                
                # Count last 24h
                if log.timestamp >= last_24h:
                    log_stats['last_24h'] += 1
            
            return render_template('device_detail.html',
                                 device=device,
                                 logs=logs,
                                 log_stats=log_stats)
                                 
    except Exception as e:
        logging.error(f"Error loading device detail: {e}")
        flash(f'Error loading device detail: {e}', 'error')
        return redirect(url_for('main.devices'))

@main_bp.route('/logs')
def logs():
    """Log viewer with filtering"""
    try:
        # Get filter parameters
        device_id = request.args.get('device_id', type=int)
        severity = request.args.get('severity')
        search = request.args.get('search', '')
        page = request.args.get('page', 1, type=int)
        per_page = min(request.args.get('per_page', 50, type=int), 200)
        
        with get_db().get_session() as session:
            # Build query
            query = session.query(LogEntry).join(Device)
            
            # Apply filters
            if device_id:
                query = query.filter(LogEntry.device_id == device_id)
            
            if severity:
                query = query.filter(LogEntry.severity == severity)
            
            if search:
                # Search in resolved message or full message
                query = query.filter(
                    # This is simplified - in production you'd want full-text search
                    LogEntry.full_message.contains(search)
                )
            
            # Order by timestamp desc
            query = query.order_by(LogEntry.timestamp.desc())
            
            # Get total count for pagination
            total = query.count()
            
            # Apply pagination
            offset = (page - 1) * per_page
            logs = query.offset(offset).limit(per_page).all()
            
            # Calculate pagination info
            total_pages = (total + per_page - 1) // per_page
            has_prev = page > 1
            has_next = page < total_pages
            
            # Get device list for filter dropdown
            devices = session.query(Device).order_by(Device.hostname).all()
            
            pagination = {
                'page': page,
                'per_page': per_page,
                'total': total,
                'total_pages': total_pages,
                'has_prev': has_prev,
                'has_next': has_next,
                'prev_num': page - 1 if has_prev else None,
                'next_num': page + 1 if has_next else None
            }
            
            return render_template('logs.html',
                                 logs=logs,
                                 pagination=pagination,
                                 devices=devices,
                                 current_device_id=device_id,
                                 current_severity=severity,
                                 current_search=search)
                                 
    except Exception as e:
        logging.error(f"Error loading logs: {e}")
        flash(f'Error loading logs: {e}', 'error')
        return render_template('logs.html', 
                             logs=[], 
                             pagination={}, 
                             devices=[],
                             current_device_id=None,
                             current_severity=None,
                             current_search='')

@main_bp.route('/templates')
def templates():
    """Message templates management"""
    try:
        with get_db().get_session() as session:
            templates = session.query(MessageTemplate).order_by(
                MessageTemplate.usage_count.desc()
            ).all()
            
            # Get template statistics
            template_stats = {
                'total': len(templates),
                'by_category': {},
                'total_usage': sum(t.usage_count for t in templates)
            }
            
            for template in templates:
                category = template.category
                template_stats['by_category'][category] = template_stats['by_category'].get(category, 0) + 1
            
            return render_template('templates.html',
                                 templates=templates,
                                 template_stats=template_stats)
                                 
    except Exception as e:
        logging.error(f"Error loading templates: {e}")
        flash(f'Error loading templates: {e}', 'error')
        return render_template('templates.html', templates=[], template_stats={})

@main_bp.route('/stats')
def stats():
    """System statistics and analytics"""
    try:
        with get_db().get_session() as session:
            # Get compression stats
            compression_stats = log_service.get_compression_stats()
            
            # Get device statistics
            device_stats = {
                'total': session.query(Device).count(),
                'active': session.query(Device).filter_by(status='active').count(),
                'inactive': session.query(Device).filter_by(status='inactive').count(),
                'maintenance': session.query(Device).filter_by(status='maintenance').count()
            }
            
            # Get log statistics by time periods
            now = datetime.utcnow()
            periods = {
                'last_hour': now - timedelta(hours=1),
                'last_24h': now - timedelta(hours=24),
                'last_week': now - timedelta(days=7),
                'last_month': now - timedelta(days=30)
            }
            
            log_stats = {}
            for period_name, period_start in periods.items():
                count = session.query(LogEntry).filter(
                    LogEntry.timestamp >= period_start
                ).count()
                log_stats[period_name] = count
            
            # Get execution statistics
            exec_stats = {
                'total': session.query(PlaybookExecution).count(),
                'successful': session.query(PlaybookExecution).filter_by(status='completed').count(),
                'failed': session.query(PlaybookExecution).filter_by(status='failed').count(),
                'running': session.query(PlaybookExecution).filter(
                    PlaybookExecution.status.in_(['queued', 'running'])
                ).count()
            }
            
            return render_template('stats.html',
                                 compression_stats=compression_stats,
                                 device_stats=device_stats,
                                 log_stats=log_stats,
                                 exec_stats=exec_stats)
                                 
    except Exception as e:
        logging.error(f"Error loading stats: {e}")
        flash(f'Error loading stats: {e}', 'error')
        return render_template('stats.html',
                             compression_stats={},
                             device_stats={},
                             log_stats={},
                             exec_stats={})

# API Endpoints for Device Management

@main_bp.route('/api/devices/add', methods=['POST'])
def api_add_device():
    """API endpoint to add a device manually"""
    try:
        data = request.get_json()
        
        # Validate required fields
        required_fields = ['hostname', 'device_ip', 'nume_masa']
        for field in required_fields:
            if not data.get(field):
                return jsonify({
                    'success': False,
                    'message': f'Missing required field: {field}'
                }), 400
        
        # Check if device already exists (MAC-first, then hostname/IP)
        with get_db().get_session() as session:
            mac_input = data.get('mac_address', '').strip().lower() or None

            existing_device = None
            if mac_input:
                existing_device = session.query(Device).filter_by(mac_address=mac_input).first()

            if not existing_device:
                existing_device = session.query(Device).filter(
                    (Device.hostname == data['hostname']) |
                    (Device.device_ip == data['device_ip'])
                ).first()

            if existing_device:
                # If found by MAC or hostname/IP, update it rather than reject
                if mac_input and not existing_device.mac_address:
                    existing_device.mac_address = mac_input
                existing_device.hostname   = data['hostname']
                existing_device.device_ip  = data['device_ip']
                existing_device.nume_masa  = data['nume_masa']
                if data.get('device_type'):
                    existing_device.device_type = data['device_type']
                if data.get('os_version'):
                    existing_device.os_version = data['os_version']
                if data.get('location'):
                    existing_device.location = data['location']
                if data.get('status'):
                    existing_device.status = data['status']
                existing_device.config_updated_at = datetime.utcnow()
                existing_device.info_reviewed_at  = datetime.utcnow()
                session.flush()
                return jsonify({
                    'success': True,
                    'message': 'Device already existed – record updated',
                    'device_id': existing_device.id
                }), 200
            
            # Create new device
            new_device = Device(
                hostname=data['hostname'],
                device_ip=data['device_ip'],
                nume_masa=data['nume_masa'],
                mac_address=data.get('mac_address', '').strip().lower() or None,
                device_type=data.get('device_type', 'unknown'),
                os_version=data.get('os_version'),
                status=data.get('status', 'active'),
                location=data.get('location'),
                description=data.get('description'),
                config_updated_at=datetime.utcnow(),
                info_reviewed_at=datetime.utcnow(),
                last_seen=datetime.utcnow()
            )
            
            session.add(new_device)
            session.commit()
            
            # Refresh ansible inventory
            try:
                from app.services.ansible_service import AnsibleService
                ansible_service = AnsibleService()
                ansible_service.generate_dynamic_inventory()
            except Exception as e:
                logging.warning(f"Failed to update ansible inventory: {e}")
            
            return jsonify({
                'success': True,
                'message': 'Device added successfully',
                'device_id': new_device.id
            }), 201
            
    except Exception as e:
        logging.error(f"Error adding device: {e}")
        return jsonify({
            'success': False,
            'message': f'Error adding device: {str(e)}'
        }), 500

@main_bp.route('/api/devices/<int:device_id>/execute', methods=['POST'])  
def api_execute_device_command(device_id):
    """API endpoint to execute commands on devices"""
    try:
        data = request.get_json()
        command = data.get('command')
        
        if not command:
            return jsonify({
                'success': False,
                'message': 'Command is required'
            }), 400
        
        with get_db().get_session() as session:
            device = session.query(Device).get(device_id)
            
            if not device:
                return jsonify({
                    'success': False,
                    'message': 'Device not found'
                }), 404
            
            # Mock implementation - in production this would execute actual commands
            if command == 'ping':
                # Simulate ping command
                import subprocess
                try:
                    result = subprocess.run(['ping', '-c', '1', device.device_ip], 
                                          capture_output=True, text=True, timeout=5)
                    success = result.returncode == 0
                    output = result.stdout if success else result.stderr
                    
                    return jsonify({
                        'success': success,
                        'command': command,
                        'output': output,
                        'device': device.hostname
                    })
                except subprocess.TimeoutExpired:
                    return jsonify({
                        'success': False,
                        'message': 'Command timed out',
                        'command': command
                    })
            else:
                # For other commands, return placeholder
                return jsonify({
                    'success': True,
                    'message': f'Command "{command}" would be executed on {device.hostname}',
                    'command': command,
                    'note': 'This is a placeholder implementation'
                })
                
    except Exception as e:
        logging.error(f"Error executing device command: {e}")
        return jsonify({
            'success': False,
            'message': f'Error executing command: {str(e)}'
        }), 500


# ---------------------------------------------------------------------------
# Device edit / delete (unified – includes WMT fields)
# ---------------------------------------------------------------------------

@main_bp.route('/devices/<int:device_id>/edit', methods=['GET', 'POST'])
def device_edit(device_id):
    """Edit a device record (monitoring + WMT fields)."""
    try:
        with get_db().get_session() as session:
            device = session.query(Device).filter_by(id=device_id).first()
            if not device:
                flash('Device not found.', 'error')
                return redirect(url_for('main.devices'))

            if request.method == 'POST':
                device.hostname = request.form.get('hostname', '').strip() or device.hostname
                device.device_ip = request.form.get('device_ip', '').strip() or device.device_ip
                device.nume_masa = request.form.get('nume_masa', '').strip() or device.nume_masa
                mac_raw = request.form.get('mac_address', '').strip().lower() or None
                # Only assign MAC if no other device owns it
                if mac_raw and mac_raw != device.mac_address:
                    conflict = session.query(Device).filter(
                        Device.mac_address == mac_raw, Device.id != device_id
                    ).first()
                    if conflict:
                        flash(f'MAC {mac_raw} is already assigned to {conflict.hostname}.', 'error')
                        return render_template('device_edit.html', device=device)
                device.mac_address = mac_raw
                device.status = request.form.get('status', 'active')
                device.location = request.form.get('location', '').strip() or None
                device.device_type = request.form.get('device_type', '').strip() or 'unknown'
                device.description = request.form.get('description', '').strip() or None
                device.os_version = request.form.get('os_version', '').strip() or None
                custom_url = request.form.get('custom_chrome_url', '').strip()
                device.custom_chrome_url = custom_url if custom_url else None
                device.config_updated_at = datetime.utcnow()
                device.info_reviewed_at = datetime.utcnow()
                flash('Device updated.', 'success')
                return redirect(url_for('main.devices'))

            return render_template(
                'device_edit.html',
                device=device,
                breadcrumbs=[
                    {'url': url_for('main.dashboard'), 'title': 'Dashboard'},
                    {'url': url_for('main.devices'), 'title': 'Devices'},
                    {'url': '#', 'title': f'Edit {device.hostname}'},
                ],
            )
    except Exception as e:
        logging.error(f'Device edit error: {e}')
        flash(f'Error: {e}', 'error')
        return redirect(url_for('main.devices'))


@main_bp.route('/devices/<int:device_id>/delete', methods=['POST'])
def device_delete(device_id):
    """Delete a device and all its logs."""
    try:
        with get_db().get_session() as session:
            device = session.query(Device).filter_by(id=device_id).first()
            if device:
                name = device.hostname
                session.delete(device)
                flash(f'Device {name} deleted.', 'success')
            else:
                flash('Device not found.', 'error')
    except Exception as e:
        logging.error(f'Device delete error: {e}')
        flash(f'Error deleting device: {e}', 'error')
    return redirect(url_for('main.devices'))


# ── Admin page ────────────────────────────────────────────────────────

INVENTORY_FILE = Path('ansible/inventory/dynamic_inventory.yaml')

@main_bp.route('/admin')
def admin():
    """Admin / maintenance page with DB and inventory stats."""
    stats = {}
    try:
        with get_db().get_session() as session:
            stats['devices'] = session.query(func.count(Device.id)).scalar()
            stats['logs'] = session.query(func.count(LogEntry.id)).scalar()
            stats['templates'] = session.query(func.count(MessageTemplate.id)).scalar()
            stats['inventory_groups'] = session.query(func.count(InventoryGroup.id)).scalar()
            stats['wmt_requests'] = session.query(func.count(WMTUpdateRequest.id)).scalar()
    except Exception as e:
        logging.error(f'Admin stats error: {e}')
    # Inventory host count
    try:
        if INVENTORY_FILE.exists():
            data = yaml.safe_load(INVENTORY_FILE.read_text()) or {}
            all_children = data.get('all', {}).get('children', {})
            inv_hosts = set()
            for g in all_children.values():
                inv_hosts.update((g or {}).get('hosts', {}).keys())
            stats['inventory_hosts'] = len(inv_hosts)
            stats['inventory_groups_yaml'] = len(all_children)
        else:
            stats['inventory_hosts'] = 0
            stats['inventory_groups_yaml'] = 0
    except Exception as e:
        logging.error(f'Admin inventory stats error: {e}')
        stats['inventory_hosts'] = '?'
        stats['inventory_groups_yaml'] = '?'
    # Pass registered devices for per-device delete UI
    devices = []
    try:
        with get_db().get_session() as session:
            devices = [
                {'id': d.id, 'hostname': d.hostname, 'nume_masa': d.nume_masa,
                 'mac_address': d.mac_address, 'device_ip': d.device_ip}
                for d in session.query(Device).order_by(Device.nume_masa).all()
            ]
    except Exception as e:
        logging.error(f'Admin device list error: {e}')
    return render_template('admin.html', stats=stats, devices=devices)


@main_bp.route('/admin/clear/logs', methods=['POST'])
def admin_clear_logs():
    """Delete all log entries from the database."""
    return _clear_all_log_entries()


@main_bp.route('/admin/clear/device-logs', methods=['POST'])
def admin_clear_device_logs():
    """Delete all log entries from the database (devices stay intact).

    Functionally identical to admin_clear_logs – both endpoints are kept because
    the admin UI exposes two separate buttons, but they share one implementation.
    """
    return _clear_all_log_entries()


def _clear_all_log_entries():
    """Shared implementation: delete every LogEntry row."""
    try:
        with get_db().get_session() as session:
            count = session.query(LogEntry).delete()
            session.commit()
        return jsonify({'success': True, 'deleted': count})
    except Exception as e:
        logging.error(f'Admin clear logs error: {e}')
        return jsonify({'success': False, 'error': str(e)}), 500


@main_bp.route('/admin/delete/device/<int:device_id>', methods=['POST'])
def admin_delete_device(device_id):
    """Delete a single registered device and its log entries."""
    try:
        with get_db().get_session() as session:
            device = session.query(Device).filter_by(id=device_id).first()
            if not device:
                return jsonify({'success': False, 'error': 'Device not found'}), 404
            name = device.nume_masa or device.hostname
            session.execute(text('DELETE FROM device_inventory_groups WHERE device_id = :id'), {'id': device_id})
            session.query(LogEntry).filter_by(device_id=device_id).delete()
            session.query(WMTUpdateRequest).filter_by(device_id=device_id).delete()
            session.delete(device)
            session.commit()
        logging.info(f'Admin deleted device {device_id} ({name})')
        return jsonify({'success': True, 'name': name})
    except Exception as e:
        logging.error(f'Admin delete device {device_id} error: {e}')
        return jsonify({'success': False, 'error': str(e)}), 500


@main_bp.route('/admin/clear/devices', methods=['POST'])
def admin_clear_devices():
    """Delete ALL devices (and their log entries) from the database."""
    try:
        with get_db().get_session() as session:
            session.execute(text('DELETE FROM device_inventory_groups'))
            logs = session.query(LogEntry).delete()
            devices = session.query(Device).delete()
            session.commit()
        return jsonify({'success': True, 'deleted_devices': devices, 'deleted_logs': logs})
    except Exception as e:
        logging.error(f'Admin clear devices error: {e}')
        return jsonify({'success': False, 'error': str(e)}), 500


@main_bp.route('/admin/clear/inventory', methods=['POST'])
def admin_clear_inventory():
    """Reset the Ansible inventory file to a completely empty state."""
    try:
        empty = {'_meta': {'hostvars': {}}, 'all': {'hosts': {}, 'children': {}}}
        INVENTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
        INVENTORY_FILE.write_text(yaml.dump(empty, default_flow_style=False))
        # Also clear inventory_groups table
        with get_db().get_session() as session:
            session.execute(text('DELETE FROM device_inventory_groups'))
            groups = session.query(InventoryGroup).delete()
            session.commit()
        return jsonify({'success': True, 'groups_deleted': groups})
    except Exception as e:
        logging.error(f'Admin clear inventory error: {e}')
        return jsonify({'success': False, 'error': str(e)}), 500


@main_bp.route('/admin/clear/wmt', methods=['POST'])
def admin_clear_wmt():
    """Delete all WMT update requests."""
    try:
        with get_db().get_session() as session:
            count = session.query(WMTUpdateRequest).delete()
            session.commit()
        return jsonify({'success': True, 'deleted': count})
    except Exception as e:
        logging.error(f'Admin clear WMT requests error: {e}')
        return jsonify({'success': False, 'error': str(e)}), 500