from flask import Flask, render_template, request, redirect, url_for, session, jsonify
import os
import subprocess
import threading
import uuid
import time
from config import MAIN_SERVER_IP, VM_HOST, VM_PORT, VM_KEY, VM_USER, RUNNING_ON_MAIN_SERVER
from modules.auth import login_required
from modules.backups import get_containers, get_local_backups, get_vm_backups
from modules.commands import run_command, run_ssh_to_vm

app = Flask(__name__)
app.secret_key = 'navitrends-secret-key-2025'

restore_jobs = {}


def _stream_restore(job_id, cmd):
    restore_jobs[job_id] = {'status': 'running', 'log': [], 'started': time.time()}
    try:
        proc = subprocess.Popen(
            cmd, shell=True,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
            text=True, bufsize=1
        )
        for line in proc.stdout:
            restore_jobs[job_id]['log'].append(line.rstrip())
        proc.wait()
        restore_jobs[job_id]['status'] = 'done' if proc.returncode == 0 else 'error'
        restore_jobs[job_id]['returncode'] = proc.returncode
    except Exception as e:
        restore_jobs[job_id]['log'].append(f"ERROR: {e}")
        restore_jobs[job_id]['status'] = 'error'


@app.route('/')
@login_required
def dashboard():
    containers = get_containers()
    running_count = sum(1 for c in containers if 'Up' in c.get('status', ''))
    backups = get_local_backups()
    vm_backups = get_vm_backups()
    return render_template('dashboard.html',
                           containers=containers,
                           running_count=running_count,
                           backups=backups,
                           vm_backups=vm_backups,
                           main_server=MAIN_SERVER_IP)


@app.route('/restore/start', methods=['POST'])
@login_required
def restore_start():
    data = request.get_json()
    if not data:
        return jsonify({'error': 'No JSON body'}), 400

    backup_source = data.get('backup_source', 'local')
    backup_file   = data.get('backup_file', '').strip()
    target        = data.get('target', 'local')
    remote_ip     = data.get('remote_ip', '').strip()
    remote_port   = str(data.get('remote_port', '22')).strip() or '22'
    remote_user   = data.get('remote_user', 'root').strip() or 'root'
    auth_method   = data.get('auth_method', 'key')
    ssh_key_path  = data.get('ssh_key_path', VM_KEY).strip()
    ssh_password  = data.get('ssh_password', '').strip()

    if not backup_file:
        return jsonify({'error': 'No backup file specified'}), 400

    # ── Resolve backup archive path on THIS server ───────────────────────────
    if backup_source == 'local':
        # Local path depends on which server we're running on
        if RUNNING_ON_MAIN_SERVER:
            backup_path = f"/root/backups/{backup_file}"
        else:
            backup_path = f"/backups/main-server/{backup_file}"

        if not os.path.exists(backup_path):
            return jsonify({'error': f'Backup not found: {backup_path}'}), 400

    else:
        # "Other server" backup source — pull it to /tmp/ first
        backup_path = f"/tmp/{backup_file}"
        if not os.path.exists(backup_path):
            if RUNNING_ON_MAIN_SERVER:
                # Original logic: pull from VM via SSH tunnel (port 2223)
                pull_cmd = (
                    f"scp -i {VM_KEY} -P {VM_PORT} "
                    f"-o StrictHostKeyChecking=no -o ConnectTimeout=15 "
                    f"{VM_USER}@{VM_HOST}:/backups/main-server/{backup_file} "
                    f"{backup_path}"
                )
            else:
                # On VM: pull from main server's /root/backups/ via port 22
                pull_cmd = (
                    f"scp -i {VM_KEY} -P 22 "
                    f"-o StrictHostKeyChecking=no -o ConnectTimeout=15 "
                    f"{VM_USER}@{MAIN_SERVER_IP}:/root/backups/{backup_file} "
                    f"{backup_path}"
                )

            result = subprocess.run(pull_cmd, shell=True, capture_output=True, text=True)
            if result.returncode != 0:
                return jsonify({'error': f'Failed to pull backup: {result.stderr}'}), 500

    restore_script_local = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'restore-myapps.sh'
    )
    if not os.path.exists(restore_script_local):
        return jsonify({'error': f'restore-myapps.sh not found at {restore_script_local}'}), 500

    # ── Build command ────────────────────────────────────────────────────────
    if target == 'local':
        session_dir = f"/tmp/restore-session-{uuid.uuid4().hex[:8]}"
        cmd = (
            f"set -e && "
            f"mkdir -p {session_dir} && "
            f"echo '📂 Extracting backup locally...' && "
            f"tar -xzf {backup_path} -C {session_dir} --strip-components=1 && "
            f"cp {restore_script_local} {session_dir}/restore-myapps.sh && "
            f"chmod +x {session_dir}/restore-myapps.sh && "
            f"cd {session_dir} && "
            f"bash restore-myapps.sh ; "
            f"EXIT=$? ; rm -rf {session_dir} ; exit $EXIT"
        )

    else:
        if not remote_ip:
            return jsonify({'error': 'remote_ip is required for remote restore'}), 400

        base_ssh_opts = f"-o StrictHostKeyChecking=no -o ConnectTimeout=15"

        if auth_method == 'key':
            if not ssh_key_path:
                return jsonify({'error': 'ssh_key_path is required'}), 400
            ssh_prefix = f"ssh -p {remote_port} -i {ssh_key_path} {base_ssh_opts}"
            scp_prefix = f"scp -P {remote_port} -i {ssh_key_path} {base_ssh_opts}"
        else:
            if not ssh_password:
                return jsonify({'error': 'ssh_password is required'}), 400
            ssh_prefix = f"sshpass -p '{ssh_password}' ssh -p {remote_port} {base_ssh_opts}"
            scp_prefix = f"sshpass -p '{ssh_password}' scp -P {remote_port} {base_ssh_opts}"

        remote_dest = f"/backups/restore-session-{uuid.uuid4().hex[:8]}"

        cmd = (
            f"echo '🔗 Connecting to {remote_user}@{remote_ip}:{remote_port}...' && "
            f"{ssh_prefix} {remote_user}@{remote_ip} 'mkdir -p {remote_dest}' && "
            f"echo '✅ Connected.' && "

            f"echo '📤 Copying backup archive to {remote_ip}:{remote_port}...' && "
            f"{scp_prefix} {backup_path} {remote_user}@{remote_ip}:{remote_dest}/{backup_file} && "

            f"echo '📤 Copying restore script...' && "
            f"{scp_prefix} {restore_script_local} {remote_user}@{remote_ip}:{remote_dest}/restore-myapps.sh && "

            f"echo '🚀 Running restore on {remote_ip}:{remote_port}...' && "
            f"{ssh_prefix} {remote_user}@{remote_ip} "
            f"'set -e && "
            f"cd {remote_dest} && "
            f"echo \"📂 Extracting backup...\" && "
            f"tar -xzf {backup_file} --strip-components=1 && "
            f"chmod +x restore-myapps.sh && "
            f"bash restore-myapps.sh' ; "

            f"EXIT=$? ; "
            f"{ssh_prefix} {remote_user}@{remote_ip} 'rm -rf {remote_dest}' 2>/dev/null ; "
            f"exit $EXIT"
        )

    job_id = str(uuid.uuid4())
    t = threading.Thread(target=_stream_restore, args=(job_id, cmd), daemon=True)
    t.start()

    return jsonify({'job_id': job_id, 'status': 'started'})


@app.route('/restore/status/<job_id>')
@login_required
def restore_status_poll(job_id):
    job = restore_jobs.get(job_id)
    if not job:
        return jsonify({'error': 'Job not found'}), 404
    return jsonify({
        'status':  job['status'],
        'log':     job['log'],
        'elapsed': round(time.time() - job.get('started', time.time()))
    })


@app.route('/api/backups')
@login_required
def api_backups():
    return jsonify({'local': get_local_backups(), 'vm': get_vm_backups()})


@app.route('/api/containers')
@login_required
def api_containers():
    containers = get_containers()
    return jsonify({
        'containers': containers,
        'running': sum(1 for c in containers if 'Up' in c.get('status', ''))
    })


@app.route('/server/status')
@login_required
def server_status():
    stdout, stderr = run_command("uptime")
    if stderr or not stdout:
        return jsonify({'status': 'offline', 'error': stderr or 'Failed'})
    return jsonify({'status': 'online', 'info': stdout.strip()})


@app.route('/login', methods=['GET', 'POST'])
def login():
    error = ''
    if request.method == 'POST':
        if request.form.get('password') == 'admin123':
            session['logged_in'] = True
            return redirect(url_for('dashboard'))
        error = 'Wrong password'
    return render_template('login.html', error=error)


@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    return redirect(url_for('login'))


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)
