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

from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel
import redis

from modules.logger_tool import initialise_logger

logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)

router = APIRouter()


def _redis_client() -> redis.Redis:
    host = os.getenv('REDIS_HOST', '127.0.0.1')
    port = int(os.getenv('REDIS_PORT', '6379'))
    return redis.Redis(host=host, port=port, decode_responses=True)


@router.get("/ping")
def ping() -> Dict[str, Any]:
    try:
        r = _redis_client()
        pong = r.ping()
        return {"ok": True, "pong": pong}
    except Exception as e:
        logger.error(f"Redis ping failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis ping failed: {e}")


@router.get("/info")
def info(section: Optional[str] = Query(None, description="Optional INFO section, e.g. memory, server, clients, keyspace")) -> Dict[str, Any]:
    try:
        r = _redis_client()
        data = r.info(section) if section else r.info()
        return {"ok": True, "info": data}
    except Exception as e:
        logger.error(f"Redis info failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis info failed: {e}")


@router.get("/scan")
def scan(cursor: int = 0, pattern: Optional[str] = None, count: int = Query(100, ge=1, le=10000)) -> Dict[str, Any]:
    try:
        r = _redis_client()
        next_cursor, keys = r.scan(cursor=cursor, match=pattern, count=count)
        return {"ok": True, "cursor": next_cursor, "keys": keys, "count": len(keys)}
    except Exception as e:
        logger.error(f"Redis scan failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis scan failed: {e}")


@router.get("/keys")
def list_keys(pattern: str = Query("*", description="Glob-style pattern to match keys"), limit: int = Query(200, ge=1, le=5000)) -> Dict[str, Any]:
    try:
        r = _redis_client()
        keys: List[str] = []
        cursor = 0
        while True and len(keys) < limit:
            cursor, batch = r.scan(cursor=cursor, match=pattern, count=min(1000, limit - len(keys)))
            keys.extend(batch)
            if cursor == 0:
                break
        return {"ok": True, "keys": keys[:limit], "count": len(keys[:limit])}
    except Exception as e:
        logger.error(f"Redis keys failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis keys failed: {e}")


@router.get("/key")
def get_key(key: str) -> Dict[str, Any]:
    try:
        r = _redis_client()
        t = r.type(key)
        value: Any = None
        if t == 'string':
            value = r.get(key)
        elif t == 'hash':
            value = r.hgetall(key)
        elif t == 'list':
            value = r.lrange(key, 0, 99)
        elif t == 'set':
            value = list(r.smembers(key))
        elif t == 'zset':
            value = r.zrange(key, 0, 99, withscores=True)
        ttl = r.ttl(key)
        return {"ok": True, "type": t, "ttl": ttl, "value": value}
    except Exception as e:
        logger.error(f"Redis get key failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis get key failed: {e}")


class DeleteKeysBody(BaseModel):
    keys: Optional[List[str]] = None
    pattern: Optional[str] = None


@router.post("/delete")
def delete_keys(body: DeleteKeysBody) -> Dict[str, Any]:
    if not body.keys and not body.pattern:
        raise HTTPException(status_code=400, detail="Provide 'keys' or 'pattern'")
    try:
        r = _redis_client()
        to_delete: List[str] = body.keys or []
        if body.pattern:
            # Resolve pattern to keys via SCAN to avoid blocking
            keys: List[str] = []
            cursor = 0
            while True:
                cursor, batch = r.scan(cursor=cursor, match=body.pattern, count=1000)
                keys.extend(batch)
                if cursor == 0:
                    break
            to_delete.extend(keys)
        deleted = 0
        for k in set(to_delete):
            try:
                deleted += r.delete(k)
            except Exception:
                pass
        return {"ok": True, "deleted": deleted}
    except Exception as e:
        logger.error(f"Redis delete failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis delete failed: {e}")


@router.post("/flushdb")
def flush_db(confirm: bool = Query(False, description="Must be true to flush the current DB")) -> Dict[str, Any]:
    if not confirm:
        raise HTTPException(status_code=400, detail="Set confirm=true to flush the current Redis DB")
    try:
        r = _redis_client()
        r.flushdb()
        return {"ok": True, "message": "Flushed current Redis DB"}
    except Exception as e:
        logger.error(f"Redis FLUSHDB failed: {e}")
        raise HTTPException(status_code=500, detail=f"Redis FLUSHDB failed: {e}")


@router.get("/queues")
def queue_stats() -> Dict[str, Any]:
    """Report queue sizes and basic counters used by DocumentProcessingQueue."""
    try:
        r = _redis_client()
        keys = {
            "high": "queue:high",
            "normal": "queue:normal",
            "low": "queue:low",
            "processing": "processing",
            "dead_letter": "dead_letter",
            "metrics": "metrics",
        }
        data = {
            "queues": {
                "high": r.llen(keys["high"]),
                "normal": r.llen(keys["normal"]),
                "low": r.llen(keys["low"]),
            },
            "processing": r.hgetall(keys["processing"]),
            "dead_letter": r.llen(keys["dead_letter"]),
            "metrics": r.hgetall(keys["metrics"]),
        }
        return {"ok": True, **data}
    except Exception as e:
        logger.error(f"Queue stats failed: {e}")
        raise HTTPException(status_code=500, detail=f"Queue stats failed: {e}")

