#!/usr/bin/env python3
"""
Mac Studio Control - Backend SSH
Flask API para executar comandos SSH remotamente no Mac Studio

Autor: Claude Code
Data: 2025-11-13
"""

from flask import Flask, request, jsonify
from flask_cors import CORS
import subprocess
import json
import os
import sqlite3
from datetime import datetime, timedelta
from contextlib import contextmanager

app = Flask(__name__)
CORS(app)  # Permitir requisições do frontend

# Configurações
MAC_STUDIO_IP = '192.168.1.30'
MAC_STUDIO_USER = os.environ.get('MAC_STUDIO_USER', 'usuario')  # Configure via env var
SSH_KEY_PATH = os.path.expanduser('~/.ssh/id_ed25519')
DB_PATH = os.path.expanduser('~/mac-studio-logs.db')

# ============================================
# LOG DATABASE CLASS - Persistência SQLite
# ============================================
class LogDatabase:
    """Gerencia logs em SQLite com busca full-text"""

    def __init__(self, db_path=DB_PATH):
        self.db_path = db_path
        self.init_database()

    @contextmanager
    def get_connection(self):
        """Context manager para conexão SQLite"""
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
            conn.commit()
        except Exception as e:
            conn.rollback()
            raise e
        finally:
            conn.close()

    def init_database(self):
        """Cria tabela de logs se não existir"""
        with self.get_connection() as conn:
            conn.execute('''
                CREATE TABLE IF NOT EXISTS logs (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    timestamp TEXT NOT NULL,
                    level TEXT CHECK(level IN ('INFO', 'WARN', 'ERROR', 'DEBUG')) NOT NULL,
                    message TEXT NOT NULL,
                    category TEXT,
                    source TEXT DEFAULT 'backend',
                    context TEXT
                )
            ''')

            # Índices para performance
            conn.execute('CREATE INDEX IF NOT EXISTS idx_timestamp ON logs(timestamp DESC)')
            conn.execute('CREATE INDEX IF NOT EXISTS idx_level ON logs(level)')
            conn.execute('CREATE INDEX IF NOT EXISTS idx_source ON logs(source)')

            # FTS (Full-Text Search) virtual table
            conn.execute('''
                CREATE VIRTUAL TABLE IF NOT EXISTS logs_fts USING fts5(
                    message,
                    category,
                    content=logs,
                    content_rowid=id
                )
            ''')

            # Triggers para manter FTS sincronizado
            conn.execute('''
                CREATE TRIGGER IF NOT EXISTS logs_ai AFTER INSERT ON logs BEGIN
                    INSERT INTO logs_fts(rowid, message, category)
                    VALUES (new.id, new.message, new.category);
                END
            ''')

            conn.execute('''
                CREATE TRIGGER IF NOT EXISTS logs_ad AFTER DELETE ON logs BEGIN
                    DELETE FROM logs_fts WHERE rowid = old.id;
                END
            ''')

            conn.execute('''
                CREATE TRIGGER IF NOT EXISTS logs_au AFTER UPDATE ON logs BEGIN
                    UPDATE logs_fts SET message = new.message, category = new.category
                    WHERE rowid = new.id;
                END
            ''')

            # Tabela de alertas
            conn.execute('''
                CREATE TABLE IF NOT EXISTS alerts (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    timestamp TEXT NOT NULL,
                    level TEXT CHECK(level IN ('INFO', 'WARN', 'ERROR')) NOT NULL,
                    metric TEXT,
                    value REAL,
                    threshold REAL,
                    message TEXT NOT NULL
                )
            ''')

            # Índice para busca rápida por timestamp
            conn.execute('CREATE INDEX IF NOT EXISTS idx_alert_timestamp ON alerts(timestamp DESC)')
            conn.execute('CREATE INDEX IF NOT EXISTS idx_alert_metric ON alerts(metric)')

    def add_log(self, level, message, category=None, source='backend', context=None):
        """Adiciona um log ao banco"""
        with self.get_connection() as conn:
            cursor = conn.execute('''
                INSERT INTO logs (timestamp, level, message, category, source, context)
                VALUES (?, ?, ?, ?, ?, ?)
            ''', (
                datetime.now().isoformat(),
                level,
                message,
                category,
                source,
                json.dumps(context) if context else None
            ))
            return cursor.lastrowid

    def get_logs(self, level=None, source=None, search=None, limit=50, offset=0):
        """Busca logs com filtros e paginação"""
        with self.get_connection() as conn:
            query = 'SELECT * FROM logs WHERE 1=1'
            params = []

            # Filtro por nível
            if level:
                query += ' AND level = ?'
                params.append(level)

            # Filtro por fonte
            if source:
                query += ' AND source = ?'
                params.append(source)

            # Busca full-text
            if search:
                query += ''' AND id IN (
                    SELECT rowid FROM logs_fts WHERE logs_fts MATCH ?
                )'''
                params.append(search)

            # Ordenação e paginação
            query += ' ORDER BY timestamp DESC LIMIT ? OFFSET ?'
            params.extend([limit, offset])

            cursor = conn.execute(query, params)
            logs = []

            for row in cursor:
                log = dict(row)
                # Parse context JSON
                if log['context']:
                    try:
                        log['context'] = json.loads(log['context'])
                    except:
                        pass
                logs.append(log)

            return logs

    def count_logs(self, level=None, source=None, search=None):
        """Conta total de logs com filtros"""
        with self.get_connection() as conn:
            query = 'SELECT COUNT(*) as total FROM logs WHERE 1=1'
            params = []

            if level:
                query += ' AND level = ?'
                params.append(level)

            if source:
                query += ' AND source = ?'
                params.append(source)

            if search:
                query += ''' AND id IN (
                    SELECT rowid FROM logs_fts WHERE logs_fts MATCH ?
                )'''
                params.append(search)

            cursor = conn.execute(query, params)
            return cursor.fetchone()['total']

    def delete_old_logs(self, days=30):
        """Deleta logs mais antigos que N dias"""
        with self.get_connection() as conn:
            cutoff = datetime.now().timestamp() - (days * 86400)
            cursor = conn.execute('''
                DELETE FROM logs WHERE timestamp < datetime(?, 'unixepoch')
            ''', (cutoff,))
            return cursor.rowcount

    # ============================================
    # MÉTODOS DE ALERTAS
    # ============================================

    def add_alert(self, level, metric, value, threshold, message):
        """Adiciona um alerta ao banco"""
        with self.get_connection() as conn:
            cursor = conn.execute('''
                INSERT INTO alerts (timestamp, level, metric, value, threshold, message)
                VALUES (?, ?, ?, ?, ?, ?)
            ''', (
                datetime.now().isoformat(),
                level,
                metric,
                value,
                threshold,
                message
            ))
            return cursor.lastrowid

    def get_alerts(self, hours=24, limit=100):
        """Busca alertas das últimas N horas"""
        with self.get_connection() as conn:
            cutoff = datetime.now() - timedelta(hours=hours)
            cursor = conn.execute('''
                SELECT * FROM alerts
                WHERE timestamp >= ?
                ORDER BY timestamp DESC
                LIMIT ?
            ''', (cutoff.isoformat(), limit))
            return [dict(row) for row in cursor.fetchall()]

    def clear_alerts(self, hours=None):
        """Limpa alertas (todos ou das últimas N horas)"""
        with self.get_connection() as conn:
            if hours:
                cutoff = datetime.now() - timedelta(hours=hours)
                cursor = conn.execute('''
                    DELETE FROM alerts WHERE timestamp >= ?
                ''', (cutoff.isoformat(),))
            else:
                cursor = conn.execute('DELETE FROM alerts')
            return cursor.rowcount

# Instanciar database globalmente
log_db = LogDatabase()

# Lista de comandos permitidos (segurança)
ALLOWED_COMMANDS = [
    'uptime',
    'top -l 1',
    'df -h',
    'sw_vers',
    'system_profiler SPHardwareDataType',
    'networksetup -listallhardwareports',
    'ps aux',
    'ifconfig',
    'who',
    'last',
    'diskutil list',
    'vm_stat',
    'sysctl -n hw.ncpu',
    'sysctl -n hw.memsize',
]

def log_message(level, message, context={}):
    """Log estruturado"""
    log_entry = {
        'timestamp': datetime.now().isoformat(),
        'level': level,
        'message': message,
        'context': context
    }
    print(json.dumps(log_entry))

    # Salvar no banco SQLite
    try:
        log_db.add_log(level, message, source='backend', context=context)
    except Exception as e:
        print(f"Erro ao salvar log no banco: {e}")

    return log_entry

def is_command_allowed(command):
    """Verifica se comando é permitido"""
    # Verifica se comando está na lista branca
    for allowed in ALLOWED_COMMANDS:
        if command.strip() == allowed:
            return True

    # Permite variações com grep, head, tail
    base_cmd = command.split('|')[0].strip()
    if base_cmd in ALLOWED_COMMANDS:
        return True

    return False

def execute_ssh_command(command):
    """Executa comando SSH no Mac Studio"""
    try:
        log_message('INFO', f'Executando comando SSH: {command}')

        # Monta comando SSH
        ssh_cmd = [
            'ssh',
            '-i', SSH_KEY_PATH,
            '-o', 'StrictHostKeyChecking=no',
            '-o', 'ConnectTimeout=10',
            f'{MAC_STUDIO_USER}@{MAC_STUDIO_IP}',
            command
        ]

        # Executa
        result = subprocess.run(
            ssh_cmd,
            capture_output=True,
            text=True,
            timeout=30
        )

        if result.returncode == 0:
            log_message('INFO', 'Comando executado com sucesso')
            return {
                'success': True,
                'stdout': result.stdout,
                'stderr': result.stderr,
                'exit_code': result.returncode
            }
        else:
            log_message('WARN', f'Comando retornou exit code {result.returncode}')
            return {
                'success': False,
                'stdout': result.stdout,
                'stderr': result.stderr,
                'exit_code': result.returncode
            }

    except subprocess.TimeoutExpired:
        log_message('ERROR', 'Timeout ao executar comando SSH')
        return {
            'success': False,
            'error': 'Timeout (30s)',
            'exit_code': -1
        }
    except Exception as e:
        log_message('ERROR', f'Erro ao executar SSH: {str(e)}')
        return {
            'success': False,
            'error': str(e),
            'exit_code': -1
        }

@app.route('/api/health', methods=['GET'])
def health_check():
    """Health check endpoint"""
    return jsonify({
        'status': 'ok',
        'service': 'mac-studio-backend',
        'timestamp': datetime.now().isoformat(),
        'mac_studio_ip': MAC_STUDIO_IP
    })

@app.route('/api/ssh/execute', methods=['POST'])
def ssh_execute():
    """Executa comando SSH no Mac Studio"""
    try:
        data = request.get_json()
        command = data.get('command', '').strip()

        if not command:
            return jsonify({
                'success': False,
                'error': 'Comando vazio'
            }), 400

        # Verifica segurança
        if not is_command_allowed(command):
            log_message('WARN', f'Comando não permitido: {command}')
            return jsonify({
                'success': False,
                'error': 'Comando não permitido',
                'allowed_commands': ALLOWED_COMMANDS
            }), 403

        # Executa
        result = execute_ssh_command(command)

        return jsonify(result)

    except Exception as e:
        log_message('ERROR', f'Erro no endpoint /api/ssh/execute: {str(e)}')
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

@app.route('/api/ssh/allowed-commands', methods=['GET'])
def get_allowed_commands():
    """Retorna lista de comandos permitidos"""
    return jsonify({
        'commands': ALLOWED_COMMANDS
    })

@app.route('/api/resources', methods=['GET'])
def get_resources():
    """Obtém recursos do Mac Studio"""
    try:
        log_message('INFO', 'Obtendo recursos do Mac Studio')

        # CPU
        cpu_result = execute_ssh_command("top -l 1 | grep 'CPU usage' | awk '{print $3}' | sed 's/%//'")

        # RAM
        ram_result = execute_ssh_command("top -l 1 | grep PhysMem | awk '{print $2}' | sed 's/M//'")

        # Disk
        disk_result = execute_ssh_command("df -h / | tail -1 | awk '{print $5}' | sed 's/%//'")

        # Uptime
        uptime_result = execute_ssh_command("uptime")

        resources = {
            'cpu_percent': cpu_result.get('stdout', '0').strip() or '0',
            'ram_used_mb': ram_result.get('stdout', '0').strip() or '0',
            'disk_percent': disk_result.get('stdout', '0').strip() or '0',
            'uptime': uptime_result.get('stdout', '').strip(),
            'timestamp': datetime.now().isoformat()
        }

        return jsonify(resources)

    except Exception as e:
        log_message('ERROR', f'Erro ao obter recursos: {str(e)}')
        return jsonify({
            'error': str(e)
        }), 500

@app.route('/api/test-connection', methods=['GET'])
def test_connection():
    """Testa conexão SSH com Mac Studio"""
    try:
        log_message('INFO', 'Testando conexão SSH')

        result = execute_ssh_command('echo "Connection OK"')

        return jsonify({
            'success': result.get('success', False),
            'message': result.get('stdout', '').strip(),
            'timestamp': datetime.now().isoformat()
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao testar conexão: {str(e)}')
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500

# ============================================
# LOG ENDPOINTS - Persistência SQLite
# ============================================

@app.route('/api/logs', methods=['GET'])
def get_logs():
    """Busca logs com filtros e paginação"""
    try:
        # Parâmetros de query
        level = request.args.get('level')  # INFO, WARN, ERROR, DEBUG
        source = request.args.get('source')  # backend, dashboard, etc
        search = request.args.get('search')  # Busca full-text
        limit = int(request.args.get('limit', 50))
        offset = int(request.args.get('offset', 0))

        # Buscar logs
        logs = log_db.get_logs(
            level=level,
            source=source,
            search=search,
            limit=limit,
            offset=offset
        )

        # Contar total
        total = log_db.count_logs(
            level=level,
            source=source,
            search=search
        )

        return jsonify({
            'logs': logs,
            'total': total,
            'limit': limit,
            'offset': offset,
            'has_more': (offset + limit) < total
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao buscar logs: {str(e)}')
        return jsonify({
            'error': str(e)
        }), 500

@app.route('/api/logs', methods=['POST'])
def create_log():
    """Cria um novo log"""
    try:
        data = request.get_json()

        if not data or 'level' not in data or 'message' not in data:
            return jsonify({
                'error': 'level e message são obrigatórios'
            }), 400

        # Validar level
        valid_levels = ['INFO', 'WARN', 'ERROR', 'DEBUG']
        level = data['level'].upper()

        if level not in valid_levels:
            return jsonify({
                'error': f'level deve ser um de: {", ".join(valid_levels)}'
            }), 400

        # Adicionar log
        log_id = log_db.add_log(
            level=level,
            message=data['message'],
            category=data.get('category'),
            source=data.get('source', 'dashboard'),
            context=data.get('context')
        )

        return jsonify({
            'success': True,
            'log_id': log_id
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao criar log: {str(e)}')
        return jsonify({
            'error': str(e)
        }), 500

@app.route('/api/logs/<int:log_id>', methods=['GET'])
def get_log(log_id):
    """Busca um log específico por ID"""
    try:
        logs = log_db.get_logs(limit=1, offset=0)

        # Buscar por ID manualmente (poderia adicionar método específico)
        with log_db.get_connection() as conn:
            cursor = conn.execute('SELECT * FROM logs WHERE id = ?', (log_id,))
            row = cursor.fetchone()

            if row:
                log = dict(row)
                if log['context']:
                    try:
                        log['context'] = json.loads(log['context'])
                    except:
                        pass
                return jsonify(log)
            else:
                return jsonify({'error': 'Log não encontrado'}), 404

    except Exception as e:
        log_message('ERROR', f'Erro ao buscar log: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/api/logs/stats', methods=['GET'])
def get_log_stats():
    """Estatísticas dos logs"""
    try:
        with log_db.get_connection() as conn:
            # Total por nível
            cursor = conn.execute('''
                SELECT level, COUNT(*) as count
                FROM logs
                GROUP BY level
            ''')
            by_level = {row['level']: row['count'] for row in cursor}

            # Total por fonte
            cursor = conn.execute('''
                SELECT source, COUNT(*) as count
                FROM logs
                GROUP BY source
            ''')
            by_source = {row['source']: row['count'] for row in cursor}

            # Total geral
            cursor = conn.execute('SELECT COUNT(*) as total FROM logs')
            total = cursor.fetchone()['total']

            # Logs mais recentes (últimas 24h)
            cursor = conn.execute('''
                SELECT COUNT(*) as count
                FROM logs
                WHERE timestamp > datetime('now', '-24 hours')
            ''')
            last_24h = cursor.fetchone()['count']

            return jsonify({
                'total': total,
                'by_level': by_level,
                'by_source': by_source,
                'last_24h': last_24h
            })

    except Exception as e:
        log_message('ERROR', f'Erro ao buscar estatísticas: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/api/logs/cleanup', methods=['POST'])
def cleanup_logs():
    """Deleta logs antigos"""
    try:
        data = request.get_json() or {}
        days = int(data.get('days', 30))

        deleted = log_db.delete_old_logs(days=days)

        log_message('INFO', f'{deleted} logs antigos deletados')

        return jsonify({
            'success': True,
            'deleted': deleted
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao limpar logs: {str(e)}')
        return jsonify({'error': str(e)}), 500


# ============================================
# ALERT ENDPOINTS - Sistema de Alertas
# ============================================

# Configuração de alertas (armazenada em arquivo JSON)
ALERT_CONFIG_PATH = os.path.expanduser('~/mac-studio-alert-config.json')

def load_alert_config():
    """Carrega configuração de alertas"""
    try:
        if os.path.exists(ALERT_CONFIG_PATH):
            with open(ALERT_CONFIG_PATH, 'r') as f:
                return json.load(f)
    except:
        pass
    # Defaults
    return {
        'enabled': True,
        'thresholds': {
            'cpu': 80,
            'ram': 90,
            'temp': 80,
            'disk': 95
        }
    }

def save_alert_config(config):
    """Salva configuração de alertas"""
    with open(ALERT_CONFIG_PATH, 'w') as f:
        json.dump(config, f, indent=2)

@app.route('/api/alerts/config', methods=['GET', 'POST'])
def alert_config():
    """GET: Retorna configuração de alertas | POST: Atualiza configuração"""
    try:
        if request.method == 'GET':
            config = load_alert_config()
            return jsonify({
                'success': True,
                'config': config
            })

        # POST - Atualizar configuração
        data = request.get_json()
        config = load_alert_config()

        if 'enabled' in data:
            config['enabled'] = bool(data['enabled'])

        if 'thresholds' in data:
            config['thresholds'].update(data['thresholds'])

        save_alert_config(config)
        log_message('INFO', 'Configuração de alertas atualizada', context=config)

        return jsonify({
            'success': True,
            'config': config
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao gerenciar config de alertas: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/api/alerts/history', methods=['GET'])
def alert_history():
    """Retorna histórico de alertas"""
    try:
        hours = int(request.args.get('hours', 24))
        limit = int(request.args.get('limit', 100))

        alerts = log_db.get_alerts(hours=hours, limit=limit)

        return jsonify({
            'success': True,
            'alerts': alerts,
            'total': len(alerts)
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao buscar histórico de alertas: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/api/alerts/clear', methods=['POST'])
def clear_alerts():
    """Limpa histórico de alertas"""
    try:
        data = request.get_json() or {}
        hours = data.get('hours')  # None = limpar todos

        deleted = log_db.clear_alerts(hours=hours)

        log_message('INFO', f'{deleted} alertas deletados')

        return jsonify({
            'success': True,
            'deleted': deleted
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao limpar alertas: {str(e)}')
        return jsonify({'error': str(e)}), 500

@app.route('/api/alerts/create', methods=['POST'])
def create_alert():
    """Cria um novo alerta (chamado pelo frontend)"""
    try:
        data = request.get_json()

        level = data.get('level', 'WARN')
        metric = data.get('metric')
        value = float(data.get('value', 0))
        threshold = float(data.get('threshold', 0))
        message = data.get('message', '')

        alert_id = log_db.add_alert(level, metric, value, threshold, message)

        return jsonify({
            'success': True,
            'alert_id': alert_id
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao criar alerta: {str(e)}')
        return jsonify({'error': str(e)}), 500


# ============================================
# PROCESS MANAGEMENT ENDPOINTS
# ============================================

@app.route('/api/processes/list', methods=['GET'])
def list_processes():
    """Lista processos do Mac Studio via SSH"""
    try:
        # Comando: ps aux com formato customizado
        # Output: PID|USER|%CPU|%MEM|COMMAND
        cmd = "ps aux | awk 'NR>1 {printf \"%s|%s|%s|%s|\",$2,$1,$3,$4; for(i=11;i<=NF;i++) printf \"%s \",$i; print \"\"}'"

        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'error': result.get('stderr', 'Erro ao listar processos')}), 500

        # Parse output
        processes = []
        lines = result['stdout'].strip().split('\n')

        for line in lines:
            if not line.strip():
                continue

            parts = line.split('|')
            if len(parts) >= 5:
                try:
                    processes.append({
                        'pid': int(parts[0]),
                        'user': parts[1],
                        'cpu': float(parts[2]),
                        'ram': float(parts[3]),
                        'command': parts[4].strip()
                    })
                except (ValueError, IndexError):
                    # Ignorar linhas com formato inválido
                    continue

        log_message('INFO', f'{len(processes)} processos listados')

        return jsonify({
            'success': True,
            'processes': processes,
            'total': len(processes)
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao listar processos: {str(e)}')
        return jsonify({'error': str(e)}), 500


@app.route('/api/processes/kill', methods=['POST'])
def kill_process():
    """Mata um processo no Mac Studio via SSH"""
    try:
        data = request.get_json()
        pid = data.get('pid')

        if not pid:
            return jsonify({'error': 'PID não fornecido'}), 400

        # Validar PID (deve ser número positivo)
        try:
            pid = int(pid)
            if pid <= 0:
                raise ValueError()
        except (ValueError, TypeError):
            return jsonify({'error': 'PID inválido'}), 400

        # Verificar se processo existe antes de matar
        check_cmd = f"ps -p {pid} > /dev/null 2>&1 && echo 'exists' || echo 'not found'"
        check_result = execute_ssh_command(check_cmd)

        if not check_result['success'] or 'not found' in check_result['stdout']:
            return jsonify({'error': f'Processo {pid} não encontrado'}), 404

        # Matar processo
        kill_cmd = f"kill -9 {pid}"
        result = execute_ssh_command(kill_cmd)

        if result['success']:
            log_message('WARN', f'Processo {pid} morto', context={'pid': pid})

            return jsonify({
                'success': True,
                'message': f'Processo {pid} morto com sucesso'
            })
        else:
            error_msg = result.get('stderr', 'Erro desconhecido')
            log_message('ERROR', f'Erro ao matar processo {pid}: {error_msg}')

            return jsonify({
                'success': False,
                'error': error_msg
            }), 500

    except Exception as e:
        log_message('ERROR', f'Erro ao matar processo: {str(e)}')
        return jsonify({'error': str(e)}), 500


# ===========================
# FILE BROWSER ENDPOINTS
# ===========================

@app.route('/api/files/list', methods=['GET'])
def list_files():
    """Lista arquivos e diretórios de um caminho"""
    try:
        path = request.args.get('path', '/Users/' + MAC_STUDIO_USER)

        # Validar path (segurança básica)
        if '..' in path or path.startswith('/etc') or path.startswith('/var'):
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # ls -lah com parsing
        cmd = f'ls -lah "{path}" 2>&1'
        result = execute_ssh_command(cmd)

        if not result['success'] or 'No such file' in result.get('stdout', ''):
            return jsonify({'success': False, 'error': 'Diretório não encontrado'}), 404

        # Parse ls output
        lines = result['stdout'].strip().split('\n')
        files = []

        for line in lines[1:]:  # Skip "total X" line
            if not line.strip():
                continue

            parts = line.split()
            if len(parts) < 9:
                continue

            # Parse ls -lah format
            permissions = parts[0]
            size = parts[4]
            month = parts[5]
            day = parts[6]
            time = parts[7]
            name = ' '.join(parts[8:])

            # Skip . and ..
            if name in ['.', '..']:
                continue

            file_type = 'dir' if permissions.startswith('d') else 'file'

            files.append({
                'name': name,
                'type': file_type,
                'size': size,
                'modified': f'{month} {day} {time}',
                'permissions': permissions
            })

        log_message('INFO', f'Listou {len(files)} arquivos em {path}')

        return jsonify({
            'success': True,
            'path': path,
            'files': files,
            'count': len(files)
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao listar arquivos: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/download', methods=['GET'])
def download_file():
    """Baixa conteúdo de um arquivo"""
    try:
        path = request.args.get('path', '')

        if not path:
            return jsonify({'success': False, 'error': 'Path obrigatório'}), 400

        # Validar path
        if '..' in path:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Ler arquivo via SSH
        cmd = f'cat "{path}"'
        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': 'Arquivo não encontrado'}), 404

        log_message('INFO', f'Download: {path}')

        return jsonify({
            'success': True,
            'path': path,
            'content': result['stdout']
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao baixar arquivo: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/upload', methods=['POST'])
def upload_file():
    """Faz upload de conteúdo para um arquivo"""
    try:
        data = request.json
        path = data.get('path', '')
        content = data.get('content', '')

        if not path:
            return jsonify({'success': False, 'error': 'Path obrigatório'}), 400

        # Validar path
        if '..' in path:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Escapar conteúdo para heredoc
        content_escaped = content.replace("'", "'\\''")

        # Escrever arquivo via SSH (heredoc)
        cmd = f"cat > '{path}' << 'EOFMARKER'\n{content_escaped}\nEOFMARKER"
        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': 'Erro ao escrever arquivo'}), 500

        log_message('INFO', f'Upload: {path} ({len(content)} bytes)')

        return jsonify({
            'success': True,
            'path': path,
            'message': f'Arquivo salvo com sucesso ({len(content)} bytes)'
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao fazer upload: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/delete', methods=['DELETE', 'POST'])
def delete_file():
    """Deleta arquivo ou diretório"""
    try:
        data = request.json
        path = data.get('path', '')

        if not path:
            return jsonify({'success': False, 'error': 'Path obrigatório'}), 400

        # Validar path (não permitir deletar raiz ou system dirs)
        if '..' in path or path in ['/', '/Users', '/System', '/Applications']:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Verificar se é diretório ou arquivo
        check_cmd = f'[ -d "{path}" ] && echo "dir" || echo "file"'
        check_result = execute_ssh_command(check_cmd)

        file_type = check_result['stdout'].strip()

        # Comando apropriado
        if file_type == 'dir':
            cmd = f'rm -rf "{path}"'
        else:
            cmd = f'rm -f "{path}"'

        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': 'Erro ao deletar'}), 500

        log_message('WARN', f'Deletado: {path} ({file_type})')

        return jsonify({
            'success': True,
            'path': path,
            'message': f'{file_type.capitalize()} deletado com sucesso'
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao deletar: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/create', methods=['POST'])
def create_file():
    """Cria novo arquivo ou diretório"""
    try:
        data = request.json
        path = data.get('path', '')
        file_type = data.get('type', 'file')  # 'file' ou 'dir'

        if not path:
            return jsonify({'success': False, 'error': 'Path obrigatório'}), 400

        # Validar path
        if '..' in path:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Comando apropriado
        if file_type == 'dir':
            cmd = f'mkdir -p "{path}"'
        else:
            cmd = f'touch "{path}"'

        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': f'Erro ao criar {file_type}'}), 500

        log_message('INFO', f'Criado {file_type}: {path}')

        return jsonify({
            'success': True,
            'path': path,
            'type': file_type,
            'message': f'{file_type.capitalize()} criado com sucesso'
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao criar: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/rename', methods=['POST'])
def rename_file():
    """Renomeia arquivo ou diretório"""
    try:
        data = request.json
        old_path = data.get('old_path', '')
        new_path = data.get('new_path', '')

        if not old_path or not new_path:
            return jsonify({'success': False, 'error': 'old_path e new_path obrigatórios'}), 400

        # Validar paths
        if '..' in old_path or '..' in new_path:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Comando mv
        cmd = f'mv "{old_path}" "{new_path}"'
        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': 'Erro ao renomear'}), 500

        log_message('INFO', f'Renomeado: {old_path} → {new_path}')

        return jsonify({
            'success': True,
            'old_path': old_path,
            'new_path': new_path,
            'message': 'Renomeado com sucesso'
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao renomear: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/files/preview', methods=['GET'])
def preview_file():
    """Preview de arquivo de texto (primeiras 100 linhas)"""
    try:
        path = request.args.get('path', '')

        if not path:
            return jsonify({'success': False, 'error': 'Path obrigatório'}), 400

        # Validar path
        if '..' in path:
            return jsonify({'success': False, 'error': 'Caminho não permitido'}), 403

        # Ler primeiras 100 linhas
        cmd = f'head -n 100 "{path}"'
        result = execute_ssh_command(cmd)

        if not result['success']:
            return jsonify({'success': False, 'error': 'Arquivo não encontrado'}), 404

        # Obter tamanho do arquivo
        size_cmd = f'wc -c < "{path}"'
        size_result = execute_ssh_command(size_cmd)
        file_size = size_result['stdout'].strip() if size_result['success'] else '?'

        log_message('INFO', f'Preview: {path}')

        return jsonify({
            'success': True,
            'path': path,
            'content': result['stdout'],
            'size': file_size,
            'truncated': len(result['stdout'].split('\n')) >= 100
        })

    except Exception as e:
        log_message('ERROR', f'Erro ao fazer preview: {str(e)}')
        return jsonify({'success': False, 'error': str(e)}), 500


if __name__ == '__main__':
    print('=' * 60)
    print('Mac Studio Control - Backend SSH')
    print('=' * 60)
    print(f'Mac Studio IP: {MAC_STUDIO_IP}')
    print(f'Mac Studio User: {MAC_STUDIO_USER}')
    print(f'SSH Key: {SSH_KEY_PATH}')
    print(f'Comandos permitidos: {len(ALLOWED_COMMANDS)}')
    print('=' * 60)

    log_message('INFO', 'Backend iniciado')

    # Inicia servidor
    app.run(
        host='0.0.0.0',
        port=9003,
        debug=True
    )
