"""
FastAPI Dependencies for Authentication and Database
"""
import json
import logging
from datetime import datetime
from fastapi import Depends, HTTPException, Header, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from typing import List, Optional, Union

from app.core.database import get_db
from app.core.config import settings
from app.models.user import User
from app.models.agent import AccessManage, AgentManagement

logger = logging.getLogger(__name__)

# Security scheme for Bearer token
security = HTTPBearer(auto_error=False)


def _normalize_agent_id_header(agent_id: Optional[Union[str, List[str]]]) -> List[str]:
    """Parse X-Agent-Id header: can be a string (single id or JSON array like '[\"1\", \"4\"]'), or a list (e.g. ['[\"1\", \"4\"]'] from header)."""
    def parse_value(v):
        if v is None or not str(v).strip():
            return []
        s = str(v).strip()
        if s.startswith("["):
            try:
                parsed = json.loads(s)
                if isinstance(parsed, list):
                    return [str(a).strip() for a in parsed if a is not None and str(a).strip()]
            except json.JSONDecodeError:
                pass
        return [s]

    if agent_id is None:
        return []
    if isinstance(agent_id, list):
        out = []
        for a in agent_id:
            out.extend(parse_value(a))
        return out
    return parse_value(agent_id)


def get_current_user(
    token: Optional[str] = Header(None, alias="X-Token"),
    agent_id: Optional[Union[str, List[str]]] = Header(None, alias="X-Agent-Id"),
    authorization: Optional[HTTPAuthorizationCredentials] = Depends(security),
    db: Session = Depends(get_db)
) -> User:
    """
    Validate user token and return current user.
    
    When BYPASS_AUTH is enabled (for testing):
    - Completely skips authentication
    - Returns default admin user (user_id=1) without any validation
    - No token or headers required
    
    When BYPASS_AUTH is disabled (normal operation):
    - Token can be provided in two ways:
      1. X-Token header: X-Token: <token>
      2. Authorization header: Authorization: Bearer <token>
    
    Args:
        token: Token from X-Token header (ignored when bypass is enabled)
        authorization: Bearer token from Authorization header (ignored when bypass is enabled)
        db: Database session
        
    Returns:
        User: Current authenticated user
        
    Raises:
        HTTPException: If token is invalid or user is not active
    """
    # Check if authentication bypass is enabled
    user_token = None
    if settings.BYPASS_AUTH:
        logger.warning("⚠️  AUTH BYPASS ACTIVE - All authentication is disabled. This should only be used for testing!")
        if token:
            user_token = token.strip() if token else None
        elif agent_id is not None:
            aid_list = _normalize_agent_id_header(agent_id)
            user_token = aid_list[0] if aid_list else None
        
        # Return default admin user (user_id=1) without any validation
        user = db.query(User).filter(User.id == 1).first()
        
        if not user:
            # If admin user doesn't exist, create a minimal user object or raise error
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Cannot bypass authentication."
            )
        
        logger.info(f"AUTH BYPASS: Returning default admin user {user.id} ({user.email}) without any authentication checks")
        return user
    
    # Normal authentication flow - validate token
    # Extract token from either header

    if token:
        user_token = token.strip() if token else None
    elif authorization:
        user_token = authorization.credentials.strip() if authorization.credentials else None
    
    if not user_token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid user"
        )

    # Query user by token (trim whitespace from database token as well)
    user = db.query(User).filter(User.user_token == user_token).first()

    if not user:
        # Log for debugging (don't expose token in production)
        logger.warning(f"Token validation failed. Token provided: {user_token}")
        # Check if token exists with different casing or whitespace
        all_users = db.query(User).all()
        token_found = False
        for u in all_users:
            if u.user_token and u.user_token.strip() == user_token:
                token_found = True
                logger.warning(f"Token found but with whitespace differences for user {u.id}")
                break

        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid user"
        )

    # Check if user is active
    if user.status.lower() != "active":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Invalid user"
        )
    # If agent_id header(s) provided, validate user has assigned access and license not expired (same logic as get_user_agents in auth)
    agent_ids = _normalize_agent_id_header(agent_id) if agent_id is not None else []
    if agent_ids:
        user_id_str = str(user.id)
        allowed_agent_ids = set()
        if user_id_str == "1":
            # User 1: admin, has access to all agents in agent_management
            allowed_agent_ids = {
                str(a.agent_id) for a in db.query(AgentManagement).all()
            }
        else:
            # Other users: from access_manage, only non-expired
            access_list = db.query(AccessManage).filter(AccessManage.user_id == user_id_str).all()
            for access in access_list:
                try:
                    end_date = datetime.fromisoformat(access.end_date)
                    if end_date < datetime.now():
                        continue
                except Exception:
                    pass
                allowed_agent_ids.add(str(access.agent_id))
        requested_set = set(agent_ids)
        if not requested_set.issubset(allowed_agent_ids):
            missing = requested_set - allowed_agent_ids
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid agent" if len(missing) == 1 else "Invalid or expired agent access for one or more agents"
            )

    return user


def get_current_user_optional(
    token: Optional[str] = Header(None, alias="X-Token"),
    authorization: Optional[HTTPAuthorizationCredentials] = Depends(security),
    db: Session = Depends(get_db)
) -> Optional[User]:
    """
    Optional token validation - returns None if no token provided.
    Useful for endpoints that work with or without authentication.
    
    Args:
        token: Token from X-Token header
        authorization: Bearer token from Authorization header
        db: Database session
        
    Returns:
        Optional[User]: Current user if token is valid, None otherwise
    """
    try:
        return get_current_user(token=token, authorization=authorization, db=db)
    except HTTPException:
        return None
