import logging
from typing import Any, Dict, List, Optional

from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import JSONResponse
from pydantic import BaseModel, validator
import datetime
import psycopg2
import redis
import qdrant_client

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Define data models for request and response validation
class PaginationParams(BaseModel):
    """
    Model for pagination parameters.
    """
    page: int = Query(1, ge=1, description="Page number")
    page_size: int = Query(10, ge=1, le=100, description="Number of items per page")

class DateFilterParams(BaseModel):
    """
    Model for date filtering parameters.
    """
    start_date: Optional[datetime.date] = Query(None, description="Start date for filtering (YYYY-MM-DD)")
    end_date: Optional[datetime.date] = Query(None, description="End date for filtering (YYYY-MM-DD)")

    @validator("end_date")
    def end_date_after_start_date(cls, end_date, values):
        """
        Validator to ensure end date is after start date.
        """
        start_date = values.get("start_date")
        if start_date and end_date and end_date < start_date:
            raise ValueError("End date must be after start date")
        return end_date


class DashboardResponse(BaseModel):
    """
    Model for the dashboard API response.
    """
    data: List[Dict[str, Any]]
    total: int
    page: int
    page_size: int

# Initialize FastAPI app
app = FastAPI(title="Dashboard API", description="REST API for all dashboard data")

# Database Configuration
DB_HOST = "postgresql-genesis-u50607.vm.elestio.app"
DB_PORT = 25432
DB_NAME = "your_db_name"  # Replace with your actual database name
DB_USER = "your_db_user"  # Replace with your actual database user
DB_PASSWORD = "your_db_password"  # Replace with your actual database password

# Redis Configuration
REDIS_HOST = "redis-genesis-u50607.vm.elestio.app"
REDIS_PORT = 26379

# Qdrant Configuration
QDRANT_HOST = "qdrant-b3knu-u50607.vm.elestio.app"
QDRANT_PORT = 6333

def get_db_connection():
    """
    Establishes and returns a database connection.
    """
    try:
        conn = psycopg2.connect(
            host=DB_HOST,
            port=DB_PORT,
            dbname=DB_NAME,
            user=DB_USER,
            password=DB_PASSWORD
        )
        return conn
    except psycopg2.Error as e:
        logging.error(f"Database connection error: {e}")
        raise HTTPException(status_code=500, detail="Database connection failed")

def get_redis_connection():
    """
    Establishes and returns a Redis connection.
    """
    try:
        r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)
        r.ping()  # Check connection
        return r
    except redis.exceptions.ConnectionError as e:
        logging.error(f"Redis connection error: {e}")
        raise HTTPException(status_code=500, detail="Redis connection failed")

def get_qdrant_client():
    """
    Establishes and returns a Qdrant client.
    """
    try:
        client = qdrant_client.QdrantClient(host=QDRANT_HOST, port=QDRANT_PORT)
        client.get_collections() # Check connection
        return client
    except Exception as e:
        logging.error(f"Qdrant connection error: {e}")
        raise HTTPException(status_code=500, detail="Qdrant connection failed")


@app.get("/api/dashboard/{data_type}", response_model=DashboardResponse)
async def get_dashboard_data(
    data_type: str,
    pagination: PaginationParams = Depends(),
    date_filter: DateFilterParams = Depends()
):
    """
    Retrieves dashboard data based on the specified data type, pagination, and date filters.
    """
    try:
        page = pagination.page
        page_size = pagination.page_size
        start_date = date_filter.start_date
        end_date = date_filter.end_date

        logging.info(f"Fetching dashboard data for type: {data_type}, page: {page}, page_size: {page_size}, start_date: {start_date}, end_date: {end_date}")

        # Placeholder for data retrieval logic based on data_type
        # This is where you would connect to your database, Redis, Qdrant, etc.
        # and construct the appropriate query based on the data_type and filters.

        # Example: Fetch data from PostgreSQL
        conn = get_db_connection()
        cur = conn.cursor()

        # Construct SQL query with pagination and date filtering
        query = f"SELECT * FROM {data_type} "  # Replace with your actual table name
        where_clauses = []
        if start_date:
            where_clauses.append(f"date_column >= '{start_date}'")  # Replace date_column
        if end_date:
            where_clauses.append(f"date_column <= '{end_date}'")  # Replace date_column

        if where_clauses:
            query += "WHERE " + " AND ".join(where_clauses)

        query += f" LIMIT {page_size} OFFSET {(page - 1) * page_size}"

        cur.execute(query)
        data = [dict(row) for row in [dict((cur.description[i][0], value) for i, value in enumerate(row)) for row in cur.fetchall()]]  # Convert to list of dictionaries

        # Get total count (without pagination)
        count_query = f"SELECT COUNT(*) FROM {data_type}"
        if where_clauses:
            count_query += " WHERE " + " AND ".join(where_clauses)
        cur.execute(count_query)
        total = cur.fetchone()[0]

        cur.close()
        conn.close()


        # Example data (replace with actual data retrieval)
        # data = [{"id": i, "name": f"Item {i}"} for i in range((page - 1) * page_size, min(page * page_size, 50))]
        # total = 50  # Replace with the actual total number of items

        return DashboardResponse(data=data, total=total, page=page, page_size=page_size)

    except Exception as e:
        logging.error(f"Error fetching dashboard data: {e}")
        raise HTTPException(status_code=500, detail=str(e))

# Dependency Injection
from fastapi import Depends

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
