#!/bin/bash
# Axon API Deployment Script for Ubuntu
set -e

# Configuration
PROJECT_NAME="axon-api"
DOMAIN="${DOMAIN:-localhost}"
WEB_USER="webuser"
WEB_DIR="/var/www/${PROJECT_NAME}"

# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
}

warn() {
    echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
}

error() {
    echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
    exit 1
}

# Check if running as root
if [[ $EUID -ne 0 ]]; then
   error "This script must be run as root. Use: sudo ./deploy-axon.sh"
fi

log "Starting Axon API deployment..."

# Update system
log "Updating system packages..."
apt update && apt upgrade -y

# Install dependencies
log "Installing system dependencies..."
apt install -y python3 python3-pip python3-venv nginx curl

# Create web user if doesn't exist
if ! id "$WEB_USER" &>/dev/null; then
    log "Creating web user: $WEB_USER"
    adduser $WEB_USER --gecos "" --disabled-password --quiet

    # Generate random password
    RANDOM_PASSWORD=$(openssl rand -base64 24)
    echo "$WEB_USER:$RANDOM_PASSWORD" | chpasswd

    # Display password and wait for confirmation
    echo ""
    echo -e "${YELLOW}=== IMPORTANT: WEB USER PASSWORD ===${NC}"
    echo -e "${YELLOW}User: $WEB_USER${NC}"
    echo -e "${YELLOW}Password: $RANDOM_PASSWORD${NC}"
    echo -e "${YELLOW}Save this password - you'll need it for SSH access!${NC}"
    echo -e "${YELLOW}=======================================${NC}"
    echo ""

    read -p "Press ENTER after you've copied the password to continue..." -r
    echo ""
fi

# Setup directory structure
log "Setting up directory structure..."
mkdir -p $WEB_DIR
mkdir -p $WEB_DIR/examples
mkdir -p $WEB_DIR/examples/files
chown -R $WEB_USER:www-data $WEB_DIR
chmod -R 775 $WEB_DIR
chmod g+s $WEB_DIR

# Create Python virtual environment
log "Setting up Python environment..."
sudo -u $WEB_USER python3 -m venv $WEB_DIR/venv
sudo -u $WEB_USER $WEB_DIR/venv/bin/pip install --upgrade pip
sudo -u $WEB_USER $WEB_DIR/venv/bin/pip install axon-api uwsgi

# Create WSGI application files
log "Creating application files..."

# Create main.py
cat > /tmp/main.py << 'EOF'
"""
WSGI application entry point for Axon API framework.

Demonstrates basic setup connecting ApplicationEngine with custom routing function.
"""
from axon_api.core.application import ApplicationEngine
from routes import my_routes


def application(environ, start_response):
    """WSGI application entry point."""
    return ApplicationEngine(environ, start_response, router=my_routes)
EOF

# Create routes.py with your actual routes
cat > /tmp/routes.py << 'EOF'
"""
Example route configuration demonstrating framework capabilities.

Shows pattern matching routes with JSON, form data, file streaming, and multipart
responses using the Axon API framework.
"""
import json
from urllib.parse import parse_qsl


def my_routes(environ, request_handler):
    # Setup request context
    method = environ['method']
    path = [part for part in request_handler.path.split('/') if part]
    # Log Route
    request_handler.logger.info(f"Route: {method} /{'/'.join(path)}")
    # Handle response
    response = request_handler.response

    match (method, path):

        case ('GET', ['hello-world']):
            # Return HTML
            return response.file("examples/hello-world.html")

        case ('GET', ['multipart', 'stream']):
            # Stream files
            files = [
                'examples/files/file1.txt',
                'examples/files/file2.txt',
                'examples/files/file3.txt'
            ]
            return response.stream(files)

        case ('GET', ['api', 'query']):
            raw_query_string = environ['query_string']
            return response.json({"raw_query_string": raw_query_string})

        case ('POST', ['api', 'json']):
            try:
                body_bytes = environ['wsgi_input'].read(environ['content_length'])
                raw_json_body = body_bytes.decode('utf-8')
                raw_parsed_data = json.loads(raw_json_body)
            except (IOError, UnicodeDecodeError, json.JSONDecodeError):
                raise
            return response.json({"raw_parsed_data": raw_parsed_data})

        case ('POST', ['api', 'form-data']):
            try:
                body_bytes = environ['wsgi_input'].read(environ['content_length'])
                raw_form_data = body_bytes.decode('utf-8')
                raw_form_data = parse_qsl(raw_form_data)
            except (IOError, UnicodeDecodeError):
                raise
            return response.json({"raw_form_data": raw_form_data})

        case ('GET', ['api', 'stream-files']):
            # Return batched response for batched request
            raw_query_string = environ['query_string']

            if not raw_query_string:
                return response.message("No files specified in query parameters", status_code=400)

            # Parse query parameters to get file paths
            query_params = dict(parse_qsl(raw_query_string))

            # Extract all file paths from query parameters
            files = list(query_params.values())

            if not files:
                return response.message("No valid file parameters found", status_code=400)

            # Stream the requested files
            return response.stream(files)

        case ('GET', ['api', 'health']):
            # Return JSON
            return response.json({"status": "available"})

        case _:
            # Return message
            return response.message("Not Found", status_code=404)
EOF

# Move files to web directory
mv /tmp/main.py $WEB_DIR/main.py
mv /tmp/routes.py $WEB_DIR/routes.py
chown $WEB_USER:www-data $WEB_DIR/main.py $WEB_DIR/routes.py

# Create example files
log "Creating example files..."

# Create hello-world.html
cat > /tmp/hello-world.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>

<h2>1. Form Data Test</h2>
<button onclick="sendFormData()">Send Form Data</button>
<div id="formResult"></div>

<h2>2. JSON Body Test</h2>
<button onclick="sendJsonBody()">Send JSON Body</button>
<div id="jsonResult"></div>

<h2>3. Query String Test</h2>
<button onclick="sendQueryString()">Send Query String</button>
<div id="queryResult"></div>

<h2>4. Multipart Stream Test</h2>
<button onclick="streamFiles()">Stream Multiple Files</button>
<div id="streamResult"></div>

<h2>5. Dynamic File Stream Test</h2>
<button onclick="streamDynamicFiles()">Stream Selected Files</button>
<div id="dynamicStreamResult"></div>

<h2>6. Health Check Test</h2>
<button onclick="checkHealth()">Check API Health</button>
<div id="healthResult"></div>

<h2>7. 404 Not Found Test</h2>
<button onclick="testNotFound()">Test Non-Existent Route</button>
<div id="notFoundResult"></div>

<h2>8. Invalid Stream Request Test</h2>
<button onclick="testInvalidStream()">Test Stream Without Files</button>
<div id="invalidStreamResult"></div>

<script>
    // Test 1: Send form data (application/x-www-form-urlencoded)
    async function sendFormData() {
        try {
            const params = new URLSearchParams();
            params.append('name', 'John Doe');
            params.append('email', 'john@example.com');
            params.append('message', 'Hello from form data');

            const response = await fetch('/api/form-data', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: params
            });

            const result = await response.text();
            document.getElementById('formResult').innerHTML = `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('formResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 2: Send JSON body
    async function sendJsonBody() {
        try {
            const jsonData = {
                name: 'Bob Smith',
                email: 'bob@example.com',
                preferences: {
                    theme: 'dark',
                    notifications: true
                },
                tags: ['developer', 'javascript']
            };

            const response = await fetch('/api/json', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(jsonData)
            });

            const result = await response.text();
            document.getElementById('jsonResult').innerHTML = `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('jsonResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 3: Send query string parameters
    async function sendQueryString() {
        try {
            const params = new URLSearchParams({
                name: 'Jane Doe',
                email: 'jane@example.com',
                filter: 'active'
            });

            const response = await fetch(`/api/query?${params}`, {
                method: 'GET'
            });

            const result = await response.text();
            document.getElementById('queryResult').innerHTML = `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('queryResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 4: Stream multiple files (static)
    async function streamFiles() {
        try {
            const response = await fetch('/multipart/stream', {
                method: 'GET'
            });

            const result = await response.text();
            const contentType = response.headers.get('content-type');

            document.getElementById('streamResult').innerHTML =
                `Status: ${response.status}<br>Content-Type: ${contentType}<br>Response: <pre>${result}</pre>`;
        } catch (error) {
            document.getElementById('streamResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 5: Stream files specified via query parameters
    async function streamDynamicFiles() {
        try {
            const params = new URLSearchParams({
                file1: 'examples/files/file3.txt',
                file2: 'examples/files/file2.txt',
                file3: 'examples/files/file1.txt'
            });

            const response = await fetch(`/api/stream-files?${params}`, {
                method: 'GET'
            });

            const result = await response.text();
            const contentType = response.headers.get('content-type');

            document.getElementById('dynamicStreamResult').innerHTML =
                `Status: ${response.status}<br>` +
                `Content-Type: ${contentType}<br>` +
                `Response: <pre>${result}</pre>`;

        } catch (error) {
            document.getElementById('dynamicStreamResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 6: Health check endpoint
    async function checkHealth() {
        try {
            const response = await fetch('/api/health', {
                method: 'GET'
            });

            const result = await response.text();
            document.getElementById('healthResult').innerHTML =
                `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('healthResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 7: 404 Not Found (default case)
    async function testNotFound() {
        try {
            const response = await fetch('/this/route/does/not/exist', {
                method: 'GET'
            });

            const result = await response.text();
            document.getElementById('notFoundResult').innerHTML =
                `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('notFoundResult').innerHTML = `Error: ${error.message}`;
        }
    }

    // Test 8: Invalid stream request (no query parameters)
    async function testInvalidStream() {
        try {
            const response = await fetch('/api/stream-files', {
                method: 'GET'
            });

            const result = await response.text();
            document.getElementById('invalidStreamResult').innerHTML =
                `Status: ${response.status}<br>Response: ${result}`;
        } catch (error) {
            document.getElementById('invalidStreamResult').innerHTML = `Error: ${error.message}`;
        }
    }
</script>
</body>
</html>
EOF

# Create file1.txt
cat > /tmp/file1.txt << 'EOF'
This is file1.

xq7mzpkfwj8ng4rtylbhc3vd9sax2ei6oqu1zfm5gpkw7nty4hbv8cx9s2aez6rqu1fmjp3gkw8ny5thlb7cv4xd2sai9eruq6zfm1pjgkw8ny5tbhlv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfmjp3gkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruqzfm1pj3gkw8ny5thlbv7cx4d2sai9eruq6zfmjp3gkw8ny5thlb7cx4d2sai9eruq6zfm1pjgkw8ny5th4lbv7cx2dsai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq6zfm1pjgkw8ny5thlbv7cx4d2sai9eruq
EOF

# Create file2.txt
cat > /tmp/file2.txt << 'EOF'
This is file2.

wm3vh9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0odw3vm9xk2lqr8jfp4yt6nzs7ea1cu5ibg0od
EOF

# Create file3.txt
cat > /tmp/file3.txt << 'EOF'
This is file3.

bp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugfbp8hz4xv6rn2jq9wk1sm7ey3oalc5idt0ugf
EOF

# Move example files to examples directory
mv /tmp/hello-world.html $WEB_DIR/examples/hello-world.html
mv /tmp/file1.txt $WEB_DIR/examples/files/file1.txt
mv /tmp/file2.txt $WEB_DIR/examples/files/file2.txt
mv /tmp/file3.txt $WEB_DIR/examples/files/file3.txt

# Set proper ownership and permissions for example files
chown -R $WEB_USER:www-data $WEB_DIR/examples/
find $WEB_DIR/examples -type f -exec chmod 644 {} \;
find $WEB_DIR/examples -type d -exec chmod 755 {} \;

# Create uWSGI configuration
log "Creating uWSGI configuration..."
cat > /tmp/uwsgi.ini << EOF
[uwsgi]
chdir = $WEB_DIR
module = main:application
home = $WEB_DIR/venv
socket = $WEB_DIR/app.sock
chmod-socket = 660
vacuum = true
die-on-term = true
uid = $WEB_USER
gid = www-data
master = true
processes = 2
threads = 16
logto = $WEB_DIR/uwsgi.log
log-level = info

buffer-size = 65536

# WSGI compliance settings for newer axon-api versions
wsgi-file = $WEB_DIR/main.py
callable = application
enable-threads = true
single-interpreter = true
EOF

mv /tmp/uwsgi.ini $WEB_DIR/uwsgi.ini
chown $WEB_USER:www-data $WEB_DIR/uwsgi.ini

# Create systemd service
log "Creating systemd service..."
cat > /tmp/uwsgi-${PROJECT_NAME}.service << EOF
[Unit]
Description=uWSGI service for ${PROJECT_NAME}
After=network.target

[Service]
User=$WEB_USER
Group=www-data
WorkingDirectory=$WEB_DIR
ExecStart=$WEB_DIR/venv/bin/uwsgi --ini $WEB_DIR/uwsgi.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all
TimeoutStartSec=30

[Install]
WantedBy=multi-user.target
EOF

mv /tmp/uwsgi-${PROJECT_NAME}.service /etc/systemd/system/
systemctl daemon-reload

# Create Nginx configuration
log "Creating Nginx configuration..."
cat > /tmp/${PROJECT_NAME} << EOF
server {
    listen 80;
    server_name $DOMAIN;

    client_body_buffer_size 64k;
    client_header_buffer_size 64k;
    large_client_header_buffers 4 64k;

    client_max_body_size 10M;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:$WEB_DIR/app.sock;

        # Add timeout settings
        uwsgi_read_timeout 300;
        uwsgi_connect_timeout 300;
        uwsgi_send_timeout 300;
    }

    # Health check endpoint (bypass uWSGI for faster response)
    location /nginx-health {
        access_log off;
        return 200 "nginx healthy\n";
        add_header Content-Type text/plain;
    }
}
EOF

mv /tmp/${PROJECT_NAME} /etc/nginx/sites-available/
ln -sf /etc/nginx/sites-available/${PROJECT_NAME} /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default

# Test Nginx configuration
log "Testing Nginx configuration..."
nginx -t || error "Nginx configuration test failed"

# Start services with proper ordering
log "Starting services..."

# Start uWSGI first
systemctl enable uwsgi-${PROJECT_NAME}
systemctl start uwsgi-${PROJECT_NAME}

# Wait for socket creation
log "Waiting for uWSGI socket..."
for i in {1..30}; do
    if [ -S "$WEB_DIR/app.sock" ]; then
        log "uWSGI socket created successfully"
        break
    fi
    if [ $i -eq 30 ]; then
        error "uWSGI socket not created after 30 seconds"
    fi
    sleep 1
done

# Verify socket permissions
chown $WEB_USER:www-data $WEB_DIR/app.sock
chmod 660 $WEB_DIR/app.sock

# Start Nginx
systemctl restart nginx
systemctl enable nginx

# Verify deployment
log "Verifying deployment..."
sleep 2

if curl -f -s http://localhost/api/health > /dev/null; then
    log "✅ Deployment successful!"
    log "Application is running at: http://$DOMAIN"
    log "Hello World example: http://$DOMAIN/hello-world"
    log "Health check: http://$DOMAIN/api/health"
else
    warn "⚠️  Application may not be responding correctly"
    log "Check logs:"
    log "  journalctl -u uwsgi-${PROJECT_NAME} -f"
    log "  tail -f $WEB_DIR/uwsgi.log"
    log "  journalctl -u nginx -f"
fi

log "Deployment complete!"
log ""
log "Available endpoints:"
log "  http://$DOMAIN/hello-world - Interactive test interface"
log "  http://$DOMAIN/api/health - Health check endpoint"
log "  http://$DOMAIN/multipart/stream - File streaming test"
log "  http://$DOMAIN/api/stream-files?file1=examples/files/file1.txt - Dynamic file streaming"
log ""
log "Useful commands:"
log "  Restart application: systemctl restart uwsgi-${PROJECT_NAME}"
log "  View logs: journalctl -u uwsgi-${PROJECT_NAME} -f"
log "  Deploy new code: Replace files in $WEB_DIR and restart service"