================================================================================
  Question 3: CoAP Air Quality Monitoring & Random Forest Classification [30 Marks]
================================================================================

FULL QUESTION:
CAE 2 IoT Practical Exam - Section B (Answer either Q3 or Q4):
Q3. CoAP-Based Air Quality Monitoring and Random Forest Classification [30 Marks]
Develop a CoAP-based air quality monitoring and classification system for Kochi.
Input API Link: https://air-quality-api.open-meteo.com/v1/air-quality?latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone
Air-quality parameters: PM2.5, PM10, NO2, SO2, and O3.
Dataset: city_day.csv.
The system should:
- Develop a CoAP Server and CoAP Client. [6 Marks]
- Retrieve live air-quality data of Kochi via CoAP Server. [6 Marks]
- Transfer data to CoAP Client using CoAP GET request. [5 Marks]
- Develop a dashboard displaying PM2.5, PM10, NO2, SO2, and O3. [5 Marks]
- Train and apply Random Forest classification using historical city_day.csv dataset to classify into Good, Satisfactory, Moderate, Poor, Very Poor, Severe. [5 Marks]
- Display predicted air-quality classification on the dashboard. [3 Marks]
Flow: Air Quality API -> CoAP Server -> CoAP Client -> Dashboard -> Random Forest Classification.

SHORT QUESTION:
CoAP Air Quality Server & Client with Random Forest Classification and Dashboard [30 Marks].

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Question 3: CoAP-Based Air Quality Monitoring and Random Forest Classification

## Problem Statement [30 Marks]
Develop a CoAP-based air quality monitoring and classification system for Kochi.

- **Input API Link**:
  `https://air-quality-api.open-meteo.com/v1/air-quality?latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone`
- **Air Quality Parameters**: PM2.5, PM10, NO₂, SO₂ and O₃
- **Historical Dataset**: Kaggle `city_day.csv` (included in this folder)
- **Model**: Random Forest Classifier
  - Input Features: `PM2.5`, `PM10`, `NO2`, `SO2`, `O3`
  - Target: `AQI_Bucket` (Good, Satisfactory, Moderate, Poor, Very Poor, Severe)
- **Live Ground Truth**: [https://app.cpcbccr.com/](https://app.cpcbccr.com/)

---

## Architectural Flow
```text
Air Quality API ──> CoAP Server (UDP 5683) ──> CoAP Client ──> Dashboard ──> Random Forest Classification
```

---

## Files in this Folder
- `server.py`: Asynchronous CoAP Server listening on port 5683 (`/airquality`), fetching live Kochi data from Open-Meteo REST API.
- `client.py`: Standalone CLI CoAP Client that fetches live readings, trains Random Forest model, and prints predictions to console.
- `app.py`: Full-featured Streamlit interactive web dashboard and CoAP Client.
- `city_day.csv`: Historical dataset containing air quality records across Indian cities.

---

## How to Run

### Step 1: Install Dependencies
```bash
pip install pandas scikit-learn streamlit aiocoap requests matplotlib
```

### Step 2: Start the CoAP Server
Open a terminal in the `Q3` directory:
```bash
python server.py
```
*Expected Server Output:*
```text
============================================================
  Kochi CoAP Air Quality Server Active on Port 5683
============================================================
Endpoint: coap://127.0.0.1:5683/airquality
Waiting for CoAP Client GET requests...
```

### Step 3: Run the CLI Client OR the Streamlit Dashboard

#### Option A: Run CLI Client
Open a second terminal in the `Q3` directory:
```bash
python client.py
```

#### Option B: Run Streamlit Dashboard
```bash
streamlit run app.py
```
Open your browser at `http://localhost:8501`. Click **"Fetch Live Air Quality via CoAP"** to trigger a CoAP GET transaction and view live predictions and model metrics!

================================================================================
SOURCE CODE FILES:
================================================================================

1. server.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 3: CoAP-Based Air Quality Monitoring and Random Forest Classification
================================================================================
Develop a CoAP-based air quality monitoring and classification system for Kochi.
Input API Link:
https://air-quality-api.open-meteo.com/v1/air-quality?latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone
The system should use the following air-quality parameters:
PM2.5, PM10, NO₂, SO₂ and O₃

Dataset:
The Random Forest model should be trained using historical air-quality data
from the following Kaggle dataset:
Official Kaggle Dataset:
https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india
File: city_day.csv

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live air-quality
   data of Kochi from the given Air Quality API through the CoAP Server. [6 Marks]
3. Transfer the retrieved air-quality data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved air-quality parameters
   (PM2.5, PM10, NO₂, SO₂ and O₃). [5 Marks]
5. Train and apply a Random Forest classification model using the historical
   city_day.csv dataset to classify the current air-quality data into appropriate
   air-quality categories such as Good, Satisfactory, Moderate, Poor, Very Poor,
   or Severe. [5 Marks]
6. Display the predicted air-quality classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Air Quality API → CoAP Server → CoAP Client → Dashboard → Random Forest Classification
================================================================================
"""

# Short Description: CoAP Server that retrieves live Kochi air-quality telemetry from Open-Meteo REST API and serves PM2.5, PM10, NO2, SO2, and O3 parameters to CoAP clients via UDP port 5683.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with aiocoap and requests installed (`pip install aiocoap requests`).
- Port / Protocol: Uses CoAP protocol over UDP port 5683.
- How to Run:
    1. Start this server first:
       python server.py
    2. Once running, start the CoAP client or Streamlit dashboard (app.py) in a separate terminal.
- Expected Behavior:
    * Server binds to 127.0.0.1:5683 with endpoint '/airquality'.
    * When a CoAP GET request is received from a client, queries Open-Meteo live Air Quality API.
    * Serves serialized JSON payload with PM2.5, PM10, NO2, SO2, and O3 to the CoAP client.
"""

import asyncio
import json
import requests
from aiocoap import Context, Message, resource

AIR_QUALITY_URL = (
    "https://air-quality-api.open-meteo.com/v1/air-quality?"
    "latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone"
)


class AirQualityResource(resource.Resource):
    """CoAP Resource serving live Kochi air quality data over CoAP GET."""

    def fetch_live_api(self):
        response = requests.get(AIR_QUALITY_URL, timeout=10)
        response.raise_for_status()
        return response.json().get("current", {})

    async def render_get(self, request):
        print(f"\n[CoAP Server] Received GET request from {request.remote}")
        try:
            # Query the live air quality API asynchronously
            current = await asyncio.to_thread(self.fetch_live_api)

            # Build standardized payload with required features
            payload = {
                "status": "success",
                "city": "Kochi",
                "latitude": 9.93,
                "longitude": 76.27,
                "time": current.get("time"),
                "PM2.5": current.get("pm2_5"),
                "PM10": current.get("pm10"),
                "NO2": current.get("nitrogen_dioxide"),
                "SO2": current.get("sulphur_dioxide"),
                "O3": current.get("ozone"),
            }

            print("[CoAP Server] Serving live air quality parameters:")
            print(f"  PM2.5 : {payload['PM2.5']} ug/m3")
            print(f"  PM10  : {payload['PM10']} ug/m3")
            print(f"  NO2   : {payload['NO2']} ug/m3")
            print(f"  SO2   : {payload['SO2']} ug/m3")
            print(f"  O3    : {payload['O3']} ug/m3")

            encoded = json.dumps(payload).encode("utf-8")
            return Message(payload=encoded)

        except Exception as e:
            error_msg = json.dumps({"status": "error", "error": str(e)}).encode("utf-8")
            print(f"[CoAP Server] Error fetching API: {e}")
            return Message(payload=error_msg)


async def main():
    root = resource.Site()
    root.add_resource(["airquality"], AirQualityResource())

    await Context.create_server_context(root, bind=("127.0.0.1", 5683))
    print("=" * 60)
    print("  Kochi CoAP Air Quality Server Active on Port 5683")
    print("=" * 60)
    print("Endpoint: coap://127.0.0.1:5683/airquality")
    print("Waiting for CoAP Client GET requests (Press Ctrl+C to stop)...")
    await asyncio.get_running_loop().create_future()


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("\n[CoAP Server] Stopped by user.")
--------------------------------------------------------------------------------

2. client.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 3: CoAP-Based Air Quality Monitoring and Random Forest Classification
================================================================================
Develop a CoAP-based air quality monitoring and classification system for Kochi.
Input API Link:
https://air-quality-api.open-meteo.com/v1/air-quality?latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone
The system should use the following air-quality parameters:
PM2.5, PM10, NO₂, SO₂ and O₃

Dataset:
The Random Forest model should be trained using historical air-quality data
from the following Kaggle dataset:
Official Kaggle Dataset:
https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india
File: city_day.csv

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live air-quality
   data of Kochi from the given Air Quality API through the CoAP Server. [6 Marks]
3. Transfer the retrieved air-quality data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved air-quality parameters
   (PM2.5, PM10, NO₂, SO₂ and O₃). [5 Marks]
5. Train and apply a Random Forest classification model using the historical
   city_day.csv dataset to classify the current air-quality data into appropriate
   air-quality categories such as Good, Satisfactory, Moderate, Poor, Very Poor,
   or Severe. [5 Marks]
6. Display the predicted air-quality classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Air Quality API → CoAP Server → CoAP Client → Dashboard → Random Forest Classification
================================================================================
"""

# Short Description: CoAP CLI Client that retrieves live Kochi air-quality parameters via CoAP GET from server.py, trains a Random Forest Classifier on city_day.csv, and outputs live classification.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with pandas, scikit-learn, aiocoap (`pip install pandas scikit-learn aiocoap`).
- Setup:
    1. Make sure `city_day.csv` is present in the current folder.
    2. Start the CoAP server in another terminal (`python server.py`).
- How to Run:
    python client.py
- Expected Behavior:
    * Connects to coap://127.0.0.1:5683/airquality and fetches live readings (PM2.5, PM10, NO2, SO2, O3).
    * Trains Random Forest model on historical city_day.csv dataset.
    * Displays test accuracy, confusion matrix, and feature importances.
    * Classifies current Kochi air quality into category: Good / Satisfactory / Moderate / Poor / Very Poor / Severe.
"""

import asyncio
import json
import os
import pandas as pd
from aiocoap import Context, Message, GET
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report

FEATURES = ["PM2.5", "PM10", "NO2", "SO2", "O3"]
TARGET = "AQI_Bucket"
SERVER_URI = "coap://127.0.0.1:5683/airquality"


async def fetch_air_quality_coap(uri=SERVER_URI):
    """Retrieve live air quality data from the CoAP Server via CoAP GET."""
    protocol = await Context.create_client_context()
    try:
        request = Message(code=GET, uri=uri)
        response = await protocol.request(request).response
        return json.loads(response.payload.decode("utf-8"))
    finally:
        await protocol.shutdown()


def train_random_forest(csv_path="city_day.csv"):
    """Train Random Forest classifier on historical Kaggle city_day.csv dataset."""
    if not os.path.exists(csv_path):
        raise FileNotFoundError(f"Dataset '{csv_path}' not found in current folder.")

    print(f"\n[1] Loading historical air quality data from '{csv_path}'...")
    df = pd.read_csv(csv_path)

    # Filter required features and clean missing values
    data = df[FEATURES + [TARGET]].dropna()
    print(f"    Loaded {len(data)} valid training records.")

    X = data[FEATURES]
    y = data[TARGET]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.20, random_state=42, stratify=y
    )

    print("[2] Training Random Forest Classifier (100 estimators)...")
    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    print(f"    Model Accuracy: {accuracy * 100:.2f}%\n")
    print("Classification Summary:")
    print(classification_report(y_test, y_pred))

    return model, accuracy


def main():
    print("=" * 70)
    print("  Kochi CoAP Air Quality Monitor & Random Forest Classification")
    print("=" * 70)

    # 1. Fetch live telemetry via CoAP
    print(f"\n[Step 1] Connecting to CoAP Server at {SERVER_URI}...")
    try:
        coap_data = asyncio.run(fetch_air_quality_coap())
        if coap_data.get("status") == "error":
            print(f"[Error from Server]: {coap_data.get('error')}")
            return
    except Exception as e:
        print(f"[ERROR] Could not connect to CoAP Server on 127.0.0.1:5683: {e}")
        print("Please ensure 'python server.py' is running in another terminal.")
        return

    print("\n[Step 2] Live Air Quality Telemetry Received via CoAP:")
    print(f"  Location   : {coap_data.get('city')} (Lat: {coap_data.get('latitude')}, Lon: {coap_data.get('longitude')})")
    print(f"  Timestamp  : {coap_data.get('time')}")
    print(f"  PM2.5      : {coap_data.get('PM2.5')} ug/m3")
    print(f"  PM10       : {coap_data.get('PM10')} ug/m3")
    print(f"  NO2        : {coap_data.get('NO2')} ug/m3")
    print(f"  SO2        : {coap_data.get('SO2')} ug/m3")
    print(f"  O3         : {coap_data.get('O3')} ug/m3")

    # 2. Train Random Forest model
    model, _ = train_random_forest()

    # 3. Classify the live CoAP parameters
    input_sample = pd.DataFrame([{
        "PM2.5": float(coap_data["PM2.5"]),
        "PM10":  float(coap_data["PM10"]),
        "NO2":   float(coap_data["NO2"]),
        "SO2":   float(coap_data["SO2"]),
        "O3":    float(coap_data["O3"])
    }])

    predicted_category = model.predict(input_sample)[0]
    probabilities = model.predict_proba(input_sample)[0]

    print("=" * 70)
    print(f"  PREDICTED AIR QUALITY CATEGORY: >>> {predicted_category.upper()} <<<")
    print("=" * 70)
    print("Class Probabilities:")
    for cls, prob in zip(model.classes_, probabilities):
        print(f"  {cls:15s}: {prob * 100:6.2f}%")
    print("\nLive CPC BCCR Ground Truth Monitor: https://app.cpcbccr.com/")


if __name__ == "__main__":
    main()
--------------------------------------------------------------------------------

3. app.py
--------------------------------------------------------------------------------
"""
================================================================================
CAE 2 – IOT PRACTICAL EXAMINATION
QUESTION 3: CoAP-Based Air Quality Monitoring and Random Forest Classification
================================================================================
Develop a CoAP-based air quality monitoring and classification system for Kochi.
Input API Link:
https://air-quality-api.open-meteo.com/v1/air-quality?latitude=9.93&longitude=76.27&current=pm10,pm2_5,nitrogen_dioxide,sulphur_dioxide,ozone
The system should use the following air-quality parameters:
PM2.5, PM10, NO₂, SO₂ and O₃

Dataset:
The Random Forest model should be trained using historical air-quality data
from the following Kaggle dataset:
Official Kaggle Dataset:
https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india
File: city_day.csv

The system should:
1. Develop a CoAP Server and CoAP Client. [6 Marks]
2. On receiving a request from the CoAP Client, retrieve the live air-quality
   data of Kochi from the given Air Quality API through the CoAP Server. [6 Marks]
3. Transfer the retrieved air-quality data from the CoAP Server to the CoAP Client
   using a CoAP GET request. [5 Marks]
4. Develop a dashboard to display the retrieved air-quality parameters
   (PM2.5, PM10, NO₂, SO₂ and O₃). [5 Marks]
5. Train and apply a Random Forest classification model using the historical
   city_day.csv dataset to classify the current air-quality data into appropriate
   air-quality categories such as Good, Satisfactory, Moderate, Poor, Very Poor,
   or Severe. [5 Marks]
6. Display the predicted air-quality classification on the dashboard. [3 Marks]

The system should demonstrate the following flow:
Air Quality API → CoAP Server → CoAP Client → Dashboard → Random Forest Classification
================================================================================
"""

# Short Description: Streamlit Dashboard acting as a CoAP Client to retrieve live Kochi air-quality parameters from CoAP Server, visualize them, and classify AQI_Bucket using a trained Random Forest model.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.8+ with streamlit, pandas, scikit-learn, aiocoap, matplotlib (`pip install streamlit pandas scikit-learn aiocoap matplotlib`).
- Setup:
    1. Make sure `city_day.csv` is present in the current folder.
    2. Start the CoAP server in a separate terminal:
       python server.py
    3. Launch this Streamlit dashboard:
       streamlit run app.py
- Expected Behavior:
    * Loads and trains Random Forest Classifier on city_day.csv (cached for instant performance).
    * Clicking 'Fetch Live Air Quality via CoAP' sends a CoAP GET request to coap://127.0.0.1:5683/airquality.
    * Displays live parameters: PM2.5, PM10, NO2, SO2, O3 in metric cards.
    * Predicts AQI category (Good, Satisfactory, Moderate, Poor, Very Poor, Severe) with probabilities.
"""

import asyncio
import json
import os
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
from aiocoap import Context, Message, GET
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix

st.set_page_config(
    page_title="Kochi Air Quality CoAP & Random Forest",
    page_icon="🌿",
    layout="wide"
)

FEATURES = ["PM2.5", "PM10", "NO2", "SO2", "O3"]
TARGET = "AQI_Bucket"
COAP_URI = "coap://127.0.0.1:5683/airquality"

AQI_COLORS = {
    "Good": "#2ecc71",
    "Satisfactory": "#82c91e",
    "Moderate": "#f1c40f",
    "Poor": "#e67e22",
    "Very Poor": "#e74c3c",
    "Severe": "#8e44ad"
}


# ==============================================================================
# Model Training & Caching
# ==============================================================================
@st.cache_resource
def load_and_train_rf(csv_file="city_day.csv"):
    if not os.path.exists(csv_file):
        st.error(f"Dataset '{csv_file}' not found.")
        st.stop()

    data = pd.read_csv(csv_file)
    data = data[FEATURES + [TARGET]].dropna()

    X = data[FEATURES]
    y = data[TARGET]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.20, random_state=42, stratify=y
    )

    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    acc = accuracy_score(y_test, y_pred)
    cm = confusion_matrix(y_test, y_pred, labels=model.classes_)

    return model, acc, cm, model.classes_, len(data)


# ==============================================================================
# CoAP Client Function
# ==============================================================================
async def async_fetch_coap(uri=COAP_URI):
    protocol = await Context.create_client_context()
    try:
        request = Message(code=GET, uri=uri)
        response = await protocol.request(request).response
        return json.loads(response.payload.decode("utf-8"))
    finally:
        await protocol.shutdown()


def get_live_data_via_coap():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        return loop.run_until_complete(async_fetch_coap())
    finally:
        loop.close()


# ==============================================================================
# UI Header
# ==============================================================================
st.title("🌿 Kochi Air Quality Monitoring & Random Forest Classification")
st.markdown(
    "**Demonstration Flow:** `Air Quality API` ➔ `CoAP Server` ➔ `CoAP Client` ➔ `Dashboard` ➔ `Random Forest Classification`"
)

# Load Model
with st.spinner("Training Random Forest Classifier on city_day.csv..."):
    model, accuracy, cm, classes, total_samples = load_and_train_rf()

# Top metric summary banner
st.sidebar.header("📊 Model Specifications")
st.sidebar.info(
    f"**Model:** Random Forest (100 Trees)\n\n"
    f"**Features:** PM2.5, PM10, NO₂, SO₂, O₃\n\n"
    f"**Target:** AQI_Bucket\n\n"
    f"**Training Records:** {total_samples:,}\n\n"
    f"**Test Accuracy:** **{accuracy * 100:.2f}%**"
)
st.sidebar.markdown("---")
st.sidebar.markdown(
    "🔗 [Official CPC BCCR Ground Truth](https://app.cpcbccr.com/)\n\n"
    "📡 **CoAP Server:** `coap://127.0.0.1:5683/airquality`"
)

# ==============================================================================
# CoAP Telemetry Acquisition Section
# ==============================================================================
st.subheader("1. CoAP Telemetry Transfer (Server ➔ Client)")

col_btn, col_info = st.columns([1, 3])
with col_btn:
    fetch_clicked = st.button("📡 Fetch Live Air Quality via CoAP", type="primary")

# Maintain state of last retrieved data
if "air_data" not in st.session_state:
    st.session_state["air_data"] = None

if fetch_clicked:
    try:
        with st.spinner("Sending CoAP GET request to coap://127.0.0.1:5683/airquality..."):
            res = get_live_data_via_coap()
            if res.get("status") == "error":
                st.error(f"Server Error: {res.get('error')}")
            else:
                st.session_state["air_data"] = res
                st.success("✅ CoAP 2.05 Content successfully received from CoAP Server!")
    except Exception as e:
        st.error(f"❌ CoAP Transmission Error: {e}")
        st.info("Tip: Ensure the CoAP Server is running in terminal: `python server.py`")

# Display live metrics if available
air_data = st.session_state["air_data"]

if air_data and air_data.get("status") == "success":
    st.caption(f"📍 City: **{air_data.get('city')}** | ⏱️ Timestamp: **{air_data.get('time')}** | Lat: 9.93, Lon: 76.27")
    m1, m2, m3, m4, m5 = st.columns(5)
    m1.metric("PM2.5", f"{air_data['PM2.5']} µg/m³")
    m2.metric("PM10", f"{air_data['PM10']} µg/m³")
    m3.metric("NO₂", f"{air_data['NO2']} µg/m³")
    m4.metric("SO₂", f"{air_data['SO2']} µg/m³")
    m5.metric("O₃", f"{air_data['O3']} µg/m³")

    # Set default values for prediction
    def_pm25 = float(air_data["PM2.5"])
    def_pm10 = float(air_data["PM10"])
    def_no2 = float(air_data["NO2"])
    def_so2 = float(air_data["SO2"])
    def_o3 = float(air_data["O3"])
else:
    st.warning("CoAP data not fetched yet. Click the button above to query CoAP Server, or test using the sliders below.")
    def_pm25, def_pm10, def_no2, def_so2, def_o3 = 32.5, 39.5, 4.2, 4.3, 143.0

st.divider()

# ==============================================================================
# Random Forest Classification Section
# ==============================================================================
st.subheader("2. Random Forest Classification & Prediction")

col_left, col_right = st.columns([1, 1])

with col_left:
    st.write("##### Air Quality Input Parameters")
    c1, c2 = st.columns(2)
    with c1:
        in_pm25 = st.number_input("PM2.5 (µg/m³)", min_value=0.0, max_value=1000.0, value=def_pm25, step=1.0)
        in_pm10 = st.number_input("PM10 (µg/m³)", min_value=0.0, max_value=1000.0, value=def_pm10, step=1.0)
        in_no2 = st.number_input("NO₂ (µg/m³)", min_value=0.0, max_value=500.0, value=def_no2, step=0.5)
    with c2:
        in_so2 = st.number_input("SO₂ (µg/m³)", min_value=0.0, max_value=500.0, value=def_so2, step=0.5)
        in_o3 = st.number_input("O₃ (µg/m³)", min_value=0.0, max_value=500.0, value=def_o3, step=1.0)

    sample_df = pd.DataFrame([{
        "PM2.5": in_pm25, "PM10": in_pm10, "NO2": in_no2, "SO2": in_so2, "O3": in_o3
    }])

    pred = model.predict(sample_df)[0]
    proba = model.predict_proba(sample_df)[0]
    color = AQI_COLORS.get(pred, "#3498db")

with col_right:
    st.write("##### Classification Result")
    st.markdown(
        f"""
        <div style="background-color: {color}; padding: 20px; border-radius: 12px; text-align: center; color: white;">
            <h3 style="margin: 0; color: white;">PREDICTED AQI CATEGORY</h3>
            <h1 style="margin: 10px 0; font-size: 42px; font-weight: bold; color: white;">{pred.upper()}</h1>
        </div>
        """,
        unsafe_allow_html=True
    )

    st.write("")
    st.write("**Prediction Probabilities Across Categories:**")
    prob_df = pd.DataFrame({
        "Category": model.classes_,
        "Probability (%)": [round(p * 100, 2) for p in proba]
    }).sort_values("Probability (%)", ascending=False)
    st.dataframe(prob_df, hide_index=True, use_container_width=True)

st.divider()

# ==============================================================================
# Model Analytics & Evaluation
# ==============================================================================
st.subheader("3. Model Performance & Evaluation")
tab1, tab2 = st.tabs(["Feature Importances", "Confusion Matrix"])

with tab1:
    fig, ax = plt.subplots(figsize=(8, 3))
    feat_imp = pd.Series(model.feature_importances_, index=FEATURES).sort_values()
    feat_imp.plot(kind="barh", ax=ax, color="#16a085")
    ax.set_xlabel("Gini Importance")
    ax.set_title("Relative Feature Importance in Random Forest")
    ax.grid(axis="x", linestyle="--", alpha=0.6)
    st.pyplot(fig)

with tab2:
    cm_df = pd.DataFrame(cm, index=classes, columns=classes)
    st.write("**Confusion Matrix (Test Split = 20%):**")
    st.dataframe(cm_df, use_container_width=True)
--------------------------------------------------------------------------------
