"""
Authentication and user management API endpoints.
Handles registration, login, logout, and user profile management.
"""

import logging
from typing import List
from functools import lru_cache
from datetime import datetime, timedelta

from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session

from config import settings
from database import get_db
from dependencies import get_current_admin_user, get_current_user
from models import User
from schemas import (
    AdminUserCreate, AdminUserUpdate, GoogleAuthRequest, GoogleAuthCodeRequest,
    GoogleOAuthUrlResponse, MessageResponse, PasswordChange,
    TokenResponse, UserListResponse, UserLogin, UserRegister, UserResponse,
    UserUpdate
)
from services import AuthService, UserService, GoogleAuthService

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/auth", tags=["authentication"])

# Cache for admin data
_admin_cache = {}
_cache_expiry = {}

def _is_cache_valid(cache_key: str, max_age_minutes: int = None) -> bool:
    """Check if cache entry is still valid."""
    if max_age_minutes is None:
        max_age_minutes = settings.CACHE_TTL_MINUTES
    
    if cache_key not in _cache_expiry:
        return False
    return datetime.now() < _cache_expiry[cache_key]

def _set_cache(cache_key: str, data: dict, max_age_minutes: int = None):
    """Set cache entry with expiry."""
    if max_age_minutes is None:
        max_age_minutes = settings.CACHE_TTL_MINUTES
    
    _admin_cache[cache_key] = data
    _cache_expiry[cache_key] = datetime.now() + timedelta(minutes=max_age_minutes)

# Admin endpoints for database management
@router.get("/admin/tables", dependencies=[Depends(get_current_admin_user)])
async def get_available_tables(db: Session = Depends(get_db)):
    """Get list of available database tables for admin panel."""
    cache_key = "available_tables"
    
    # Check cache first
    if _is_cache_valid(cache_key, max_age_minutes=10):
        return _admin_cache[cache_key]
    
    from sqlalchemy import inspect
    inspector = inspect(db.bind)
    tables = inspector.get_table_names()
    # Filter to show only our application tables
    app_tables = [table for table in tables if table in [
        'users', 'payments', 'layers', 'layer_translations', 'pois', 'poi_translations'
    ]]
    
    result = {"tables": app_tables}
    _set_cache(cache_key, result, max_age_minutes=10)
    return result

@router.get("/admin/table/{table_name}", dependencies=[Depends(get_current_admin_user)])
async def get_table_data(table_name: str, db: Session = Depends(get_db)):
    """Get data from specific table."""
    from sqlalchemy import text
    
    # Security check - only allow specific tables
    allowed_tables = ['users', 'payments', 'layers', 'layer_translations', 'pois', 'poi_translations']
    if table_name not in allowed_tables:
        raise HTTPException(status_code=400, detail="Table not allowed")
    
    try:
        # Get table structure using SQLAlchemy inspector (PostgreSQL compatible)
        from sqlalchemy import inspect
        inspector = inspect(db.bind)
        table_columns = inspector.get_columns(table_name)
        columns = [col['name'] for col in table_columns]
        
        # Get table data
        result = db.execute(text(f"SELECT * FROM {table_name} LIMIT 100"))
        rows = result.fetchall()
        
        # Convert to list of dictionaries
        data = []
        for row in rows:
            if hasattr(row, '_mapping'):
                # SQLAlchemy 2.x style - row has _mapping attribute
                data.append(dict(row._mapping))
            elif hasattr(row, '_asdict'):
                # Named tuple style
                data.append(row._asdict())
            else:
                # Fallback - create dict manually using column names
                row_dict = {}
                for i, col_name in enumerate(columns):
                    if i < len(row):
                        row_dict[col_name] = row[i]
                data.append(row_dict)
        
        return {
            "table_name": table_name,
            "columns": columns,
            "data": data,
            "total_rows": len(data)
        }
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error fetching table data: {str(e)}")

@router.post("/admin/refresh-tables", dependencies=[Depends(get_current_admin_user)])
async def refresh_tables(db: Session = Depends(get_db)):
    """Refresh table structures and data."""
    try:
        # This could include database migrations or cache refresh
        return {"message": "Tables refreshed successfully"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error refreshing tables: {str(e)}")

from typing import Dict, Any

@router.put("/admin/table/{table_name}/{record_id}", dependencies=[Depends(get_current_admin_user)])
async def update_table_record(
    table_name: str, 
    record_id: int, 
    update_data: Dict[str, Any],
    db: Session = Depends(get_db)
):
    """Update a specific record in a table."""
    from sqlalchemy import text
    
    # Логирование входящих данных
    logger.info(f"📨 Update request for {table_name} record {record_id}")
    logger.info(f"📋 Update data: {update_data}")
    logger.info(f"📊 Data types: {[(k, type(v).__name__) for k, v in update_data.items()]}")
    
    # Security check - only allow specific tables
    allowed_tables = ['users', 'payments', 'layers', 'layer_translations', 'pois', 'poi_translations']
    if table_name not in allowed_tables:
        raise HTTPException(status_code=400, detail="Table not allowed")
    
    try:
        # Get table structure to validate columns (PostgreSQL compatible)
        from sqlalchemy import inspect
        inspector = inspect(db.bind)
        table_columns = inspector.get_columns(table_name)
        columns = [col['name'] for col in table_columns]
        
        # Filter update_data to only include valid columns and exclude id
        valid_updates = {}
        invalid_columns = []
        
        # update_data уже является dict
        for k, v in update_data.items():
            if k == 'id':
                continue  # Skip id field
            elif k not in columns:
                invalid_columns.append(k)
            else:
                valid_updates[k] = v
        
        if invalid_columns:
            raise HTTPException(
                status_code=400, 
                detail=f"Invalid columns: {', '.join(invalid_columns)}. Allowed columns: {', '.join([c for c in columns if c != 'id'])}"
            )
        
        if not valid_updates:
            raise HTTPException(status_code=400, detail="No valid fields to update")
        
        # Build UPDATE query
        set_clauses = []
        params = {'record_id': record_id}
        
        for column, value in valid_updates.items():
            set_clauses.append(f"{column} = :{column}")
            params[column] = value
        
        query = f"UPDATE {table_name} SET {', '.join(set_clauses)} WHERE id = :record_id"
        
        # Execute update
        result = db.execute(text(query), params)
        db.commit()
        
        if result.rowcount == 0:
            raise HTTPException(status_code=404, detail="Record not found")
        
        return {"message": "Record updated successfully", "updated_fields": list(valid_updates.keys())}
        
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        
        # Handle specific database errors
        error_str = str(e)
        
        # Check for MySQL/MariaDB duplicate entry error
        if "Duplicate entry" in error_str and "for key" in error_str:
            # Extract field name from error message
            if "users.email" in error_str:
                raise HTTPException(
                    status_code=422, 
                    detail={
                        "type": "validation_error",
                        "message": "Email уже используется другим пользователем",
                        "field": "email",
                        "code": "duplicate_value"
                    }
                )
            elif "users.name" in error_str:
                raise HTTPException(
                    status_code=422,
                    detail={
                        "type": "validation_error", 
                        "message": "Имя пользователя уже занято",
                        "field": "name",
                        "code": "duplicate_value"
                    }
                )
            else:
                # Generic duplicate key error
                raise HTTPException(
                    status_code=422,
                    detail={
                        "type": "validation_error",
                        "message": "Значение должно быть уникальным",
                        "code": "duplicate_value"
                    }
                )
        
        # Check for foreign key constraint errors
        elif "foreign key constraint" in error_str.lower() or "cannot add or update a child row" in error_str.lower():
            raise HTTPException(
                status_code=422,
                detail={
                    "type": "validation_error",
                    "message": "Связанная запись не найдена",
                    "code": "foreign_key_error"
                }
            )
        
        # Check for NOT NULL constraint errors
        elif "cannot be null" in error_str.lower() or "not null constraint" in error_str.lower():
            raise HTTPException(
                status_code=422,
                detail={
                    "type": "validation_error",
                    "message": "Обязательное поле не может быть пустым",
                    "code": "required_field"
                }
            )
        
        # Check for data type errors
        elif "invalid input syntax" in error_str.lower() or "incorrect" in error_str.lower():
            raise HTTPException(
                status_code=422,
                detail={
                    "type": "validation_error",
                    "message": "Неверный формат данных",
                    "code": "invalid_format"
                }
            )
        
        # Generic database error
        else:
            logger.error(f"Database error updating {table_name} record {record_id}: {error_str}")
            raise HTTPException(
                status_code=500, 
                detail=f"Ошибка обновления записи в базе данных"
            )


# Authentication endpoints
@router.post("/register", response_model=MessageResponse)
async def register(user_data: UserRegister, db: Session = Depends(get_db)) -> MessageResponse:
    """Register new user (always created as partner)."""
    result = AuthService.register_user(user_data, db)
    return MessageResponse(**result)


@router.post("/login", response_model=TokenResponse)
async def login(user_data: UserLogin, db: Session = Depends(get_db)) -> TokenResponse:
    """User login."""
    return AuthService.login_user(user_data, db)


@router.post("/logout", response_model=MessageResponse)
async def logout(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
) -> MessageResponse:
    """User logout."""
    result = AuthService.logout_user(current_user)
    return MessageResponse(**result)


# User profile endpoints
@router.get("/me", response_model=UserResponse)
async def get_current_user_info(
    current_user: User = Depends(get_current_user)
) -> UserResponse:
    """Get current user information."""
    return UserResponse.from_orm(current_user)


@router.put("/me", response_model=UserResponse)
async def update_user_info(
    user_data: UserUpdate,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
) -> UserResponse:
    """Update user information."""
    return UserService.update_user_profile(user_data, current_user, db)


@router.post("/change-password", response_model=MessageResponse)
async def change_password(
    password_data: PasswordChange,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
) -> MessageResponse:
    """Change user password."""
    result = AuthService.change_password(password_data, current_user, db)
    return MessageResponse(**result)


# Admin endpoints
@router.get("/admin/users", response_model=UserListResponse)
async def get_all_users(
    skip: int = 0,
    limit: int = 100,
    current_admin: User = Depends(get_current_admin_user),
    db: Session = Depends(get_db)
) -> UserListResponse:
    """Get all users (admin only)."""
    return UserService.get_all_users(db, skip, limit)


@router.get("/admin/users/{user_id}", response_model=UserResponse)
async def get_user_by_id(
    user_id: int,
    current_admin: User = Depends(get_current_admin_user),
    db: Session = Depends(get_db)
) -> UserResponse:
    """Get user by ID (admin only)."""
    user = UserService.get_user_by_id(user_id, db)
    return UserResponse.from_orm(user)


@router.post("/admin/users", response_model=UserResponse)
async def create_user(
    user_data: AdminUserCreate,
    current_admin: User = Depends(get_current_admin_user),
    db: Session = Depends(get_db)
) -> UserResponse:
    """Create new user (admin only)."""
    return UserService.create_user_by_admin(user_data, db)


@router.put("/admin/users/{user_id}", response_model=UserResponse)
async def update_user(
    user_id: int,
    user_data: AdminUserUpdate,
    current_admin: User = Depends(get_current_admin_user),
    db: Session = Depends(get_db)
) -> UserResponse:
    """Update user (admin only)."""
    return UserService.update_user_by_admin(user_id, user_data, db)


@router.delete("/admin/users/{user_id}", response_model=MessageResponse)
async def delete_user(
    user_id: int,
    current_admin: User = Depends(get_current_admin_user),
    db: Session = Depends(get_db)
) -> MessageResponse:
    """Delete user (admin only)."""
    result = UserService.delete_user_by_admin(user_id, current_admin, db)
    return MessageResponse(**result)


# Google OAuth endpoints
@router.get("/google/url", response_model=GoogleOAuthUrlResponse)
async def get_google_oauth_url() -> GoogleOAuthUrlResponse:
    """Get Google OAuth authorization URL."""
    auth_url = GoogleAuthService.get_oauth_url()
    return GoogleOAuthUrlResponse(auth_url=auth_url)


@router.post("/google/callback", response_model=TokenResponse)
async def google_oauth_callback(
    auth_data: GoogleAuthCodeRequest,
    db: Session = Depends(get_db)
) -> TokenResponse:
    """Handle Google OAuth callback with authorization code."""
    return GoogleAuthService.handle_oauth_callback(auth_data, db, AuthService)


@router.get("/google/callback", response_model=TokenResponse)
async def google_oauth_callback_get(
    code: str = Query(..., description="Authorization code from Google"),
    db: Session = Depends(get_db)
) -> TokenResponse:
    """Handle Google OAuth callback (GET) with code query param."""
    logger.info("🔄 Google OAuth GET callback received")
    auth_data = GoogleAuthCodeRequest(code=code)
    return GoogleAuthService.handle_oauth_callback(auth_data, db, AuthService)

@router.post("/google/token", response_model=TokenResponse)
async def google_token_auth(
    auth_data: GoogleAuthRequest,
    db: Session = Depends(get_db)
) -> TokenResponse:
    """Authenticate user with Google ID token (for frontend)."""
    return GoogleAuthService.authenticate_with_token(auth_data, db, AuthService) 