"""
═══════════════════════════════════════════════════════════
📱 Example Flask App with OpenTelemetry
═══════════════════════════════════════════════════════════

Run with:
    python app.py

Or with auto-instrumentation:
    opentelemetry-instrument python app.py
"""

import os
import time
import random
import atexit
from flask import Flask, jsonify, request
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
from opentelemetry.instrumentation.flask import FlaskInstrumentor

# Initialize tracing BEFORE creating Flask app
from tracing import init_tracing, get_tracer, shutdown

init_tracing()
tracer = get_tracer()

app = Flask(__name__)

# Auto-instrument Flask
FlaskInstrumentor().instrument_app(app)

# Register shutdown handler
atexit.register(shutdown)

# Simulated database
users = [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"},
    {"id": 3, "name": "Charlie", "email": "charlie@example.com"},
]


@app.route('/health')
def health():
    """Health check endpoint."""
    return jsonify({"status": "healthy", "service": "python-example"})


@app.route('/users')
def get_users():
    """Get all users with custom span."""
    with tracer.start_as_current_span("db.query.users") as span:
        span.set_attribute("db.system", "memory")
        span.set_attribute("db.operation", "SELECT")
        
        # Simulate database latency
        time.sleep(random.uniform(0.01, 0.1))
        
        span.set_attribute("db.row_count", len(users))
        span.set_status(Status(StatusCode.OK))
        
        return jsonify({"users": users})


@app.route('/users/<int:user_id>')
def get_user(user_id: int):
    """Get user by ID."""
    with tracer.start_as_current_span("db.query.user_by_id") as span:
        span.set_attribute("db.system", "memory")
        span.set_attribute("db.operation", "SELECT")
        span.set_attribute("user.id", user_id)
        
        # Simulate database latency
        time.sleep(random.uniform(0.01, 0.05))
        
        user = next((u for u in users if u["id"] == user_id), None)
        
        if not user:
            span.set_status(Status(StatusCode.ERROR, "User not found"))
            return jsonify({"error": "User not found"}), 404
        
        span.set_status(Status(StatusCode.OK))
        return jsonify({"user": user})


@app.route('/orders', methods=['POST'])
def create_order():
    """Create order with nested spans."""
    with tracer.start_as_current_span("order.create") as parent_span:
        try:
            data = request.get_json() or {}
            items = data.get("items", [])
            
            # Step 1: Validate inventory
            with tracer.start_as_current_span("inventory.check") as span:
                span.set_attribute("order.items", len(items))
                time.sleep(random.uniform(0.05, 0.1))
                span.set_status(Status(StatusCode.OK))
            
            # Step 2: Process payment
            with tracer.start_as_current_span("payment.process") as span:
                span.set_attribute("payment.method", "credit_card")
                time.sleep(random.uniform(0.1, 0.2))
                span.set_status(Status(StatusCode.OK))
            
            # Step 3: Create order record
            with tracer.start_as_current_span("db.insert.order") as span:
                time.sleep(random.uniform(0.02, 0.05))
                order_id = hex(int(time.time() * 1000))[2:]
                span.set_attribute("order.id", order_id)
                span.set_status(Status(StatusCode.OK))
            
            parent_span.set_status(Status(StatusCode.OK))
            return jsonify({"orderId": order_id, "status": "created"})
            
        except Exception as e:
            parent_span.set_status(Status(StatusCode.ERROR, str(e)))
            parent_span.record_exception(e)
            return jsonify({"error": str(e)}), 500


@app.route('/error')
def trigger_error():
    """Trigger error for testing."""
    span = trace.get_current_span()
    error = Exception("Simulated error for testing")
    
    span.record_exception(error)
    span.set_status(Status(StatusCode.ERROR, str(error)))
    
    return jsonify({"error": str(error)}), 500


@app.route('/external-call')
def external_call():
    """Make external HTTP call (demonstrates distributed tracing)."""
    import requests
    
    with tracer.start_as_current_span("external.http.call") as span:
        span.set_attribute("http.url", "https://httpbin.org/get")
        
        try:
            # Note: requests auto-instrumentation propagates trace context
            response = requests.get("https://httpbin.org/get", timeout=5)
            span.set_attribute("http.status_code", response.status_code)
            span.set_status(Status(StatusCode.OK))
            return jsonify({"status": "ok", "external_status": response.status_code})
        except Exception as e:
            span.record_exception(e)
            span.set_status(Status(StatusCode.ERROR, str(e)))
            return jsonify({"error": str(e)}), 500


if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))
    print(f"""
🚀 Example app listening on http://localhost:{port}

Try these endpoints:
  GET  http://localhost:{port}/health
  GET  http://localhost:{port}/users
  GET  http://localhost:{port}/users/1
  POST http://localhost:{port}/orders
  GET  http://localhost:{port}/error
  GET  http://localhost:{port}/external-call
""")
    app.run(host='0.0.0.0', port=port, debug=False)
