import os
import subprocess
import uuid
import yaml
import paramiko
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS
from flask_jwt_extended import (
    JWTManager, create_access_token,
    jwt_required, get_jwt_identity
)
from passlib.hash import pbkdf2_sha256
from logging.handlers import RotatingFileHandler
import json
from datetime import datetime, timedelta
import random
import logging
import string
import user_agents
import traceback
from werkzeug.exceptions import BadRequest
from apscheduler.schedulers.background import BackgroundScheduler
from jinja2 import Template

# Load environment variables for sensitive configurations
from dotenv import load_dotenv
load_dotenv()  # Takes environment variables from a .env file

SSH_CONFIG = {
    'hostname': os.getenv('SSH_HOSTNAME', '78.46.62.242'),
    'port': int(os.getenv('SSH_PORT', 22)),
    'username': os.getenv('SSH_USERNAME', 'root'),
    'password': os.getenv('SSH_PASSWORD'),  # Ensure this is set
    'key_filepath': os.getenv('SSH_KEY_FILEPATH')  # Optional: path to SSH private key
}

app = Flask(__name__, static_folder='static', template_folder='templates')
CORS(app)

# JWT Configuration
app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'default_jwt_secret_key')  # Set this in environment
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
jwt = JWTManager(app)

OS_OPTIONS = {
    'win2020': 'Windows Server 2020',
    'win2022': 'Windows Server 2022',
    'win2025': 'Windows Server 2025',
    'win10': 'Windows 10',
    'win11': 'Windows 11'
}

IP_POOL_START = [192, 168, 1, 100]  # Starting IP address
current_ip = IP_POOL_START.copy()

CONTAINERS_DIR = os.path.join(os.getcwd(), 'containers')
TEMPLATES_DIR = os.path.join(CONTAINERS_DIR, 'templates')
os.makedirs(TEMPLATES_DIR, exist_ok=True)

# Ensure the Docker Compose template exists
TEMPLATE_PATH = os.path.join(TEMPLATES_DIR, 'docker-compose-template.yml')
if not os.path.exists(TEMPLATE_PATH):
    with open(TEMPLATE_PATH, 'w') as f:
        f.write("""version: '3'
services:
  windows:
    image: dockurr/windows  # Ensure this image exists or adjust to the correct one
    container_name: {{ container_name }}
    environment:
      VERSION: "{{ version }}"
      DISK_SIZE: "{{ disk_size }}"
      RAM_SIZE: "{{ ram_size }}"
      CPU_CORES: "{{ cpu_cores }}"
      USERNAME: "{{ username }}"
      PASSWORD: "{{ password }}"
      KEYBOARD: "{{ keyboard }}"
    devices:
      - /dev/kvm
    cap_add:
      - NET_ADMIN
    ports:
      - "{{ public_port_8006 }}:8006/tcp"
      - "{{ public_port_rdp_tcp }}:3389/tcp"
      - "{{ public_port_rdp_udp }}:3389/udp"
    stop_grace_period: 2m
    restart: unless-stopped
""")

def generate_unique_ip():
    global current_ip
    ip = '.'.join(map(str, current_ip))
    current_ip[3] += 1
    if current_ip[3] > 254:
        current_ip[3] = 1
        current_ip[2] += 1
        if current_ip[2] > 254:
            current_ip[2] = 1
            current_ip[1] += 1
            if current_ip[1] > 254:
                current_ip[1] = 1
                current_ip[0] += 1
                if current_ip[0] > 254:
                    raise Exception("IP Pool Exhausted")
    return ip

@app.route('/')
def home():
    return render_template('index.html')

def setup_logging():
    # Create logs directory if it doesn't exist
    if not os.path.exists('logs'):
        os.makedirs('logs')
    
    # Configure main application logger
    formatter = logging.Formatter(
        '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
    )
    
    # File handler for general logs
    file_handler = RotatingFileHandler(
        'logs/app.log', 
        maxBytes=1024 * 1024,  # 1MB
        backupCount=10
    )
    file_handler.setFormatter(formatter)
    file_handler.setLevel(logging.INFO)
    
    # File handler for errors
    error_file_handler = RotatingFileHandler(
        'logs/error.log',
        maxBytes=1024 * 1024,  # 1MB
        backupCount=10
    )
    error_file_handler.setFormatter(formatter)
    error_file_handler.setLevel(logging.ERROR)
    
    # Configure Flask logger
    app.logger.addHandler(file_handler)
    app.logger.addHandler(error_file_handler)
    app.logger.setLevel(logging.INFO)
    
    # Log basic request info
    @app.before_request
    def log_request_info():
        app.logger.info(f'Request: {request.method} {request.url} from {request.remote_addr}')
        app.logger.debug(f'Headers: {dict(request.headers)}')
    
    # Error handler
    @app.errorhandler(Exception)
    def handle_exception(e):
        app.logger.error(f'Unhandled Exception: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({
            'error': 'An internal server error occurred',
            'details': str(e) if app.debug else None
        }), 500

def load_db():
    try:
        if not os.path.exists('db.json'):
            with open('db.json', 'w') as f:
                json.dump({'users': {}, 'logs': []}, f)
        with open('db.json', 'r') as f:
            db = json.load(f)
        return db
    except Exception as e:
        app.logger.error(f'Database load error: {str(e)}')
        app.logger.error(traceback.format_exc())
        raise

def save_db(db):
    with open('db.json', 'w') as f:
        json.dump(db, f, indent=2)

def log_activity(username, action, user_agent_string):
    try:
        db = load_db()
        ua = user_agents.parse(user_agent_string)
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'username': username,
            'action': action,
            'browser': ua.browser.family,
            'os': ua.os.family,
            'device': ua.device.family,
            'ip': request.remote_addr
        }
        db['logs'].append(log_entry)
        save_db(db)
    except Exception as e:
        app.logger.error(f'Error logging activity: {str(e)}')
        app.logger.error(traceback.format_exc())

def generate_password():
    return ''.join(random.choices(string.ascii_letters + string.digits, k=12))

def connect_via_ssh():
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    connect_params = {
        'hostname': SSH_CONFIG['hostname'],
        'port': SSH_CONFIG.get('port', 22),
        'username': SSH_CONFIG['username']
    }
    if SSH_CONFIG.get('key_filepath'):
        connect_params['key_filename'] = SSH_CONFIG['key_filepath']
    elif SSH_CONFIG.get('password'):
        connect_params['password'] = SSH_CONFIG['password']
    else:
        raise ValueError("Either 'key_filepath' or 'password' must be provided in SSH_CONFIG.")
    
    try:
        ssh.connect(**connect_params)
        app.logger.info(f"SSH connection established to {SSH_CONFIG['hostname']}")
    except Exception as e:
        app.logger.error(f'SSH connection failed: {e}')
        return None
    return ssh

def execute_ssh_command(command, ssh_client, remote_path=None):
    try:
        if remote_path:
            command = f"cd {remote_path} && {command}"
        
        app.logger.info(f"Executing SSH Command: {command}")
        stdin, stdout, stderr = ssh_client.exec_command(command)
        stdout_str = stdout.read().decode().strip()
        stderr_str = stderr.read().decode().strip()
        return stdout_str, stderr_str
    except Exception as e:
        app.logger.error(f'SSH command execution error: {e}')
        return None, str(e)

def create_docker_compose_content(container_name, version, plan_details, rdp_username, rdp_password, public_port_8006, public_port_rdp_tcp, public_port_rdp_udp):
    docker_compose_template = """
version: '3'
services:
  windows:
    image: dockurr/windows  # Ensure this image exists or adjust to the correct one
    container_name: {{ container_name }}
    environment:
      VERSION: "{{ version }}"
      DISK_SIZE: "{{ disk_size }}"
      RAM_SIZE: "{{ ram_size }}"
      CPU_CORES: "{{ cpu_cores }}"
      USERNAME: "{{ username }}"
      PASSWORD: "{{ password }}"
      KEYBOARD: "us"
    devices:
      - /dev/kvm
    cap_add:
      - NET_ADMIN
    ports:
      - "{{ public_port_8006 }}:8006/tcp"
      - "{{ public_port_rdp_tcp }}:3389/tcp"
      - "{{ public_port_rdp_udp }}:3389/udp"
    stop_grace_period: 2m
    restart: unless-stopped
"""
    template = Template(docker_compose_template)
    return template.render(
        container_name=container_name,
        version=version,
        disk_size=plan_details['disk'],
        ram_size=plan_details['ram'],
        cpu_cores=plan_details['cpu'],
        username=rdp_username,
        password=rdp_password,
        public_port_8006=public_port_8006,
        public_port_rdp_tcp=public_port_rdp_tcp,
        public_port_rdp_udp=public_port_rdp_udp
    )

# Define server plans centrally in the backend
SERVER_PLANS = [
    {
        "key": "basic",
        "name": "Базовый",
        "cpu": 2,
        "ram": 4,
        "disk": "50GB",
        "price": 500
    },
    {
        "key": "advanced",
        "name": "Продвинутый",
        "cpu": 4,
        "ram": 8,
        "disk": "100GB",
        "price": 1000
    },
    {
        "key": "premium",
        "name": "Премиум",
        "cpu": 8,
        "ram": 16,
        "disk": "200GB",
        "price": 2000
    }
]

@app.route('/api/login', methods=['POST'])
def login():
    try:
        app.logger.info('Processing login request')
        db = load_db()
        data = request.get_json()
        
        if not data:
            app.logger.warning('No JSON data in login request')
            return jsonify({'error': 'No JSON data provided'}), 400
        
        username = data.get('username')
        password = data.get('password')
        
        app.logger.info(f'Login attempt for user: {username}')
        
        if not username or not password:
            app.logger.warning('Missing username or password in login request')
            return jsonify({'error': 'Username and password are required'}), 400
        
        user_data = db['users'].get(username)
        if not user_data:
            app.logger.warning(f'Login failed: User {username} not found')
            return jsonify({'error': 'Invalid credentials'}), 401
            
        if pbkdf2_sha256.verify(password, user_data['password']):
            app.logger.info(f'Login successful for user: {username}')
            access_token = create_access_token(identity=username)
            try:
                log_activity(username, 'login', request.headers.get('User-Agent', ''))
            except Exception as e:
                app.logger.error(f'Failed to log login activity: {str(e)}')
                # Continue with login even if logging fails
            return jsonify({
                'token': access_token,
                'username': username,
                'balance': user_data.get('balance', 0)
            })
        
        app.logger.warning(f'Login failed: Invalid password for user {username}')
        return jsonify({'error': 'Invalid credentials'}), 401
        
    except Exception as e:
        app.logger.error(f'Login error: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({'error': 'An internal server error occurred'}), 500

@app.route('/api/register', methods=['POST'])
def register():
    try:
        app.logger.info('Processing registration request')
        db = load_db()
        data = request.get_json()
        
        if not data:
            app.logger.warning('No JSON data in registration request')
            return jsonify({'error': 'No JSON data provided'}), 400
        
        username = data.get('username')
        password = data.get('password')
        
        app.logger.info(f'Registration attempt for username: {username}')
        
        if not username or not password:
            app.logger.warning('Missing username or password in registration request')
            return jsonify({'error': 'Username and password are required'}), 400
        
        if username in db['users']:
            app.logger.warning(f'Registration failed: Username {username} already exists')
            return jsonify({'error': 'Username already taken'}), 400
        
        hashed_password = pbkdf2_sha256.hash(password)
        
        db['users'][username] = {
            'password': hashed_password,
            'servers': [],
            'balance': 0,
            'tickets': [],
            'created_at': datetime.now().isoformat()
        }
        
        save_db(db)
        app.logger.info(f'Registration successful for user: {username}')
        log_activity(username, 'register', request.headers.get('User-Agent', ''))
        return jsonify({'message': 'Registration successful'}), 201
        
    except Exception as e:
        app.logger.error(f'Registration error: {str(e)}')
        app.logger.error(traceback.format_exc())
        return jsonify({'error': 'An internal server error occurred'}), 500

@app.route('/api/servers', methods=['GET', 'POST'])
@jwt_required()
def manage_servers():
    current_user = get_jwt_identity()
    db = load_db()

    # Retrieve user data
    user_data = db['users'].get(current_user)
    if not user_data:
        app.logger.warning(f'User {current_user} not found when accessing /api/servers')
        return jsonify({'error': 'User not found'}), 404

    if request.method == 'GET':
        # Fetch and return the list of servers for the current user
        servers = user_data.get('servers', [])
        return jsonify(servers), 200

    elif request.method == 'POST':
        data = request.get_json()

        if not data or 'plan' not in data or 'os' not in data or 'name' not in data:
            app.logger.warning('Invalid request data for server creation')
            return jsonify({'error': 'Invalid request data'}), 400

        plan_key = data['plan']
        os_selected = data['os']

        # Find the plan by key
        selected_plan = next((p for p in SERVER_PLANS if p['key'] == plan_key), None)

        if not selected_plan:
            app.logger.error(f"Invalid plan selected: {plan_key}")
            return jsonify({'error': 'Invalid plan selected'}), 400

        # Check user balance
        if user_data.get('balance', 0) < selected_plan['price']:
            app.logger.error(f"User {current_user} has insufficient funds.")
            return jsonify({'error': 'Insufficient funds'}), 400

        if os_selected not in OS_OPTIONS:
            app.logger.error(f"Invalid operating system selected: {os_selected}")
            return jsonify({'error': 'Invalid operating system selected'}), 400

        # Proceed with server creation logic...

        try:
            server_ip = generate_unique_ip()
        except Exception as e:
            app.logger.error(f"IP generation error: {e}")
            return jsonify({'error': 'Failed to generate unique IP address'}), 500

        container_name = f"server_{uuid.uuid4().hex[:8]}"
        rdp_password = generate_password()
        public_port_8006 = random.randint(10000, 60000)
        public_port_rdp_tcp = random.randint(10000, 60000)
        public_port_rdp_udp = public_port_rdp_tcp

        # Generate Docker Compose content
        docker_compose_content = create_docker_compose_content(
            container_name, os_selected, selected_plan,
            data.get('rdp_username', 'admin'), rdp_password,
            public_port_8006, public_port_rdp_tcp, public_port_rdp_udp
        )

        # Connect via SSH and deploy the server
        ssh_client = connect_via_ssh()
        if not ssh_client:
            return jsonify({'error': 'Failed to establish SSH connection'}), 500

        remote_path = f"/root/{container_name}"

        try:
            # Create remote directory
            try:
                ssh_client.exec_command(f"mkdir -p {remote_path}")
                app.logger.info(f"Created remote directory: {remote_path}")
            except Exception as e:
                app.logger.error(f"Failed to create remote directory {remote_path}: {e}")
                raise

            # Create docker-compose.yml directly on the remote server
            docker_compose_command = f"""cat <<EOF > {remote_path}/docker-compose.yml
{docker_compose_content}
EOF
"""
            app.logger.info(f"Creating docker-compose.yml on remote server for {container_name}")
            stdin, stdout, stderr = ssh_client.exec_command(docker_compose_command)
            exit_status = stdout.channel.recv_exit_status()
            if exit_status != 0:
                error = stderr.read().decode().strip()
                app.logger.error(f"Error creating docker-compose.yml on remote server: {error}")
                raise Exception(f"Failed to create docker-compose.yml: {error}")
            app.logger.info(f"docker-compose.yml created successfully at {remote_path}")

            # Validate docker-compose.yml on remote server
            validate_command = f"docker-compose -f {remote_path}/docker-compose.yml config"
            validate_stdout, validate_stderr = execute_ssh_command(validate_command, ssh_client, remote_path=remote_path)
            if validate_stderr:
                app.logger.error(f'Docker Compose Validation Error: {validate_stderr}')
                raise Exception(f'Docker Compose validation failed: {validate_stderr}')
            else:
                app.logger.info("Docker Compose configuration is valid.")

            # Deploy the container
            deploy_command = f"docker-compose -f {remote_path}/docker-compose.yml up -d"
            deploy_stdout, deploy_stderr = execute_ssh_command(deploy_command, ssh_client, remote_path=remote_path)
            if deploy_stderr:
                app.logger.error(f'Docker Compose Deployment Error: {deploy_stderr}')
                raise Exception(f'Docker Compose deployment failed: {deploy_stderr}')
            else:
                app.logger.info(f"Server {container_name} deployed successfully.")
        except Exception as e:
            app.logger.error(f'Error during SSH operation: {e}')
            return jsonify({'error': f'Failed to deploy server: {str(e)}'}), 500
        finally:
            ssh_client.close()

        # Register the new server
        new_server = {
            'id': len(user_data['servers']) + 1,
            'name': data['name'],
            'plan': selected_plan,
            'os': OS_OPTIONS[os_selected],
            'status': 'active',
            'ip': server_ip,
            'created_at': datetime.now().isoformat(),
            'container_name': container_name,
            'ports': {
                '8006/tcp': public_port_8006,
                '3389/tcp': public_port_rdp_tcp,
                '3389/udp': public_port_rdp_udp
            }
        }

        user_data['balance'] -= selected_plan['price']
        user_data['servers'].append(new_server)
        save_db(db)
        log_activity(current_user, 'create_server', request.headers.get('User-Agent', ''))
        return jsonify(new_server), 201

@app.route('/api/servers/<int:server_id>', methods=['GET'])
@jwt_required()
def get_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    # Optionally, format the server data to include plan name and key
    formatted_server = {
        'id': server['id'],
        'name': server['name'],
        'plan': {
            'key': server['plan']['key'],
            'name': server['plan']['name'],
            'cpu': server['plan']['cpu'],
            'ram': server['plan']['ram'],
            'disk': server['plan']['disk'],
            'price': server['plan']['price']
        },
        'os': server['os'],
        'status': server['status'],
        'ip': server['ip'],
        'created_at': server['created_at'],
        'container_name': server['container_name'],
        'ports': server['ports']
    }

    return jsonify(formatted_server), 200

@app.route('/api/servers/<int:server_id>/toggle', methods=['POST'])
@jwt_required()
def toggle_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    container_name = server.get('container_name')
    if not container_name:
        app.logger.error('Container name not found for this server')
        return jsonify({'error': 'Container name not found for this server. Please contact support.'}), 400

    # Define the command to stop or start the container
    server_dir = os.path.join(CONTAINERS_DIR, container_name)
    if server['status'] == 'active':
        cmd = f"cd {server_dir} && docker-compose stop"
    else:
        cmd = f"cd {server_dir} && docker-compose start"
    
    stdout, stderr = execute_ssh_command(cmd)
    if stderr:
        app.logger.error(f'SSH command error: {stderr}')
        return jsonify({'error': f'Failed to toggle server status: {stderr}'}), 500
    
    # Update the status
    server['status'] = 'frozen' if server['status'] == 'active' else 'active'
    save_db(db)
    log_activity(current_user, f'toggle_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': f"Server status changed to {server['status']}"}), 200

@app.route('/api/servers/<int:server_id>', methods=['DELETE'])
@jwt_required()
def delete_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    container_name = server.get('container_name')
    if not container_name:
        return jsonify({'error': 'Container name not found for this server'}), 400

    # Commands to stop and remove the container on the remote server
    server_dir = os.path.join(CONTAINERS_DIR, container_name)
    commands = [
        f"cd {server_dir} && docker-compose down",
        f"rm -rf {server_dir}"
    ]
    full_command = ' && '.join(commands)
    
    stdout, stderr = execute_ssh_command(full_command)
    if stderr:
        app.logger.error(f'SSH command error: {stderr}')
        return jsonify({'error': f'Failed to delete server: {stderr}'}), 500

    # Remove local archive if exists
    zip_filename = f"{container_name}.tar.gz"
    if os.path.exists(zip_filename):
        os.remove(zip_filename)
    
    # Remove server from the database
    user_data['servers'].remove(server)
    save_db(db)
    log_activity(current_user, f'delete_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': 'Server deleted successfully'}), 200

@app.route('/api/servers/<int:server_id>', methods=['PUT'])
@jwt_required()
def update_server(server_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    server = next((s for s in user_data['servers'] if s['id'] == server_id), None)
    if not server:
        return jsonify({'error': 'Server not found'}), 404

    data = request.get_json()
    if not data:
        return jsonify({'error': 'No data provided'}), 400

    # Example: Allow updating server name and OS
    new_name = data.get('name')
    new_os = data.get('os')

    if new_name:
        server['name'] = new_name

    if new_os:
        if new_os not in OS_OPTIONS:
            return jsonify({'error': 'Invalid operating system selected'}), 400
        # Update Docker Compose file
        container_name = server.get('container_name')
        server_dir = os.path.join(CONTAINERS_DIR, container_name)
        docker_compose_path = os.path.join(server_dir, 'docker-compose.yml')
        
        if not os.path.exists(docker_compose_path):
            return jsonify({'error': 'docker-compose.yml not found for this server'}), 400
        
        # Load current docker-compose.yml
        try:
            with open(docker_compose_path, 'r') as f:
                compose_data = yaml.safe_load(f)
        except Exception as e:
            app.logger.error(f'Error reading docker-compose.yml: {e}')
            return jsonify({'error': 'Failed to read docker-compose.yml'}), 500
        
        # Update OS version
        compose_data['services']['windows']['environment']['VERSION'] = new_os
        compose_data['services']['windows']['os'] = OS_OPTIONS[new_os]
        
        # Write updated docker-compose.yml
        try:
            with open(docker_compose_path, 'w') as f:
                yaml.dump(compose_data, f)
        except Exception as e:
            app.logger.error(f'Error writing docker-compose.yml: {e}')
            return jsonify({'error': 'Failed to update docker-compose.yml'}), 500
        
        # Restart container on remote server to apply changes
        cmd = f"cd {server_dir} && docker-compose up -d"
        stdout, stderr = execute_ssh_command(cmd)
        if stderr:
            app.logger.error(f'SSH command error: {stderr}')
            return jsonify({'error': f'Failed to restart server: {stderr}'}), 500

        # Update OS field in the database
        server['os'] = OS_OPTIONS[new_os]

    save_db(db)
    log_activity(current_user, f'update_server_{server_id}', request.headers.get('User-Agent', ''))
    return jsonify({'message': 'Server updated successfully', 'server': server}), 200

@app.route('/api/userinfo', methods=['GET', 'PUT'])
@jwt_required()
def userinfo():
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    if request.method == 'GET':
        return jsonify({'username': current_user, 'balance': user_data.get('balance', 0)}), 200
    
    elif request.method == 'PUT':
        data = request.get_json()
        if not data:
            return jsonify({'error': 'No data provided'}), 400
        
        current_password = data.get('current_password')
        new_username = data.get('new_username')
        new_password = data.get('new_password')

        if not current_password:
            return jsonify({'error': 'Current password is required'}), 400

        # Verify current password
        if not pbkdf2_sha256.verify(current_password, user_data['password']):
            return jsonify({'error': 'Current password is incorrect'}), 401

        # Update username if provided
        if new_username:
            if new_username in db['users']:
                return jsonify({'error': 'Username already taken'}), 400
            db['users'][new_username] = db['users'].pop(current_user)
            current_user = new_username

        # Update password if provided
        if new_password:
            db['users'][current_user]['password'] = pbkdf2_sha256.hash(new_password)

        save_db(db)
        log_activity(current_user, 'update_userinfo', request.headers.get('User-Agent', ''))
        return jsonify({'message': 'User information updated successfully', 'username': current_user}), 200

@app.route('/api/tickets', methods=['GET', 'POST'])
@jwt_required()
def tickets():
    current_user = get_jwt_identity()
    db = load_db()

    if request.method == 'GET':
        user_data = db['users'].get(current_user)
        if not user_data:
            return jsonify({'error': 'User not found'}), 404

        return jsonify(user_data.get('tickets', [])), 200

    elif request.method == 'POST':
        data = request.get_json()
        if not data or 'title' not in data or 'description' not in data:
            return jsonify({'error': 'Invalid request data'}), 400

        user_data = db['users'].get(current_user)
        if not user_data:
            return jsonify({'error': 'User not found'}), 404

        new_ticket = {
            'id': len(user_data['tickets']) + 1,
            'title': data['title'],
            'description': data.get('description', ''),
            'status': 'open',
            'created_at': datetime.now().isoformat(),
            'messages': []
        }

        user_data['tickets'].append(new_ticket)
        save_db(db)
        log_activity(current_user, 'create_ticket', request.headers.get('User-Agent', ''))
        return jsonify(new_ticket), 201

def monthly_billing_check():
    app.logger.info("Checking for monthly billing...")
    try:
        db = load_db()
        for username, user in db['users'].items():
            for server in user.get('servers', []):
                if server.get("next_payment_due"):
                    due_date = datetime.fromisoformat(server["next_payment_due"])
                    if datetime.now() >= due_date:
                        handle_billing_cycle(user, server)
        save_db(db)
    except Exception as e:
        app.logger.error(f"Error during monthly billing check: {e}")
        app.logger.error(traceback.format_exc())

def handle_billing_cycle(user, server):
    plan = server["plan"]
    plan_price = server["plan"]["price"]

    if user.get("balance", 0) >= plan_price:
        # Deduct the price and update the next payment due date
        user["balance"] -= plan_price
        server["next_payment_due"] = (datetime.now() + timedelta(days=30)).isoformat()
        app.logger.info(
            f"Successfully billed user {user['id']} for server {server['name']}. Next payment due on {server['next_payment_due']}."
        )
    else:
        # Freeze the server if insufficient funds
        server["status"] = "Заморожен"
        server["freeze_reason"] = "Недостаточно средств."
        server["freeze_date"] = datetime.now().isoformat()
        app.logger.info(
            f"Server '{server['name']}' for user {user['id']} frozen due to insufficient funds."
        )
        # Notify the user via email or another notification method
        # Example:
        # send_notification(user['email'], f"Your server '{server['name']}' has been frozen due to insufficient funds.")

def initialize_scheduler():
    scheduler = BackgroundScheduler()
    scheduler.add_job(func=monthly_billing_check, trigger="interval", days=30)
    scheduler.add_job(func=check_frozen_servers, trigger="interval", hours=24)
    scheduler.start()
    app.logger.info("Scheduler started for billing and frozen server checks.")

def check_frozen_servers():
    app.logger.info("Checking for frozen servers...")
    try:
        db = load_db()
        for username, user in db['users'].items():
            servers_to_remove = []
            for server in user.get('servers', []):
                if server["status"] == "Заморожен":
                    freeze_date = datetime.fromisoformat(server["freeze_date"])
                    if datetime.now() > freeze_date + timedelta(days=3):
                        # Delete the server
                        execute_docker_command(server["id"], "kill")
                        execute_docker_command(server["id"], "rm")
                        servers_to_remove.append(server)
                        app.logger.info(
                            f"Deleting server '{server['name']}' for user {username} after 3 days of being frozen."
                        )
                        # Notify the user
                        # Example:
                        # send_notification(user['email'], f"Your server '{server['name']}' has been deleted after being frozen for over 3 days.")
            for server in servers_to_remove:
                user['servers'].remove(server)
        save_db(db)
    except Exception as e:
        app.logger.error(f"Error during frozen server check: {e}")
        app.logger.error(traceback.format_exc())

@app.route('/api/tickets/<int:ticket_id>', methods=['GET'])
@jwt_required()
def get_ticket(ticket_id):
    current_user = get_jwt_identity()
    db = load_db()
    user_data = db['users'].get(current_user)

    if not user_data:
        return jsonify({'error': 'User not found'}), 404

    ticket = next((t for t in user_data['tickets'] if t['id'] == ticket_id), None)
    if not ticket:
        return jsonify({'error': 'Ticket not found'}), 404

    return jsonify(ticket), 200

@app.route('/api/tickets/<int:ticket_id>/reply', methods=['POST'])
@jwt_required()
def reply_ticket(ticket_id):
    current_user = get_jwt_identity()
    db = load_db()
    
    user_data = db['users'].get(current_user)
    if not user_data:
        return jsonify({'error': 'User not found'}), 404
        
    ticket = next((t for t in user_data['tickets'] if t['id'] == ticket_id), None)
    if not ticket:
        return jsonify({'error': 'Ticket not found'}), 404
        
    data = request.get_json()
    if not data or 'message' not in data:
        return jsonify({'error': 'Message is required'}), 400
        
    message = {
        'content': data['message'],
        'sender': current_user,
        'timestamp': datetime.now().isoformat()
    }
    
    ticket['messages'].append(message)
    save_db(db)
    log_activity(current_user, f'reply_ticket_{ticket_id}', request.headers.get('User-Agent', ''))
    return jsonify(message), 201

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



<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Управление серверами</title>
    <link rel="stylesheet" href="/static/styles.css">
</head>
<body>
    <div class="container">
        <!-- Auth forms container -->
        <div id="auth-container">
            <!-- Форма входа -->
            <div id="login-form" class="form-container">
                <h2><p align="center">Вход в систему</p></h2>
                <form id="login">
                    <input type="text" id="login-username" placeholder="Имя пользователя" required>
                    <input type="password" id="login-password" placeholder="Пароль" required>
                    <button type="submit" class="btn primary">Войти</button>
                    <p>Нет аккаунта? <a href="#" id="show-register">Зарегистрироваться</a></p>
                </form>
            </div>

            <!-- Форма регистрации -->
            <div id="register-form" class="form-container hidden">
                <h2><p align="center">Регистрация</p></h2>
                <form id="register">
                    <input type="text" id="reg-username" placeholder="Имя пользователя" required>
                    <input type="password" id="reg-password" placeholder="Пароль" required>
                    <button type="submit" class="btn primary">Зарегистрироваться</button>
                    <p>Уже есть аккаунт? <a href="#" id="show-login">Войти</a></p>
                </form>
            </div>
        </div>

        <!-- User panel container -->
        <div id="main-content" class="hidden">
            <header>
                <div id="user-info">
                    <span id="username"></span>
                    <span id="balance"></span>
                </div>
                <button id="settings-btn" class="btn secondary">Настройки</button>
                <button id="logout-btn" class="btn primary">Выйти</button>
            </header>

            <nav>
                <button class="tab-btn active" data-tab="servers">Серверы</button>
                <button class="tab-btn" data-tab="shop">Магазин</button>
                <button class="tab-btn" data-tab="support">Поддержка</button>
            </nav>

            <!-- Секция серверов -->
            <section id="servers" class="tab-content">
                <h2>Мои серверы</h2>
                <div id="servers-list" class="grid"></div>
            </section>

            <section id="shop" class="tab-content hidden">
                <h2>Магазин серверов</h2>
                <div class="plans-grid">
                    <div class="plan-card">
                        <h3>Базовый</h3>
                        <p>2 CPU / 4 GB RAM</p>
                        <p class="price">500 ₽/мес</p>
                        <button class="btn primary buy-server" data-plan="basic">Купить</button>
                    </div>
                    <div class="plan-card">
                        <h3>Продвинутый</h3>
                        <p>4 CPU / 8 GB RAM</p>
                        <p class="price">1000 ₽/мес</p>
                        <button class="btn primary buy-server" data-plan="advanced">Купить</button>
                    </div>
                    <div class="plan-card">
                        <h3>Премиум</h3>
                           <p>8 CPU / 16 GB RAM</p>
                        <p class="price">2000 ₽/мес</p>
                        <button class="btn primary buy-server" data-plan="premium">Купить</button>
                    </div>
                </div>
            </section>
            
            <!-- Секция поддержки -->
            <section id="support" class="tab-content hidden">
                <h2>Поддержка</h2>
                <div class="support-container">
                    <form id="ticket-form">
                        <input type="text" id="ticket-title" placeholder="Тема" required>
                        <textarea id="ticket-description" placeholder="Описание проблемы" required></textarea>
                        <button type="submit" class="btn primary">Отправить</button>
                    </form>
                    <div id="tickets-list"></div>
                </div>
            </section>
        </div>
    </div>

    <!-- Notification Container -->
    <div id="notification-container"></div>

    <!-- Ticket Detail Modal -->
    <div id="ticket-modal" class="modal hidden">
        <div class="modal-content">
            <div class="modal-header">
                <h3 id="modal-ticket-title">Тема Тикета</h3>
                <button class="close-modal" id="close-ticket-modal">&times;</button>
            </div>
            <div class="modal-body">
                <p id="modal-ticket-description">Описание</p>
                <div id="modal-ticket-messages"></div>
                <form id="reply-form">
                    <textarea id="reply-message" placeholder="Ваш ответ" required></textarea>
                    <button type="submit" class="btn primary">Отправить ответ</button>
                </form>
            </div>
        </div>
    </div>

    <!-- Settings Modal -->
    <div id="settings-modal" class="modal hidden">
        <div class="modal-content">
            <div class="modal-header">
                <h3>Настройки</h3>
                <button class="close-modal" id="close-settings-modal">&times;</button>
            </div>
            <div class="modal-body">
                <form id="settings-form">
                    <div class="form-group">
                        <label for="new-username">Новое имя пользователя</label>
                        <input type="text" id="new-username" placeholder="Имя пользователя">
                    </div>
                    <div class="form-group">
                        <label for="current-password">Текущий пароль</label>
                        <input type="password" id="current-password" placeholder="Текущий пароль" required>
                    </div>
                    <div class="form-group">
                        <label for="new-password">Новый пароль</label>
                        <input type="password" id="new-password" placeholder="Новый пароль">
                    </div>
                    <button type="submit" class="btn primary">Сохранить</button>
                </form>
            </div>
        </div>
    </div>

    <!-- Buy Server Modal -->
    <div id="buy-server-modal" class="modal hidden">
        <div class="modal-content">
            <div class="modal-header">
                <h3>Создать сервер</h3>
                <button class="close-modal" id="close-buy-server-modal">&times;</button>
            </div>
            <div class="modal-body">
                <form id="buy-server-form">
                    <div class="form-group">
                        <label for="server-name">Имя сервера</label>
                        <input type="text" id="server-name" placeholder="Введите имя сервера" required>
                    </div>
                    <div class="form-group">
                        <label for="server-os">Операционная система</label>
                        <select id="server-os" required>
                            <option value="" disabled selected>Выберите ОС</option>
                            <option value="win2020">Windows Server 2020</option>
                            <option value="win2022">Windows Server 2022</option>
                            <option value="win2025">Windows Server 2025</option>
                            <option value="win10">Windows 10</option>
                            <option value="win11">Windows 11</option>
                        </select>
                    </div>
                    <button type="submit" class="btn primary">Купить сервер</button>
                </form>
            </div>
        </div>
    </div>

    <!-- Server Details Modal -->
    <div id="server-details-modal" class="modal hidden">
        <div class="modal-content">
            <div class="modal-header">
                <h3 id="modal-server-name">Имя Сервера</h3>
                <button class="close-modal" id="close-server-details-modal">&times;</button>
            </div>
            <div class="modal-body">
                <p><strong>IP адрес:</strong> <span id="modal-server-ip">192.168.1.1</span></p>
                <p><strong>План:</strong> <span id="modal-server-plan">Базовый</span></p>
                <p><strong>ОС:</strong> <span id="modal-server-os">Windows Server 2022</span></p>
                <p><strong>Статус:</strong> <span id="modal-server-status">Active</span></p>
                <p><strong>Пароль:</strong> <span id="modal-server-password" class="hidden-password">************</span>
                    <button class="btn secondary" id="toggle-password-btn">Показать пароль</button>
                </p>
            </div>
        </div>
    </div>

    <script src="/static/script.js"></script>
</body>
</html>



// -------------- Global Variables --------------
let currentUser = null;
const API_URL = '/api';

// ----------------- Plan Configs -----------------
// Updated to include the 'key' matching the backend's SERVER_PLANS
const planConfigs = {
    basic: { key: 'basic', name: 'Базовый', cpu: 2, ram: 4, disk: '50GB', price: 500 },
    advanced: { key: 'advanced', name: 'Продвинутый', cpu: 4, ram: 8, disk: '100GB', price: 1000 },
    premium: { key: 'premium', name: 'Премиум', cpu: 8, ram: 16, disk: '200GB', price: 2000 }
};

// ----------------- Utility Functions -----------------
const showElement = (id) => document.getElementById(id).classList.remove('hidden');
const hideElement = (id) => document.getElementById(id).classList.add('hidden');
const setActiveTab = (tabId) => {
    document.querySelectorAll('.tab-content').forEach(tab => tab.classList.add('hidden'));
    document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
    document.getElementById(tabId).classList.remove('hidden');
    document.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
};

const validatePlan = (planKey) => {
    const validPlans = ['basic', 'advanced', 'premium'];
    if (!validPlans.includes(planKey)) {
        throw new Error('Неверный план подписки');
    }
    return planConfigs[planKey];
};

const getAuthHeaders = () => {
    const token = localStorage.getItem('authToken');
    return {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
    };
};

function showNotification(message, type = 'error') {
    const container = document.getElementById('notification-container');
    const notification = document.createElement('div');
    notification.className = `notification ${type}`;
    notification.innerHTML = `
        <span>${message}</span>
        <button class="close-notification">&times;</button>
    `;
    container.appendChild(notification);

    // Show the notification
    setTimeout(() => {
        notification.classList.add('show');
    }, 100);

    // Remove the notification after 5 seconds
    setTimeout(() => {
        notification.classList.remove('show');
        setTimeout(() => {
            notification.remove();
        }, 500);
    }, 5000);

    // Close button functionality
    notification.querySelector('.close-notification').addEventListener('click', () => {
        notification.classList.remove('show');
        setTimeout(() => {
            notification.remove();
        }, 500);
    });
}

function logout() {
    localStorage.removeItem('authToken');
    localStorage.removeItem('username');
    localStorage.removeItem('balance');
    currentUser = null;
    updateUserInterface();
    location.reload(); // Reload the page after logout
}

// ----------------- Registration Handler -----------------
document.getElementById('register').addEventListener('submit', async (e) => {
    e.preventDefault();
    const username = document.getElementById('reg-username').value.trim();
    const password = document.getElementById('reg-password').value.trim();

    if (!username || !password) {
        showNotification('Пожалуйста, заполните все поля', 'error');
        return;
    }

    try {
        const response = await fetch(`${API_URL}/register`, {
            method: 'POST',
            headers: { 
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({ username, password })
        });

        const data = await response.json();

        if (response.ok) {
            showNotification('Регистрация успешна! Теперь вы можете войти.', 'success');
            showElement('login-form');
            hideElement('register-form');
        } else {
            showNotification(data.error || 'Ошибка регистрации', 'error');
        }
    } catch (error) {
        console.error('Ошибка регистрации:', error);
        showNotification('Ошибка при регистрации', 'error');
    }
});

// ----------------- Login Handler -----------------
document.getElementById('login').addEventListener('submit', async (e) => {
    e.preventDefault();
    const username = document.getElementById('login-username').value.trim();
    const password = document.getElementById('login-password').value.trim();

    if (!username || !password) {
        showNotification('Пожалуйста, заполните все поля', 'error');
        return;
    }

    try {
        const response = await fetch(`${API_URL}/login`, {
            method: 'POST',
            headers: { 
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'X-Requested-With': 'XMLHttpRequest' // Add this header
            },
            body: JSON.stringify({ username, password })
        });

        if (!response.ok) {
            const data = await response.json();
            throw new Error(data.error || `HTTP error! status: ${response.status}`);
        }

        const data = await response.json();

        if (data.token) {
            // Store the token and user data in localStorage
            localStorage.setItem('authToken', data.token);
            localStorage.setItem('username', data.username);
            localStorage.setItem('balance', data.balance); // Assuming balance is a number
            currentUser = {
                username: data.username,
                balance: data.balance
            };
            updateUserInterface();
            setActiveTab('servers');
            showNotification('Вход выполнен успешно!', 'success');
        } else {
            showNotification('Неверные учетные данные', 'error');
        }
    } catch (error) {
        console.error('Ошибка входа:', error);
        showNotification(`Ошибка при входе в систему: ${error.message}`, 'error');
    }
});

// ----------------- Authentication Check -----------------
function isAuthenticated() {
    return localStorage.getItem('authToken') !== null;
}

// ----------------- Manage Servers -----------------
async function loadServers() {
    try {
        const response = await fetch(`${API_URL}/servers`, {
            method: 'GET',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        const servers = await response.json();
        displayServers(servers);
    } catch (error) {
        console.error('Ошибка загрузки серверов:', error);
        showNotification(error.message, 'error');
    }
}

function displayServers(servers) {
    const serversList = document.getElementById('servers-list');
    serversList.innerHTML = '';

    if (!Array.isArray(servers) || servers.length === 0) {
        serversList.innerHTML = '<p>У вас нет серверов.</p>';
        return;
    }

    servers.forEach(server => {
        const serverCard = document.createElement('div');
        serverCard.className = 'server-card';
        serverCard.innerHTML = `
            <h3>${server.name}</h3>
            <span class="server-status status-${server.status}">${server.status}</span>
            <p><strong>IP:</strong> ${server.ip}</p>
            <p>План: ${server.plan.name}</p>
            <p>CPU: ${server.plan.cpu} / RAM: ${server.plan.ram}GB</p>
            <button class="btn primary view-server" data-server-id="${server.id}">Просмотреть</button>
            <button class="btn secondary toggle-server" data-server-id="${server.id}">${server.status === 'active' ? 'Остановить' : 'Запустить'}</button>
            <button class="btn danger delete-server" data-server-id="${server.id}">Удалить</button>
        `;
        serversList.appendChild(serverCard);
    });

    // Attach event listeners to view server buttons
    document.querySelectorAll('.view-server').forEach(button => {
        button.addEventListener('click', () => {
            const serverId = button.getAttribute('data-server-id');
            openServerDetailsModal(serverId);
        });
    });

    // Attach event listeners to toggle server buttons
    document.querySelectorAll('.toggle-server').forEach(button => {
        button.addEventListener('click', () => {
            const serverId = button.getAttribute('data-server-id');
            const currentStatus = button.textContent.includes('Остановить') ? 'active' : 'frozen';
            toggleServer(serverId, currentStatus);
        });
    });

    // Attach event listeners to delete server buttons
    document.querySelectorAll('.delete-server').forEach(button => {
        button.addEventListener('click', () => {
            const serverId = button.getAttribute('data-server-id');
            deleteServer(serverId);
        });
    });
}

// Function to toggle server status
async function toggleServer(serverId, currentStatus) {
    try {
        const response = await fetch(`${API_URL}/servers/${serverId}/toggle`, {
            method: 'POST',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        await loadServers();
        showNotification(`Сервер успешно ${currentStatus === 'active' ? 'остановлен' : 'запущен'}!`, 'success');
    } catch (error) {
        console.error('Ошибка при переключении сервера:', error);
        showNotification(error.message, 'error');
    }
}

// Function to delete a server
async function deleteServer(serverId) {
    if (!confirm('Вы уверены, что хотите удалить этот сервер?')) return;

    try {
        const response = await fetch(`${API_URL}/servers/${serverId}`, {
            method: 'DELETE',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        await loadServers();
        showNotification('Сервер успешно удален!', 'success');
    } catch (error) {
        console.error('Ошибка при удалении сервера:', error);
        showNotification(error.message, 'error');
    }
}

// ----------------- Manage Tickets -----------------
document.getElementById('ticket-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const title = document.getElementById('ticket-title').value.trim();
    const description = document.getElementById('ticket-description').value.trim();

    if (!title || !description) {
        showNotification('Пожалуйста, заполните все поля', 'error');
        return;
    }

    try {
        const response = await fetch(`${API_URL}/tickets`, {
            method: 'POST',
            headers: getAuthHeaders(),
            body: JSON.stringify({
                title,
                description
            })
        });

        if (!response.ok) {
            const data = await response.json();
            throw new Error(data.error || 'Ошибка при создании тикета');
        }

        showNotification('Тикет успешно создан!', 'success');
        document.getElementById('ticket-form').reset();
        loadTickets();
    } catch (error) {
        console.error('Ошибка создания тикета:', error);
        showNotification(error.message, 'error');
    }
});

async function loadTickets() {
    try {
        const response = await fetch(`${API_URL}/tickets`, {
            method: 'GET',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        const tickets = await response.json();
        displayTickets(tickets);
    } catch (error) {
        console.error('Ошибка загрузки тикетов:', error);
        showNotification(error.message, 'error');
    }
}

function displayTickets(tickets) {
    const ticketsList = document.getElementById('tickets-list');
    ticketsList.innerHTML = '';

    if (!Array.isArray(tickets) || tickets.length === 0) {
        ticketsList.innerHTML = '<p>У вас нет тикетов.</p>';
        return;
    }

    tickets.forEach(ticket => {
        const ticketCard = document.createElement('div');
        ticketCard.className = 'ticket-card';
        ticketCard.innerHTML = `
            <div class="ticket-header">
                <h3>${ticket.title}</h3>
                <span class="ticket-status status-${ticket.status}">${ticket.status}</span>
            </div>
            <p>${ticket.description}</p>
            <small>Создан: ${new Date(ticket.created_at).toLocaleString()}</small>
            <button class="btn primary view-ticket" data-ticket-id="${ticket.id}">Просмотреть</button>
        `;
        ticketsList.appendChild(ticketCard);
    });

    // Attach event listeners to view ticket buttons
    document.querySelectorAll('.view-ticket').forEach(button => {
        button.addEventListener('click', () => {
            const ticketId = button.getAttribute('data-ticket-id');
            openTicketModal(ticketId);
        });
    });
}

async function openTicketModal(ticketId) {
    try {
        const response = await fetch(`${API_URL}/tickets/${ticketId}`, {
            method: 'GET',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        const ticket = await response.json();
        displayTicketDetails(ticket);
    } catch (error) {
        console.error('Ошибка загрузки тикета:', error);
        showNotification(error.message, 'error');
    }
}

function displayTicketDetails(ticket) {
    // Populate modal fields
    document.getElementById('modal-ticket-title').textContent = ticket.title;
    document.getElementById('modal-ticket-description').textContent = ticket.description;

    const messagesContainer = document.getElementById('modal-ticket-messages');
    messagesContainer.innerHTML = '';

    if (!Array.isArray(ticket.messages) || ticket.messages.length === 0) {
        messagesContainer.innerHTML = '<p>Нет сообщений.</p>';
    } else {
        ticket.messages.forEach(msg => {
            const msgDiv = document.createElement('div');
            msgDiv.className = 'ticket-message';
            msgDiv.innerHTML = `
                <p><span class="sender">${msg.sender}</span> <span class="timestamp">${new Date(msg.timestamp).toLocaleString()}</span></p>
                <p>${msg.content}</p>
            `;
            messagesContainer.appendChild(msgDiv);
        });
    }

    // Show modal
    showElement('ticket-modal');
}

// ----------------- Reply to Ticket -----------------
document.getElementById('reply-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const message = document.getElementById('reply-message').value.trim();

    // Identify which ticket you're replying to
    const ticketModal = document.getElementById('ticket-modal');
    const ticketId = ticketModal.getAttribute('data-ticket-id');

    if (!message) {
        showNotification('Пожалуйста, введите сообщение', 'error');
        return;
    }

    if (!ticketId) {
        showNotification('Не удалось определить тикет для ответа', 'error');
        return;
    }

    try {
        const response = await fetch(`${API_URL}/tickets/${ticketId}/reply`, {
            method: 'POST',
            headers: getAuthHeaders(),
            body: JSON.stringify({ message })
        });

        if (!response.ok) {
            const data = await response.json();
            throw new Error(data.error || 'Ошибка при отправке ответа');
        }

        // Reload ticket details
        openTicketModal(ticketId);
        showNotification('Ответ отправлен успешно!', 'success');
    } catch (error) {
        console.error('Ошибка отправки ответа:', error);
        showNotification(error.message, 'error');
    }
});

// ----------------- Close Modals -----------------
document.getElementById('close-ticket-modal').addEventListener('click', () => {
    hideElement('ticket-modal');
    document.getElementById('reply-form').reset();
});

document.getElementById('close-settings-modal').addEventListener('click', () => {
    hideElement('settings-modal');
    document.getElementById('settings-form').reset();
});

document.getElementById('close-buy-server-modal').addEventListener('click', () => {
    hideElement('buy-server-modal');
    document.getElementById('buy-server-form').reset();
});

// ----------------- Settings Modal Functionality -----------------
document.getElementById('settings-btn').addEventListener('click', () => {
    showElement('settings-modal');
});

document.getElementById('settings-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const newUsername = document.getElementById('new-username').value.trim();
    const currentPassword = document.getElementById('current-password').value.trim();
    const newPassword = document.getElementById('new-password').value.trim();

    if (!currentPassword) {
        showNotification('Текущий пароль обязателен', 'error');
        return;
    }

    if (!newUsername && !newPassword) {
        showNotification('Пожалуйста, введите новое имя пользователя или пароль', 'error');
        return;
    }

    const payload = {
        current_password: currentPassword
    };

    if (newUsername) payload.new_username = newUsername;
    if (newPassword) payload.new_password = newPassword;

    try {
        const response = await fetch(`${API_URL}/userinfo`, {
            method: 'PUT',
            headers: getAuthHeaders(),
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            const data = await response.json();
            throw new Error(data.error || `HTTP error! status: ${response.status}`);
        }

        const data = await response.json();

        // Update local storage and UI
        if (data.username) {
            localStorage.setItem('username', data.username);
            currentUser.username = data.username;
            document.getElementById('username').textContent = `Пользователь: ${currentUser.username}`;
        }

        if (data.balance !== undefined) {
            localStorage.setItem('balance', data.balance);
            currentUser.balance = data.balance;
            document.getElementById('balance').textContent = `Баланс: ${currentUser.balance} ₽`;
        }

        hideElement('settings-modal');
        document.getElementById('settings-form').reset();
        showNotification('Настройки успешно обновлены!', 'success');
    } catch (error) {
        console.error('Ошибка обновления настроек:', error);
        showNotification(error.message, 'error');
    }
});

// ----------------- Buy Server Modal Functionality -----------------
document.querySelectorAll('.buy-server').forEach(btn => {
    btn.addEventListener('click', () => {
        // Remove 'active' class from all buttons
        document.querySelectorAll('.buy-server').forEach(b => b.classList.remove('active'));
        // Add 'active' class to the clicked button
        btn.classList.add('active');

        showElement('buy-server-modal');
    });
});

// Handle Buy Server Form Submission
document.getElementById('buy-server-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const serverName = document.getElementById('server-name').value.trim();
    const serverOS = document.getElementById('server-os').value;

    // Determine which plan is selected based on the active buy-server button
    const selectedPlanKey = document.querySelector('.buy-server.active')?.dataset.plan;
    const planDetails = planConfigs[selectedPlanKey];

    if (!serverName || !serverOS || !planDetails) {
        showNotification('Пожалуйста, заполните все поля и выберите план', 'error');
        return;
    }

    try {
        const response = await fetch(`${API_URL}/servers`, {
            method: 'POST',
            headers: getAuthHeaders(),
            body: JSON.stringify({
                name: serverName,
                plan: planDetails.key, // Use the key here
                os: serverOS
            })
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || 'Ошибка при создании сервера');
        }

        const newServer = await response.json();
        showNotification('Сервер успешно создан!', 'success');
        hideElement('buy-server-modal');
        document.getElementById('buy-server-form').reset();
        loadServers();

        // Update the user balance
        currentUser.balance -= planDetails.price;
        localStorage.setItem('balance', currentUser.balance); // Update local storage
        document.getElementById('balance').textContent = `Баланс: ${currentUser.balance} ₽`;
    } catch (error) {
        console.error('Ошибка покупки сервера:', error);
        showNotification(error.message, 'error');
    }
});

// ----------------- Server Details Modal Functionality -----------------
async function openServerDetailsModal(serverId) {
    try {
        const response = await fetch(`${API_URL}/servers/${serverId}`, {
            method: 'GET',
            headers: getAuthHeaders()
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
        }

        const server = await response.json();
        displayServerDetails(server);
    } catch (error) {
        console.error('Ошибка загрузки сервера:', error);
        showNotification(error.message, 'error');
    }
}

function displayServerDetails(server) {
    // Populate modal fields
    document.getElementById('modal-server-name').textContent = server.name;
    document.getElementById('modal-server-ip').textContent = server.ip;
    document.getElementById('modal-server-plan').textContent = server.plan.name;
    document.getElementById('modal-server-os').textContent = server.os;
    document.getElementById('modal-server-status').textContent = server.status;

    const passwordElement = document.getElementById('modal-server-password');
    passwordElement.textContent = server.password;
    passwordElement.classList.add('hidden-password');

    const togglePasswordBtn = document.getElementById('toggle-password-btn');
    togglePasswordBtn.textContent = 'Показать пароль';

    togglePasswordBtn.onclick = () => {
        if (passwordElement.classList.contains('hidden-password')) {
            passwordElement.classList.remove('hidden-password');
            togglePasswordBtn.textContent = 'Скрыть пароль';
        } else {
            passwordElement.classList.add('hidden-password');
            togglePasswordBtn.textContent = 'Показать пароль';
        }
    };

    // Store the serverId in the modal if needed for further actions
    const serverDetailsModal = document.getElementById('server-details-modal');
    serverDetailsModal.setAttribute('data-server-id', server.id);

    // Show modal
    showElement('server-details-modal');
}

// Close Server Details Modal
document.getElementById('close-server-details-modal').addEventListener('click', () => {
    hideElement('server-details-modal');
});

// ----------------- User Info Management -----------------
function updateUserInterface() {
    const authContainer = document.getElementById('auth-container');
    const mainContent = document.getElementById('main-content');
    const usernameElement = document.getElementById('username');
    const balanceElement = document.getElementById('balance');

    if (isAuthenticated()) {
        if (!currentUser) {
            const username = localStorage.getItem('username');
            const balance = parseFloat(localStorage.getItem('balance'));

            if (username && !isNaN(balance)) {
                currentUser = {
                    username: username,
                    balance: balance
                };
                finalizeUserInterface();
            } else {
                // Fetch user info from backend
                fetch(`${API_URL}/userinfo`, {
                    method: 'GET',
                    headers: getAuthHeaders()
                })
                .then(response => {
                    if (!response.ok) {
                        throw new Error('Failed to fetch user info');
                    }
                    return response.json();
                })
                .then(data => {
                    currentUser = {
                        username: data.username,
                        balance: data.balance
                    };
                    // Store these details in localStorage
                    localStorage.setItem('username', data.username);
                    localStorage.setItem('balance', data.balance);
                    // Update UI
                    finalizeUserInterface();
                })
                .catch(error => {
                    console.error('Failed to fetch user info:', error);
                    logout(); // Optionally, log out the user if fetching fails
                });
            }
        } else {
            finalizeUserInterface();
        }
    } else {
        // Show auth container and hide main content
        authContainer?.classList.remove('hidden');
        mainContent?.classList.add('hidden');
    }
}

function finalizeUserInterface() {
    const authContainer = document.getElementById('auth-container');
    const mainContent = document.getElementById('main-content');
    const usernameElement = document.getElementById('username');
    const balanceElement = document.getElementById('balance');

    authContainer?.classList.add('hidden');
    mainContent?.classList.remove('hidden');

    // Update user information in the header
    if (usernameElement) usernameElement.textContent = `Пользователь: ${currentUser.username}`;
    if (balanceElement) balanceElement.textContent = `Баланс: ${currentUser.balance} ₽`;

    // Load user data
    loadServers();
    loadTickets();
}

// ----------------- Form Toggle Handlers -----------------
document.getElementById('show-register').addEventListener('click', (e) => {
    e.preventDefault();
    hideElement('login-form');
    showElement('register-form');
});

document.getElementById('show-login').addEventListener('click', (e) => {
    e.preventDefault();
    hideElement('register-form');
    showElement('login-form');
});

// ----------------- Tab Navigation Handlers -----------------
document.querySelectorAll('.tab-btn').forEach(btn => {
    btn.addEventListener('click', () => setActiveTab(btn.dataset.tab));
});

// ----------------- Initialization on Page Load -----------------
document.addEventListener('DOMContentLoaded', function() {
    const logoutBtn = document.getElementById('logout-btn');
    if (logoutBtn) {
        logoutBtn.addEventListener('click', logout);
    }

    updateUserInterface();
});



/* Основные стили */
:root {
    --primary-color: #941b31;
    --secondary-color: #bbb;
    --success-color: #46d369;
    --danger-color: #f53b57;
    --light-gray: #0a0909; /* Dark background color */
    --dark-gray: #eaeaea; /* Light text color */
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    line-height: 1.8;
    color: var(--dark-gray);
    background-color: var(--light-gray);
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

/* Заголовок */
header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
    padding: 20px;
    background-color: #1e1e1e;
    color: var(--dark-gray);
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

#user-info {
    display: flex;
    align-items: center;
    gap: 20px;
}

/* Формы */
.form-container {
    width: 800px;
    height: 370px;
    margin: 290px auto;
    padding: 70px;
    background-image: url("https://api15.iloveimg.com/v1/download/1thxyjmgs31rqAnbAm61gfgd0wp969xy0kw96mzb811s717rAs2k5089zhk5Asj0svvjhdbgvwrrnts9plw9rx99xf1qpbmlqp7zdwdzq3sbp9j384lxjcfzkwvp87yg1n0z2AcnAxls24w5mz5pqnjcp6wwt298jbvjqjcq0msmncz7pxp1");
    color: var(--dark-gray);
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

form {
    display: flex;
    flex-direction: column;
    gap: 15px;
}

input,
textarea {
    padding: 10px;
    background-color: #333;
    border: 2px solid #5c5c5c;
    border-radius: 12px;
    color: var(--dark-gray);
    font-size: 16px;
}

textarea {
    min-height: 100px;
    resize: vertical;
}

/* Кнопки */
.btn {
    padding: 10px 20px;
    border: none;
    border-radius: 9px;
    cursor: pointer;
    font-size: 16px;
    transition: background-color 0.3s;
}

.btn.primary {
    background-color: var(--primary-color);
    color: white;
}

.btn.primary:hover {
    background-color: #b51844;
}

/* Навигация */
nav {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.tab-btn {
    padding: 10px 20px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 16px;
    color: var(--secondary-color);
    border-bottom: 2px solid transparent;
}

.tab-btn.active {
    color: var(--primary-color);
    border-bottom-color: var(--primary-color);
}

/* Сетка серверов */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
    margin-top: 20px;
}

.server-card {
    background-color: #1e1e1e;
    color: var(--dark-gray);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

.server-card h3 {
    margin-bottom: 10px;
}

.server-password {
    background-color: #333;
    padding: 8px;
    border-radius: 4px;
    margin-top: 10px;
    cursor: pointer;
}

.server-password.hidden-password {
    color: transparent;
    text-shadow: 0 0 8px rgba(255,255,255,0.5);
}

.server-status {
    display: inline-block;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 14px;
    margin-bottom: 10px;
    color: var(--dark-gray);
}

.status-active {
    background-color: var(--success-color);
}

.status-frozen {
    background-color: var(--danger-color);
}

/* Планы в магазине */
.plans-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 20px;
    margin-top: 20px;
}

.plan-card {
    background-color: #1e1e1e;
    color: var(--dark-gray);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
    text-align: center;
}

.plan-card h3 {
    margin-bottom: 10px;
}

.price {
    font-size: 24px;
    font-weight: bold;
    color: var(--primary-color);
    margin: 15px 0;
}

/* Поддержка */
.support-container {
    max-width: 800px;
    margin: 0 auto;
}

.ticket-card {
    background-color: #1e1e1e;
    color: var(--dark-gray);
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
    margin-bottom: 15px;
}

.ticket-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 10px;
}

.ticket-status {
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 14px;
    color: var(--dark-gray);
}

/* Notification */
.notification {
    position: fixed;
    top: 20px;
    right: 20px;
    padding: 15px 25px;
    background-color: #1e1e1e;
    color: var(--dark-gray);
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 10px;
    opacity: 0;
    transition: opacity 0.5s ease-in-out;
}

.notification.show {
    opacity: 1;
}

.notification.success {
    background-color: var(--success-color);
    color: white;
}

.notification.error {
    background-color: var(--danger-color);
    color: white;
}

.notification .close-notification {
    background: none;
    border: none;
    color: white;
    font-size: 20px;
    cursor: pointer;
}

/* Modal Styles */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1001;
}

.modal.hidden {
    display: none;
}

.modal-content {
    background-color: #1e1e1e;
    color: var(--dark-gray);
    padding: 20px;
    border-radius: 8px;
    width: 90%;
    max-width: 600px;
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}

.close-modal {
    background: none;
    border: none;
    font-size: 24px;
    cursor: pointer;
    color: var(--secondary-color);
}

.modal-body {
    max-height: 70vh;
    overflow-y: auto;
}

#modal-ticket-messages, #modal-server-messages {
    margin-top: 20px;
    border-top: 1px solid var(--secondary-color);
    padding-top: 10px;
}

.ticket-message {
    margin-bottom: 10px;
}

.ticket-message .sender {
    font-weight: bold;
}

.ticket-message .timestamp {
    font-size: 12px;
    color: var(--secondary-color);
}

.server-password {
    background-color: #333;
    padding: 8px;
    border-radius: 4px;
    margin-top: 10px;
    cursor: pointer;
}

.hidden-password {
    color: transparent;
    text-shadow: 0 0 8px rgba(255,255,255,0.5);
}

.show-password {
    color: var(--dark-gray);
    text-shadow: none;
}

/* Settings Button Style */
.btn.secondary {
    background-color: var(--secondary-color);
    color: var(--light-gray);
}

.btn.secondary:hover {
    background-color: #999;
}

/* Buy Server Modal Specific Styles */
#buy-server-form .form-group {
    margin-bottom: 15px;
}

#buy-server-form .form-group label {
    display: block;
    margin-bottom: 5px;
}

#buy-server-form .form-group input,
#buy-server-form .form-group select {
    width: 100%;
    padding: 8px;
    border-radius: 4px;
    background-color: #333;
    border: 1px solid #444;
    color: var(--dark-gray);
}

/* Server Details Modal Specific Styles */
#server-details-modal p {
    margin-bottom: 10px;
}

#toggle-password-btn {
    margin-left: 10px;
    padding: 5px 10px;
    font-size: 14px;
}

/* Animation */
@keyframes slideIn {
    from {
        transform: translateX(120%);
    }
    to {
        transform: translateX(0);
    }
}

@keyframes slideOut {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(120%);
    }
}

.notification-slide-in {
    animation: slideIn 0.3s ease-in-out forwards;
}

.notification-slide-out {
    animation: slideOut 0.3s ease-in-out forwards;
}

.status-open {
    background-color: var(--primary-color);
}

.status-closed {
    background-color: var(--secondary-color);
}

/* Утилиты */
.hidden {
    display: none !important;
}

/* Адаптивность */
@media (max-width: 768px) {
    .container {
        padding: 10px;
    }

    header {
        flex-direction: column;
        text-align: center;
        gap: 15px;
    }

    .grid,
    .plans-grid {
        grid-template-columns: 1fr;
    }

    nav {
        flex-wrap: wrap;
    }

    .tab-btn {
        flex: 1;
        min-width: 120px;
    }
}

.error-message {
    position: fixed;
    top: 20px;
    right: 20px;
    background-color: #ff4444;
    color: white;
    padding: 15px 20px;
    border-radius: 8px;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 10px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    animation: slideIn 0.3s ease-in-out;
}

.close-error {
    background: none;
    border: none;
    color: white;
    font-size: 20px;
    cursor: pointer;
    padding: 0 5px;
}

.password-container {
    position: relative;
    display: flex;
    align-items: center;
}

.password-toggle {
    position: absolute;
    right: 10px;
    background: none;
    border: none;
    color: var(--secondary-color);
    cursor: pointer;
}

.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.modal-content {
    background-color: #1e1e1e;
    padding: 20px;
    border-radius: 8px;
    width: 90%;
    max-width: 500px;
}

.form-group {
    margin-bottom: 15px;
}

.form-group label {
    display: block;
    margin-bottom: 5px;
}

.form-group select,
.form-group input {
    width: 100%;
    padding: 8px;
    border-radius: 4px;
    background-color: #333;
    border: 1px solid #444;
    color: var(--dark-gray);
}


/* Анимации */
@keyframes fadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.fade-in {
    animation: fadeIn 0.3s ease-in;
}

/* Модальные окна */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1000;
}

.modal-content {
    background-color: #1e1e1e;
    color: var(--dark-gray);
    padding: 20px;
    border-radius: 8px;
    max-width: 500px;
    width: 90%;
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}

.close-modal {
    background: none;
    border: none;
    font-size: 24px;
    cursor: pointer;
    color: var(--secondary-color);
}



{
  "users": {
    "test": {
      "password": "test",
      "servers": [
        {
          "id": 1,
          "name": "Server-1730443463374",
          "plan": {
            "name": "\u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439",
            "cpu": 4,
            "ram": 8,
            "price": 1000
          },
          "status": "active",
          "password": "SVBes7qlNc40",
          "created_at": "2024-11-01T13:44:23.380705",
          "auto_renewal": true,
          "container_name": "server_47387a38"
        }
      ],
      "balance": 34765734657833657,
      "tickets": [
        {
          "id": 1,
          "title": "test",
          "description": "test",
          "status": "open",
          "created_at": "2024-11-01T13:34:30.026514"
        }
      ],
      "logs": [],
      "created_at": "2024-11-01T13:23:04.989224"
    },
    "test47": {
      "password": "$pbkdf2-sha256$29000$wHgvxRhDSIkRQsjZu9caIw$cMYTrGY5hm.RKAilJmMHgEwZA3WjuDeGoBjwGDsfryY",
      "servers": [],
      "balance": 0,
      "tickets": [],
      "created_at": "2024-11-01T14:16:46.398342"
    },
    "root": {
      "password": "$pbkdf2-sha256$29000$CyHkvFcKgZAyZoxRqvVeSw$Ia2SkYeNPfIR7t.hSL6Iddbpjce6xhXlRSN1BQ6ORFStE",
      "servers": [],
      "created_at": "2024-11-01T20:48:24.685767"
    },
    "sdfgsdfgsdf": {
      "password": "$pbkdf2-sha256$29000$7X1PKYVwzplTak3pXUuJcQ$t9lQHDe4it6i90B.g/o33Pq1I9C172El0xQIgoJRCig",
      "servers": [],
      "balance": 0,
      "tickets": [],
      "created_at": "2024-11-01T20:54:43.226355"
    },
    "test4453": {
      "password": "$pbkdf2-sha256$29000$DqH0nhPiPKe09t6bs5YSYg$oUtN3nRJKAo3y6taOgvk9JsJbilW/5JzWe/7ZyFoyhM",
      "servers": [
        {
          "id": 1,
          "name": "Server-1730470683877",
          "plan": {
            "name": "\u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439",
            "cpu": 4,
            "ram": 8,
            "price": 1000
          },
          "os": "Windows Server 2022",
          "status": "active",
          "password": "Ry6G7xdxEdgJ",
          "created_at": "2024-11-01T21:18:04.038807",
          "auto_renewal": true,
          "container_name": "server_dcdbff07"
        },
        {
          "id": 2,
          "name": "Server-1730470708511",
          "plan": {
            "name": "\u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439",
            "cpu": 4,
            "ram": 8,
            "price": 1000
          },
          "os": "Windows Server 2022",
          "status": "active",
          "password": "wRLWLik4YbCQ",
          "created_at": "2024-11-01T21:18:28.675348",
          "auto_renewal": true,
          "container_name": "server_a1ec05b8"
        }
      ],
      "balance": 6995000,
      "tickets": [
        {
          "id": 1,
          "title": "test supp",
          "description": "test oke da da",
          "status": "open",
          "created_at": "2024-11-01T14:45:03.348993",
          "messages": [
            {
              "content": "lol test",
              "sender": "nigger",
              "timestamp": "2024-11-01T21:13:22.314963"
            },
            {
              "content": "bro wtfsdsd",
              "sender": "nigger",
              "timestamp": "2024-11-01T21:13:32.032827"
            },
            {
              "content": "xd xd test test",
              "sender": "nigger",
              "timestamp": "2024-11-01T21:14:30.812600"
            }
          ]
        }
      ],
      "created_at": "2024-11-01T14:32:36.132607"
    }
  },
  "logs": [
    {
      "timestamp": "2024-11-01T14:32:36.142610",
      "username": "test4453",
      "action": "register",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T14:32:40.835576",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T14:33:31.349385",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T14:39:23.799863",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T14:44:47.422889",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T14:45:03.356971",
      "username": "test4453",
      "action": "create_ticket",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:48:19.544146",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:48:24.695532",
      "username": "root",
      "action": "register",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:48:37.954109",
      "username": "root",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:48:48.717128",
      "username": "root",
      "action": "create_ticket",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:49:47.317213",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:03.664990",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:06.326324",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:08.417534",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:10.569951",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:10.740979",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:14.040738",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:14.349935",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:18.862408",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:50:31.491974",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:51:01.499242",
      "username": "root",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:51:13.442373",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:19.305104",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:21.009966",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:22.741393",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:24.553724",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:27.102851",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:28.822780",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:30.550532",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:33.369865",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:52:35.224460",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:15.296781",
      "username": "root",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:20.213626",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:22.211460",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:23.906212",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:25.607781",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:27.145068",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:29.887940",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:30.075342",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:32.157342",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:45.955139",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:48.143456",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:50.033988",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:51.823643",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:53:54.171391",
      "username": "root",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:54:43.240790",
      "username": "sdfgsdfgsdf",
      "action": "register",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:54:47.064831",
      "username": "sdfgsdfgsdf",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:54:47.462689",
      "username": "sdfgsdfgsdf",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T20:55:31.311108",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:11:32.257462",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:12:11.641341",
      "username": "nigger",
      "action": "update_userinfo",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:12:28.946506",
      "username": "nigger",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:13:22.325933",
      "username": "nigger",
      "action": "reply_ticket_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:13:32.046816",
      "username": "nigger",
      "action": "reply_ticket_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:14:05.433402",
      "username": "nigger",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:14:30.822573",
      "username": "nigger",
      "action": "reply_ticket_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:18.926730",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:24.937540",
      "username": "test4453",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:27.749704",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:29.867492",
      "username": "test4453",
      "action": "delete_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:41.232193",
      "username": "test4453",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:43.765487",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:44.328450",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:44.490759",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:45.779561",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:47.292192",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:16:48.931452",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:17:19.222597",
      "username": "test4453",
      "action": "delete_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:18:04.051771",
      "username": "test4453",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:18:16.806402",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:18:21.803735",
      "username": "test4453",
      "action": "toggle_server_1",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:18:28.690149",
      "username": "test4453",
      "action": "create_server",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:30:35.405035",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T21:31:11.611327",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T22:01:43.631426",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T22:20:11.950689",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T22:45:48.556343",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T22:57:27.285335",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T22:58:00.339594",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T23:07:20.071026",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-01T23:32:12.713467",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:00:45.579589",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:19:06.118767",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:24:10.431269",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:24:49.379987",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:31:12.774636",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:43:22.127023",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T00:59:22.784522",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    },
    {
      "timestamp": "2024-11-02T01:33:34.202842",
      "username": "test4453",
      "action": "login",
      "browser": "Chrome",
      "os": "Windows",
      "device": "Other",
      "ip": "127.0.0.1"
    }
  ]
}



modify frontend to backend, add all new features, modify database, modify all frontend files, with features, that mentioned in backend