"""
Authentication Routes Template pentru ROA2WEB FastAPI Applications

Acest modul oferă rute predefinite pentru autentificare care pot fi integrate
în orice aplicație FastAPI din ecosistemul ROA2WEB.

Endpoints disponibile:
- POST /auth/login - Autentificare utilizator
- POST /auth/refresh - Refresh access token
- POST /auth/logout - Deconectare utilizator
- GET /auth/me - Informații utilizator curent
- GET /auth/companies - Firmele utilizatorului
- GET /auth/status - Status autentificare
"""

import logging
from typing import List, Optional
from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, status, Request, Response
from fastapi.security import HTTPAuthorizationCredentials

from .models import (
    LoginRequest, TokenResponse, RefreshTokenRequest, LogoutRequest,
    CurrentUser, UserCompany, CompanyAccessRequest, CompanyAccessResponse,
    AuthError, AuthStats, CheckEmailRequest, CheckEmailResponse, ServerInfo,
    CheckIdentityRequest, CheckIdentityResponse
)
from .auth_service import auth_service, AuthenticationError
from .jwt_handler import jwt_handler
from .dependencies import (
    get_current_user, get_optional_user,
    security_required, security_optional
)
from .middleware import default_rate_limiter, RateLimiter

logger = logging.getLogger(__name__)


def create_auth_router(
    prefix: str = "/auth",
    tags: Optional[List[str]] = None,
    include_admin_routes: bool = False
) -> APIRouter:
    """
    Creează un router FastAPI cu toate rutele de autentificare
    
    Args:
        prefix: Prefix-ul pentru toate rutele
        tags: Tag-urile pentru documentația OpenAPI
        include_admin_routes: Dacă să includă rutele de administrare
        
    Returns:
        Router-ul FastAPI configurat
    """
    router = APIRouter(prefix=prefix, tags=tags or ["authentication"])

    # Rate limiter pentru check-identity/check-email: 5 requests per minut per IP
    check_identity_rate_limiter = RateLimiter(max_requests=5, time_window=60)

    @router.post("/check-identity", response_model=CheckIdentityResponse, status_code=status.HTTP_200_OK)
    async def check_identity(
        check_data: CheckIdentityRequest,
        request: Request
    ) -> CheckIdentityResponse:
        """
        Verifică dacă un email sau username există în sistem și pe câte servere Oracle (US-013).

        Acest endpoint suportă dual login:
        - Input cu '@': tratează ca email și caută în EmailServerCache
        - Input fără '@': tratează ca username și caută direct în Oracle

        SECURITATE:
        - Rate limited: max 5 requests/minut per IP
        - NU expune serverele disponibile pentru identități invalide
        - Identități invalide returnează {exists: false, servers: []}

        Args:
            check_data: Identitatea de verificat (email sau username)
            request: Request-ul HTTP (pentru rate limiting)

        Returns:
            CheckIdentityResponse cu exists, servers[] și identity_type

        Raises:
            HTTPException 429: Rate limit exceeded
        """
        # Rate limiting - 5 req/min per IP
        client_ip = request.client.host if request.client else "unknown"

        if not check_identity_rate_limiter.is_allowed(client_ip):
            reset_time = check_identity_rate_limiter.get_reset_time(client_ip)
            logger.warning(f"Rate limit exceeded for check-identity from IP {client_ip}")
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="Too many requests. Please try again later.",
                headers={
                    "X-RateLimit-Limit": "5",
                    "X-RateLimit-Remaining": "0",
                    "X-RateLimit-Reset": str(reset_time),
                    "Retry-After": str(max(1, reset_time - int(__import__('time').time())))
                }
            )

        try:
            from .email_server_cache import email_server_cache
            from backend.config import settings

            identity = check_data.identity  # Already normalized by validator
            is_email = '@' in identity

            identity_type = "email" if is_email else "username"
            logger.info(f"Check-identity request for '{identity}' (type: {identity_type}) from IP {client_ip}")

            # Get server IDs based on identity type
            if is_email:
                # Email lookup from cache
                server_ids = email_server_cache.get_servers_for_email(identity)
            else:
                # Username lookup directly from Oracle (async)
                server_ids = await email_server_cache.get_servers_for_username(identity)

            if not server_ids:
                # Identity not found - return empty response (don't expose available servers!)
                logger.info(f"Identity '{identity}' not found in any server")
                return CheckIdentityResponse(exists=False, servers=[], identity_type=identity_type)

            # Build server info list with human-readable names
            servers: List[ServerInfo] = []
            for server_id in server_ids:
                server_config = settings.get_oracle_server(server_id)
                if server_config:
                    servers.append(ServerInfo(
                        id=server_config.id,
                        name=server_config.name
                    ))
                else:
                    # Fallback if server config not found (shouldn't happen)
                    logger.warning(f"Server '{server_id}' not found in config")
                    servers.append(ServerInfo(id=server_id, name=server_id))

            logger.info(f"Identity '{identity}' found on {len(servers)} server(s): {[s.id for s in servers]}")
            return CheckIdentityResponse(exists=True, servers=servers, identity_type=identity_type)

        except Exception as e:
            logger.error(f"Error checking identity '{check_data.identity}': {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Error checking identity"
            )

    @router.post("/check-email", response_model=CheckEmailResponse, status_code=status.HTTP_200_OK)
    async def check_email(
        check_data: CheckEmailRequest,
        request: Request
    ) -> CheckEmailResponse:
        """
        Verifică dacă un email există în sistem și pe câte servere Oracle.

        DEPRECATED: Folosește /check-identity pentru suport dual email/username.
        Păstrat pentru backward compatibility.

        Args:
            check_data: Email-ul de verificat
            request: Request-ul HTTP (pentru rate limiting)

        Returns:
            CheckEmailResponse cu exists și servers[]
        """
        # Rate limiting - shared with check-identity
        client_ip = request.client.host if request.client else "unknown"

        if not check_identity_rate_limiter.is_allowed(client_ip):
            reset_time = check_identity_rate_limiter.get_reset_time(client_ip)
            logger.warning(f"Rate limit exceeded for check-email from IP {client_ip}")
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="Too many requests. Please try again later.",
                headers={
                    "X-RateLimit-Limit": "5",
                    "X-RateLimit-Remaining": "0",
                    "X-RateLimit-Reset": str(reset_time),
                    "Retry-After": str(max(1, reset_time - int(__import__('time').time())))
                }
            )

        try:
            from .email_server_cache import email_server_cache
            from backend.config import settings

            email = check_data.email.lower().strip()
            logger.info(f"Check-email request for '{email}' from IP {client_ip}")

            # Get server IDs from cache
            server_ids = email_server_cache.get_servers_for_email(email)

            if not server_ids:
                # Email not found - return empty response (don't expose available servers!)
                logger.info(f"Email '{email}' not found in any server")
                return CheckEmailResponse(exists=False, servers=[])

            # Build server info list with human-readable names
            servers: List[ServerInfo] = []
            for server_id in server_ids:
                server_config = settings.get_oracle_server(server_id)
                if server_config:
                    servers.append(ServerInfo(
                        id=server_config.id,
                        name=server_config.name
                    ))
                else:
                    # Fallback if server config not found (shouldn't happen)
                    logger.warning(f"Server '{server_id}' not found in config")
                    servers.append(ServerInfo(id=server_id, name=server_id))

            logger.info(f"Email '{email}' found on {len(servers)} server(s): {[s.id for s in servers]}")
            return CheckEmailResponse(exists=True, servers=servers)

        except Exception as e:
            logger.error(f"Error checking email '{check_data.email}': {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Error checking email"
            )

    @router.post("/login", response_model=TokenResponse, status_code=status.HTTP_200_OK)
    async def login(
        login_data: LoginRequest,
        request: Request,
        response: Response
    ) -> TokenResponse:
        """
        Autentifică un utilizator și returnează token-urile JWT

        Acest endpoint:
        - Validează credențialele utilizatorului în Oracle
        - Obține firmele la care utilizatorul are acces
        - Generează access și refresh token-uri JWT
        - Aplică rate limiting pentru securitate
        - Suportă modul multi-server (server_id opțional)

        Args:
            login_data: Datele de autentificare (username, password, server_id opțional)
            request: Request-ul HTTP (pentru rate limiting)
            response: Response-ul HTTP (pentru header-e)

        Returns:
            Token-urile JWT și informațiile utilizatorului

        Raises:
            HTTPException 400: Pentru server_id invalid
            HTTPException 401: Pentru credențiale invalide
            HTTPException 500: Pentru erori de sistem
        """
        try:
            # Log tentativa de autentificare
            client_ip = request.client.host if request.client else "unknown"
            server_info = f" on server {login_data.server_id}" if login_data.server_id else ""
            logger.info(f"Login attempt for user {login_data.username}{server_info} from IP {client_ip}")

            # Validare server_id dacă specificat (multi-server mode)
            if login_data.server_id:
                from backend.config import settings
                from shared.database.oracle_pool import oracle_pool

                # Verifică dacă serverul există în configurație
                server_config = settings.get_oracle_server(login_data.server_id)
                if not server_config:
                    logger.warning(f"Invalid server_id '{login_data.server_id}' in login request")
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail=f"Invalid server_id: '{login_data.server_id}'. Server not found in configuration."
                    )

                # Verifică dacă serverul este înregistrat în pool
                if not oracle_pool.is_server_registered(login_data.server_id):
                    logger.warning(f"Server '{login_data.server_id}' not registered in pool")
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail=f"Server '{login_data.server_id}' is not available."
                    )

            # Autentifică și creează token-urile
            success, token_response, error_message = await auth_service.authenticate_and_create_tokens(
                login_data.username,
                login_data.password,
                login_data.server_id
            )
            
            if not success:
                logger.warning(f"Failed login attempt for user {login_data.username}{server_info}: {error_message}")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail=error_message or "Authentication failed"
                )

            # token_response.user este deja populat corect de auth_service.authenticate_and_create_tokens
            # cu username-ul Oracle rezolvat (nu email-ul) și lista de firme

            # Header-e de securitate
            response.headers["X-Content-Type-Options"] = "nosniff"
            response.headers["X-Frame-Options"] = "DENY"

            logger.info(f"Successful login for user {login_data.username}{server_info}")
            return token_response
            
        except HTTPException:
            # Re-raise HTTP exceptions as-is (e.g., 401 for invalid credentials)
            raise
        except AuthenticationError as e:
            logger.error(f"Authentication error for user {login_data.username}: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=str(e)
            )
        except Exception as e:
            logger.error(f"Unexpected error during login for user {login_data.username}: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Internal authentication error"
            )
    
    @router.post("/refresh", response_model=TokenResponse, status_code=status.HTTP_200_OK)
    async def refresh_token(refresh_data: RefreshTokenRequest) -> TokenResponse:
        """
        Reîmprospătează access token-ul folosind refresh token-ul
        
        Args:
            refresh_data: Refresh token-ul valid
            
        Returns:
            Noul access token și informațiile utilizatorului
            
        Raises:
            HTTPException: Pentru refresh token-uri invalide
        """
        try:
            # Validează refresh token-ul
            token_data = jwt_handler.verify_token(refresh_data.refresh_token)
            
            if not token_data or token_data.token_type != "refresh":
                logger.warning("Invalid refresh token provided")
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid refresh token"
                )
            
            # Obține datele actualizate ale utilizatorului
            companies = await auth_service.get_user_companies(token_data.username)
            permissions = ["read", "reports"]  # Poate fi extins în viitor
            
            # Creează noul access token
            new_access_token = jwt_handler.create_access_token(
                username=token_data.username,
                companies=companies,
                user_id=token_data.user_id,
                permissions=permissions
            )
            
            # Informațiile utilizatorului
            current_user = CurrentUser(
                username=token_data.username,
                user_id=token_data.user_id,
                companies=companies,
                permissions=permissions
            )
            
            token_response = TokenResponse(
                access_token=new_access_token,
                token_type="bearer",
                expires_in=jwt_handler.access_token_expire_minutes * 60,
                user=current_user
            )
            
            logger.info(f"Token refreshed for user {token_data.username}")
            return token_response
            
        except Exception as e:
            logger.error(f"Error refreshing token: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Token refresh failed"
            )
    
    @router.post("/logout", status_code=status.HTTP_200_OK)
    async def logout(
        logout_data: Optional[LogoutRequest] = None,
        current_user: CurrentUser = Depends(get_current_user)
    ) -> dict:
        """
        Deconectează utilizatorul (invalidează token-urile)
        
        Note: În implementarea curentă, token-urile JWT sunt stateless,
        deci nu pot fi invalidate direct. În viitor poate fi implementat
        un blacklist pentru token-uri.
        
        Args:
            logout_data: Date pentru logout (opțional)
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Confirmarea deconectării
        """
        logger.info(f"User {current_user.username} logged out")
        
        # În viitor, aici se poate implementa:
        # - Adăugarea token-ului într-un blacklist
        # - Invalidarea tuturor sesiunilor utilizatorului
        # - Notificări de securitate
        
        return {
            "message": "Successfully logged out",
            "username": current_user.username,
            "logout_time": datetime.now().isoformat()
        }
    
    @router.get("/me", response_model=CurrentUser)
    async def get_current_user_info(
        current_user: CurrentUser = Depends(get_current_user)
    ) -> CurrentUser:
        """
        Returnează informațiile despre utilizatorul curent
        
        Args:
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Informațiile complete ale utilizatorului
        """
        logger.debug(f"User info requested for {current_user.username}")
        return current_user
    
    @router.get("/companies", response_model=List[UserCompany])
    async def get_user_companies(
        current_user: CurrentUser = Depends(get_current_user)
    ) -> List[UserCompany]:
        """
        Returnează lista firmelor la care utilizatorul are acces
        
        Args:
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Lista firmelor cu permisiunile asociate
        """
        try:
            # Obține firmele actualizate din baza de date
            companies = await auth_service.get_user_companies(current_user.username)
            
            user_companies = []
            for i, company_code in enumerate(companies):
                # Obține permisiunile pentru fiecare firmă
                permissions = await auth_service.get_user_permissions(
                    current_user.username, 
                    company_code
                )
                
                user_company = UserCompany(
                    code=company_code,
                    permissions=permissions,
                    is_default=(i == 0)  # Prima firmă ca default
                )
                user_companies.append(user_company)
            
            logger.debug(f"Returned {len(user_companies)} companies for user {current_user.username}")
            return user_companies
            
        except Exception as e:
            logger.error(f"Error getting companies for user {current_user.username}: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Error retrieving user companies"
            )
    
    @router.post("/check-company-access", response_model=CompanyAccessResponse)
    async def check_company_access(
        access_request: CompanyAccessRequest,
        current_user: CurrentUser = Depends(get_current_user)
    ) -> CompanyAccessResponse:
        """
        Verifică dacă utilizatorul are acces la o firmă specifică
        
        Args:
            access_request: Request-ul de verificare acces
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Răspunsul cu informații despre acces
        """
        try:
            has_access = await auth_service.validate_user_company_access(
                current_user.username,
                access_request.company_code
            )
            
            if not has_access:
                return CompanyAccessResponse(
                    has_access=False,
                    company=None,
                    missing_permissions=None
                )
            
            # Obține permisiunile pentru firmă
            permissions = await auth_service.get_user_permissions(
                current_user.username,
                access_request.company_code
            )
            
            # Verifică permisiunile cerute
            missing_permissions = []
            if access_request.required_permissions:
                missing_permissions = [
                    perm for perm in access_request.required_permissions
                    if perm not in permissions
                ]
            
            user_company = UserCompany(
                code=access_request.company_code,
                permissions=permissions
            )
            
            return CompanyAccessResponse(
                has_access=True,
                company=user_company,
                missing_permissions=missing_permissions if missing_permissions else None
            )
            
        except Exception as e:
            logger.error(f"Error checking company access: {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Error checking company access"
            )
    
    @router.get("/my-servers", response_model=dict)
    async def get_my_servers(
        current_user: CurrentUser = Depends(get_current_user)
    ) -> dict:
        """
        Returnează lista serverelor la care utilizatorul autentificat are acces (US-006).

        Acest endpoint este folosit de frontend pentru a popula dropdown-ul de server switch.
        Lookup-ul se face pe baza email-ului sau username-ului utilizatorului curent.

        Args:
            current_user: Utilizatorul curent autentificat

        Returns:
            Dict cu lista de servere: {servers: [{id: string, name: string}, ...]}
        """
        try:
            from .email_server_cache import email_server_cache
            from backend.config import settings

            logger.info(f"Get my-servers request for user '{current_user.username}'")

            # Try email lookup first (faster, from cache)
            server_ids: List[str] = []
            if current_user.email:
                server_ids = email_server_cache.get_servers_for_email(current_user.email)
                logger.debug(f"Email lookup for '{current_user.email}': {server_ids}")

            # If no email or no results, try username lookup (queries Oracle directly)
            if not server_ids:
                server_ids = await email_server_cache.get_servers_for_username(current_user.username)
                logger.debug(f"Username lookup for '{current_user.username}': {server_ids}")

            # Build server info list with human-readable names
            servers: List[ServerInfo] = []
            for server_id in server_ids:
                server_config = settings.get_oracle_server(server_id)
                if server_config:
                    servers.append(ServerInfo(
                        id=server_config.id,
                        name=server_config.name
                    ))
                else:
                    # Fallback if server config not found
                    logger.warning(f"Server '{server_id}' not found in config")
                    servers.append(ServerInfo(id=server_id, name=server_id))

            logger.info(f"User '{current_user.username}' has access to {len(servers)} server(s)")
            return {"servers": [s.model_dump() for s in servers]}

        except Exception as e:
            logger.error(f"Error getting servers for user '{current_user.username}': {str(e)}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Error retrieving user servers"
            )

    @router.get("/status")
    async def get_auth_status(
        current_user: Optional[CurrentUser] = Depends(get_optional_user)
    ) -> dict:
        """
        Returnează statusul de autentificare (endpoint public)
        
        Args:
            current_user: Utilizatorul curent (opțional)
            
        Returns:
            Statusul de autentificare
        """
        if current_user:
            return {
                "authenticated": True,
                "username": current_user.username,
                "companies_count": len(current_user.companies),
                "permissions": current_user.permissions
            }
        else:
            return {
                "authenticated": False,
                "username": None,
                "companies_count": 0,
                "permissions": []
            }
    
    # Rute de administrare (opționale)
    if include_admin_routes:
        
        @router.get("/admin/stats", response_model=AuthStats)
        async def get_auth_stats(
            current_user: CurrentUser = Depends(get_current_user)
        ) -> AuthStats:
            """
            Returnează statistici despre sistemul de autentificare
            
            Necesită permisiuni de admin.
            """
            # Verifică permisiuni admin
            if "admin" not in current_user.permissions:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Admin permissions required"
                )
            
            cache_stats = auth_service.get_cache_stats()
            
            return AuthStats(
                total_users=1,  # Placeholder - poate fi implementat
                active_sessions=1,  # Placeholder - poate fi implementat
                cache_hit_ratio=cache_stats.get('cache_hit_ratio', 0),
                last_cleanup=datetime.now()
            )
        
        @router.post("/admin/refresh-cache")
        async def refresh_user_cache(
            username: Optional[str] = None,
            current_user: CurrentUser = Depends(get_current_user)
        ) -> dict:
            """
            Reîmprospătează cache-ul utilizatorilor
            
            Necesită permisiuni de admin.
            """
            if "admin" not in current_user.permissions:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Admin permissions required"
                )
            
            if username:
                success = await auth_service.refresh_user_data(username)
                return {
                    "message": f"Cache refreshed for user {username}",
                    "success": success
                }
            else:
                auth_service.clear_cache()
                return {"message": "All user cache cleared"}
    
    return router


# Router implicit pentru folosire rapidă
auth_router = create_auth_router()

# Router cu rute de admin incluse
auth_router_with_admin = create_auth_router(include_admin_routes=True)