from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import and_, or_, select, func
from typing import Optional
import logging
import math
import json
from pydantic import ValidationError
import traceback
from app.utils.responses import custom_response

from app.core.security import validate_token
from app.db.session import get_db
from app.models.category import Category
from app.models.tattoo_style import TattooStyle
from app.api.v1.crypto_api import decrypt_data
from pydantic import BaseModel
from app.schemas.category import CategoryQuery, CategoryIdRequest
from app.core.logging import logger
from sqlalchemy.orm import aliased
import math

# Alias for tattoo_styles table
ts = aliased(TattooStyle)


router = APIRouter()


# ---------- Get Categories (POST) ----------
@router.post("/categories")
async def get_categories(
    request: Request,
    db: AsyncSession = Depends(get_db),
    token_data: dict = Depends(validate_token)
):
    logger.info("Received request for /categories endpoint")
    try:
        # Step 1: Authorization check
        if token_data.get('code') != 200:
            logger.warning(f"Unauthorized access attempt to /categories. Message: {token_data.get('message', 'N/A')}")
            return custom_response(code=token_data.get('code', 500), message=token_data.get('message', 'Unauthorized'))
        
        # Step 2: Read raw encrypted data
        try:
            encrypted_data = await request.body()
            logger.debug(f"Encrypted request body received: {encrypted_data}")
        except Exception as e:
            logger.error(f"Failed to read request body: {str(e)}")
            return custom_response(code=400, message=f"Failed to read request body: {str(e)}")

        # Step 3: Decrypt data
        decrypted_data = decrypt_data(encrypted_data)
        
        # Step 4: Parse decrypted JSON
        try:
            decrypted_json = decrypted_data
            logger.debug(f"Decrypted JSON request payload: {decrypted_json}")
        except json.JSONDecodeError:
            logger.warning("Invalid JSON after decryption. Payload: %s", decrypted_data)
            return custom_response(code=400, message="Invalid JSON after decryption")
        
        # Step 5: Validate schema
        try:
            params = CategoryQuery.model_validate(decrypted_json)
        except ValidationError as ve:
            logger.warning(f"Validation failed for category query: {ve.errors()}")
            return custom_response(422, data=ve.errors(), message="Validation failed")
            
        page = max(1, params.page)
        limit = min(100, max(1, params.limit))
        tag = params.tag.lower() if params.tag else 'all'
        search = params.search.strip() if params.search else None

        logger.info(f"Fetching categories with parameters: page={page}, limit={limit}, tag={tag}, search={search}")
        
        # Build query conditions dynamically
        conditions = [Category.is_active.is_(True)]
        # ... (rest of your query building logic)

        if tag == 'free':
            conditions.append(Category.is_free.is_(True))
        elif tag == 'popular':
            conditions.append(Category.is_popular.is_(True))
        elif tag == 'pro':
            conditions.append(Category.is_free.is_(False))

        if search:
            term = f"%{search}%"
            conditions.append(or_(Category.name.ilike(term)))
        
        stmt = (
            select(
                Category.category_id,
                Category.name,
                Category.description,
                Category.thumbnail_url.label("category_thumbnail"),
                Category.is_active,
                Category.is_free,
                Category.is_popular,
                Category.sort_order,
                func.min(ts.thumbnail_url).label("thumbnail_style_url"),  # first thumbnail per category
                func.count().over().label("total_count")  # total rows for pagination
            )
            .outerjoin(ts, and_(Category.category_id == ts.category_id, ts.is_thumbnail == 1))
            .where(and_(*conditions))
            .group_by(Category.category_id)
            .order_by(Category.name.asc())
            .offset((page - 1) * limit)
            .limit(limit)
        )

        result = await db.execute(stmt)
        rows = result.all()

        # Pagination info
        total_count = rows[0].total_count if rows else 0
        total_pages = math.ceil(total_count / limit) if total_count > 0 else 0
        has_next = page < total_pages

        # Serialize
        items = [
            {
                "category_id": row.category_id,
                "name": row.name,
                "description": row.description,
                "category_thumbnail": row.category_thumbnail,
                "is_active": row.is_active,
                "is_free": row.is_free,
                "is_popular": row.is_popular,
                "total_category": row.sort_order,
            }
            for row in rows
        ]
        
        if not items:
            logger.info("No categories found for the given query.")
            # return custom_response(code=404, data=[], message="No categories found", page=page, limit=limit, total_count=total_count, total_pages=total_pages, has_next=has_next)
            data = {'categories': items,"page":page, "limit":limit, "total_count":total_count, "total_pages":total_pages, "has_next":has_next}

            return custom_response(code=404, data=data, message="No categories found")
        
        logger.info(f"Successfully retrieved {len(items)} categories.")
        data = {'details': items,"page":page, "limit":limit, "total_count":total_count, "total_pages":total_pages, "has_next":has_next}
        return custom_response(200, data=data, message="Success")
        
    except Exception as e:
        logger.error(f"Internal server error in /categories: {str(e)}", exc_info=True)
        return custom_response(code=500, message=f"Internal server error: {str(e)}")

# ---------- Get Category by ID (POST) ----------
@router.post("/categories/imges")
async def get_category_by_id(
    request: Request,
    db: AsyncSession = Depends(get_db),
    token_data: dict = Depends(validate_token)
):
    logger.info("Received request for /categories/imges endpoint")
    try:
        # Step 1: Authorization check
        if token_data.get('code') != 200:
            logger.warning(f"Unauthorized access attempt to /categories/imges. Message: {token_data.get('message', 'N/A')}")
            return custom_response(code=token_data.get('code', 500), message=token_data.get('message', 'Unauthorized'))

        # Step 2: Read raw encrypted data
        try:
            encrypted_data = await request.body()
            logger.debug(f"Encrypted request body received for /categories/imges: {encrypted_data}")
        except Exception as e:
            logger.error(f"Failed to read request body for /categories/imges: {str(e)}")
            return custom_response(code=400, message=f"Failed to read request body: {str(e)}")

        # Step 3: Decrypt data
        decrypted_data = decrypt_data(encrypted_data)

        # Step 4: Parse decrypted JSON
        try:
            decrypted_json = json.loads(decrypted_data)
            logger.debug(f"Decrypted JSON payload for /categories/imges: {decrypted_json}")
        except json.JSONDecodeError:
            logger.warning("Invalid JSON after decryption for /categories/imges. Payload: %s", decrypted_data)
            return custom_response(code=400, message="Invalid JSON after decryption")

        # Step 5: Validate schema
        try:
            body = CategoryIdRequest.model_validate(decrypted_json)
        except ValidationError as ve:
            logger.warning(f"Validation failed for category ID request: {ve.errors()}")
            return custom_response(code=422, data=ve.errors(), message="Validation failed")

        logger.info(f"Fetching category with ID: {body.category_id}")
        
        # Step 6: DB query logic
        stmt = select(Category).where(
            and_(
                Category.category_id == body.category_id,
                Category.is_active.is_(True)
            )
        )
        result = await db.execute(stmt)
        category = result.scalar_one_or_none()

        if not category:
            logger.warning(f"Category with ID {body.category_id} not found or is inactive.")
            return custom_response(code=404, data=[], message="Category not found")

        # Step 7: Serialize and return response
        category_data = {
            "category_id": category.category_id,
            "name": category.name,
            "description": category.description,
            "is_active": category.is_active,
            "is_free": category.is_free,
            "thumbnail_url": category.thumbnail_url,
            "is_popular": category.is_popular,
            "sort_order": category.sort_order,
        }

        logger.info(f"Successfully retrieved category with ID: {body.category_id}")
        return custom_response(code=200, data=category_data, message="Success")

    except Exception as e:
        logger.error(f"Internal server error in /categories/imges: {str(e)}", exc_info=True)
        return custom_response(code=500, message="Internal server error")