from fastapi import FastAPI, Depends, HTTPException, status, Request, Form, File, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from starlette.middleware.sessions import SessionMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from sqlalchemy import create_engine, Column, Integer, String, Text, Enum, DateTime, ForeignKey, JSON
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, Session
from sqlalchemy.sql import func
from typing import Optional, List, Dict, Any, Union
from datetime import datetime, timedelta
from pydantic import BaseModel, EmailStr, Field
import jwt
import yaml
import os
import json
import time  # Add this import for sleep functionality
from passlib.context import CryptContext


# Load configuration
with open('db_config.yaml') as f:
    db_config = yaml.safe_load(f)

# Database setup
DATABASE_URL = f"mysql+pymysql://{db_config['mysql_user']}:{db_config['mysql_password']}@{db_config['mysql_host']}/{db_config['mysql_db']}"
engine = create_engine(
    DATABASE_URL,
    pool_pre_ping=True,  # Enables connection testing before usage
    pool_recycle=3600,   # Recycle connections after 1 hour
    pool_size=10,        # Maximum number of connections in the pool
    max_overflow=20,     # Maximum number of connections that can be created beyond pool_size
    echo=False
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# Define SQLAlchemy models (similar to previous models)
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String(100), unique=True, nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    password = Column(String(255), nullable=False)
    first_name = Column(String(100))
    last_name = Column(String(100))
    role = Column(Enum('client', 'admin', name='role_types'), default='client')
    created_at = Column(DateTime, default=func.now())
    
    # Relationships
    blog_posts = relationship('BlogPost', back_populates='author')
    questionnaires = relationship('Questionnaire', back_populates='user')

class BlogPost(Base):
    __tablename__ = 'blog_posts'
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    content = Column(Text, nullable=False)
    author_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    image_url = Column(String(255))
    created_at = Column(DateTime, default=func.now())
    updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
    
    # Relationships
    author = relationship('User', back_populates='blog_posts')

class Questionnaire(Base):
    __tablename__ = 'questionnaires'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    title = Column(String(200))
    data = Column(JSON)
    status = Column(Enum('draft', 'submitted', 'reviewed', name='status_types'), default='draft')
    created_at = Column(DateTime, default=func.now())
    updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
    
    # Relationships
    user = relationship('User', back_populates='questionnaires')

# Create tables
Base.metadata.create_all(bind=engine)

# Define Pydantic models for request/response
class UserBase(BaseModel):
    username: str
    email: EmailStr
    first_name: Optional[str] = None
    last_name: Optional[str] = None

class UserCreate(UserBase):
    password: str
    confirm_password: str

class UserLogin(BaseModel):
    username: str
    password: str

class UserResponse(UserBase):
    id: int
    role: str
    created_at: datetime
    
    class Config:
        from_attributes = True

class TokenData(BaseModel):
    username: str
    user_id: int
    role: str

class Token(BaseModel):
    access_token: str
    token_type: str

class BlogPostBase(BaseModel):
    title: str
    content: str
    image_url: Optional[str] = None

class BlogPostCreate(BlogPostBase):
    pass

class BlogPostResponse(BlogPostBase):
    id: int
    author_id: int
    created_at: datetime
    updated_at: datetime
    
    class Config:
        from_attributes = True

class QuestionnaireBase(BaseModel):
    title: str
    status: str = "draft"
    data: Dict[str, Any]

class QuestionnaireCreate(QuestionnaireBase):
    pass

class QuestionnaireResponse(QuestionnaireBase):
    id: int
    user_id: int
    created_at: datetime
    updated_at: datetime
    
    class Config:
        from_attributes = True

# Security utilities
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = os.environ.get("SECRET_KEY", os.urandom(24).hex())
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60

# Database dependency with retry logic
def get_db():
    db = None
    max_retries = 3
    retry_count = 0
    
    while retry_count < max_retries:
        try:
            db = SessionLocal()
            # Test the connection
            db.execute("SELECT 1")
            break
        except Exception as e:
            if db:
                db.close()
            if retry_count < max_retries - 1:
                print(f"Database connection error: {e}. Retrying... ({retry_count + 1}/{max_retries})", flush=True)
                retry_count += 1
                time.sleep(1)  # Wait 1 second before retrying
            else:
                print(f"Failed to connect to the database after {max_retries} attempts", flush=True)
                raise
    
    try:
        yield db
    except Exception as e:
        print(f"Database operation error: {e}", flush=True)
        # Add reconnect logic for common MySQL connection errors
        if isinstance(e, Exception) and (
            "2013" in str(e) or  # Lost connection
            "2006" in str(e) or  # MySQL server has gone away
            "MySQL server has gone away" in str(e) or
            "Lost connection" in str(e)
        ):
            print("MySQL connection lost. Attempting to reconnect...", flush=True)
            db.close()
            db = SessionLocal()
            yield db
        else:
            raise
    finally:
        if db:
            db.close()

# Security functions
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# Original OAuth2 scheme (keep this for backward compatibility)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Custom token dependency that checks headers, cookies, and query parameters
async def get_token_from_request(request: Request):
    # First try to get from Authorization header
    authorization = request.headers.get("Authorization")
    if (authorization and authorization.startswith("Bearer ")):
        token = authorization.replace("Bearer ", "")
        print(f"Token found in Authorization header", flush=True)
        return token
    
    # Then try to get from cookies
    token_cookie = request.cookies.get("access_token")
    if token_cookie:
        print(f"Token found in cookies", flush=True)
        return token_cookie
    
    
    # Finally try query parameters (legacy support)
    token_param = request.query_params.get('token')
    if token_param:
        print(f"Token found in URL query parameters (deprecated)", flush=True)
        return token_param
    
    # If no token found, raise 401
    print("No token found in request", flush=True)
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Not authenticated",
        headers={"WWW-Authenticate": "Bearer"},
    )

# Auth dependency using our custom token extractor
async def get_current_user(token: str = Depends(get_token_from_request), db: Session = Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    print(f"Authenticating user with token: {token[:10]}...", flush=True)
    
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        print(f"Token decoded successfully, username: {username}", flush=True)
        
        if username is None:
            print("Username is None in token payload")
            raise credentials_exception
            
        token_data = TokenData(username=username, user_id=payload.get("user_id"), role=payload.get("role"))
    except Exception as e:
        print(f"Exception during token validation: {str(e)}", flush=True)
        raise credentials_exception
    
    user = db.query(User).filter(User.username == token_data.username).first()
    if user is None:
        print(f"User {username} not found in database", flush=True)
        raise credentials_exception
        
    print(f"Authentication successful for user: {user.username}, role: {user.role}")
    return user

# Admin dependency
async def get_admin_user(current_user: User = Depends(get_current_user)):
    if (current_user.role != "admin"):
        raise HTTPException(status_code=403, detail="Not enough permissions")
    return current_user

# Initialize FastAPI
app = FastAPI(title="Financial Planner")

# Add SessionMiddleware
app.add_middleware(
    SessionMiddleware, 
    secret_key=os.environ.get("SECRET_KEY", os.urandom(24).hex())
)

# Configure templates and static files
templates = Jinja2Templates(directory="../UI/templates")
app.mount("/static", StaticFiles(directory="../UI/static"), name="static")

# Custom message storage class for flash messages
class MessageStore:
    def __init__(self):
        self.messages = []
    
    def add_message(self, text: str, category: str = "info"):
        self.messages.append({"text": text, "category": category})
    
    def get_messages(self):
        return self.messages
    
    def clear(self):
        messages = self.messages.copy()
        self.messages = []
        return messages

# Global message store since we can't rely on sessions yet
_message_store = MessageStore()

# Context processor equivalent
@app.middleware("http")
async def add_context_data(request: Request, call_next):
    # Get the current user if available from token
    user = None
    print("testing middleware", flush=True)
    
    # Check for token in different sources
    token = None
    
    # 1. Check Authorization header (primary method)
    authorization = request.headers.get("Authorization")
    print(f"Authorization header: {authorization}", flush=True)
    if authorization and authorization.startswith("Bearer "):
        token = authorization.replace("Bearer ", "")
        print(f"Found token in Authorization header", flush=True)
    
    # 2. Check query parameters if no Authorization header
    if not token:
        # Get token from URL query parameters
        token_param = request.query_params.get('token')
        if token_param:
            token = token_param
            print(f"Found token in URL query parameters", flush=True)
    
    # Try to authenticate with the token if we found one
    if token:
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
            username = payload.get("sub")
            print(f"Decoded token, username: {username}", flush=True)
            if username:
                db = next(get_db())
                user = db.query(User).filter(User.username == username).first()
        except Exception as e:
            print(f"Token validation error: {str(e)}", flush=True)
    
    # Get messages
    try:
        # Try to use session if available
        messages = request.session.pop("messages", []) if hasattr(request, "session") else []
    except (AssertionError, AttributeError):
        # Fall back to our message store
        messages = _message_store.clear()
    
    # Set request state
    request.state.user = user
    request.state.messages = messages
    request.state.now = datetime.now()  # Add the current datetime to all templates
    
    # Continue processing the request
    response = await call_next(request)
    return response

# Helper function to flash messages
def flash_message(request: Request, text: str, category: str = "info"):
    try:
        # Try to use session if available
        if not hasattr(request.session, "messages"):
            request.session["messages"] = []
        request.session["messages"].append({"text": text, "category": category})
    except (AssertionError, AttributeError):
        # Fall back to our message store
        _message_store.add_message(text, category)

# Routes
@app.get("/", response_class=HTMLResponse)
async def index(request: Request, db: Session = Depends(get_db)):
    # Get latest blog posts
    latest_posts = db.query(
        BlogPost.id, BlogPost.title, BlogPost.content, BlogPost.image_url, 
        BlogPost.created_at, User.first_name, User.last_name
    ).join(User).order_by(BlogPost.created_at.desc()).limit(3).all()
    
    
    # Transform posts to include excerpt
    posts = []
    for post in latest_posts:
        post_dict = {
            "id": post.id,
            "title": post.title,
            "excerpt": post.content[:200],
            "image_url": post.image_url,
            "created_at": post.created_at,
            "first_name": post.first_name,
            "last_name": post.last_name
        }
        posts.append(post_dict)
    
    return templates.TemplateResponse(
        "index.html", 
        {
            "request": request, 
            "posts": posts, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

@app.get("/about", response_class=HTMLResponse)
async def about(request: Request):
    return templates.TemplateResponse(
        "about.html", 
        {
            "request": request, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

@app.get("/services", response_class=HTMLResponse)
async def services(request: Request):
    return templates.TemplateResponse(
        "services.html", 
        {
            "request": request, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

@app.get("/contact", response_class=HTMLResponse)
async def contact(request: Request):
    return templates.TemplateResponse(
        "contact.html", 
        {
            "request": request, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

@app.get("/blog", response_class=HTMLResponse)
async def blog(request: Request, db: Session = Depends(get_db)):
    posts_data = db.query(
        BlogPost.id, BlogPost.title, BlogPost.content, BlogPost.image_url,
        BlogPost.created_at, User.first_name, User.last_name
    ).join(User).order_by(BlogPost.created_at.desc()).all()
    
    posts = []
    for post in posts_data:
        post_dict = {
            "id": post.id,
            "title": post.title,
            "excerpt": post.content[:200],
            "image_url": post.image_url,
            "created_at": post.created_at,
            "first_name": post.first_name,
            "last_name": post.last_name
        }
        posts.append(post_dict)
    
    return templates.TemplateResponse(
        "blog/blog.html", 
        {
            "request": request, 
            "posts": posts, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

@app.get("/blog/post/{post_id}", response_class=HTMLResponse)
async def post(request: Request, post_id: int, db: Session = Depends(get_db)):
    post = db.query(
        BlogPost.id, BlogPost.title, BlogPost.content, BlogPost.image_url,
        BlogPost.created_at, User.first_name, User.last_name
    ).join(User).filter(BlogPost.id == post_id).first()
    
    if not post:
        raise HTTPException(status_code=404, detail="Post not found")
    
    post_dict = {
        "id": post.id,
        "title": post.title,
        "content": post.content,
        "image_url": post.image_url,
        "created_at": post.created_at,
        "first_name": post.first_name,
        "last_name": post.last_name
    }
    
    return templates.TemplateResponse(
        "blog/post.html", 
        {
            "request": request, 
            "post": post_dict, 
            "now": datetime.now(),
            "user": request.state.user,
            "messages": request.state.messages
        }
    )

# Authentication routes
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    user = db.query(User).filter(User.username == form_data.username).first()
    if not user or not verify_password(form_data.password, user.password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username, "user_id": user.id, "role": user.role}, 
        expires_delta=access_token_expires
    )
    
    # Create response with token
    response = JSONResponse({
        "access_token": access_token,
        "token_type": "bearer",
        "username": user.username,
        "role": user.role
    })
    
    # Set cookie with the token (secure in production, httponly for security)
    cookie_secure = os.environ.get("ENVIRONMENT", "development") == "production"
    response.set_cookie(
        key="access_token", 
        value=access_token,
        httponly=True,  # Prevents JavaScript access
        secure=cookie_secure,  # Only send over HTTPS in production
        samesite="strict",  # Protects against CSRF
        max_age=ACCESS_TOKEN_EXPIRE_MINUTES * 60,  # In seconds
        path="/"
    )
    
    return response

@app.get("/register", response_class=HTMLResponse)
async def register_form(request: Request):
    return templates.TemplateResponse("auth/register.html", {"request": request, "now": datetime.now()})

@app.post("/register", response_class=HTMLResponse)
async def register(
    request: Request,
    username: str = Form(...),
    email: str = Form(...),
    password: str = Form(...),
    confirm_password: str = Form(...),
    first_name: str = Form(...),
    last_name: str = Form(...),
    db: Session = Depends(get_db)
):
    # Validate data
    if password != confirm_password:
        return templates.TemplateResponse(
            "auth/register.html", 
            {"request": request, "error": "Passwords do not match", "now": datetime.now()}
        )
    
    # Check if user exists
    existing_user = db.query(User).filter(
        (User.username == username) | (User.email == email)
    ).first()
    
    if existing_user:
        return templates.TemplateResponse(
            "auth/register.html", 
            {"request": request, "error": "Username or email already exists", "now": datetime.now()}
        )
    
    # Create user
    hashed_password = get_password_hash(password)
    new_user = User(
        username=username,
        email=email,
        password=hashed_password,
        first_name=first_name,
        last_name=last_name
    )
    db.add(new_user)
    db.commit()
    
    return RedirectResponse(url="/login?success=registration", status_code=303)

@app.get("/login", response_class=HTMLResponse)
async def login_form(request: Request, success: Optional[str] = None):
    message = None
    if success == "registration":
        message = "Registration successful! You can now login."
    
    return templates.TemplateResponse(
        "auth/login.html", 
        {"request": request, "message": message, "now": datetime.now()}
    )

# Client routes
@app.get("/client/dashboard", response_class=HTMLResponse)
async def client_dashboard(
    request: Request,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    print("Client dashboard accessed", flush=True)
    print(f"Client dashboard accessed by user: {current_user.username}", flush=True)
    
    questionnaires = db.query(Questionnaire).filter(
        Questionnaire.user_id == current_user.id
    ).all()
    
    print(f"Found {len(questionnaires)} questionnaires for user")
   
   

    return templates.TemplateResponse(
        "client/dashboard.html", 
        {
            "request": request, 
            "questionnaires": questionnaires, 
            "user": current_user, 
            "now": datetime.now(),
            "messages": request.state.messages
        }
    )

@app.get("/client/questionnaire", response_class=HTMLResponse)
async def questionnaire_form(
    request: Request,
    current_user: User = Depends(get_current_user)
):
    return templates.TemplateResponse(
        "client/questionnaire.html", 
        {
            "request": request, 
            "user": current_user, 
            "now": datetime.now(),
            "messages": request.state.messages
        }
    )

@app.post("/client/questionnaire/save")
async def save_questionnaire(
    request: Request,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    form_data = await request.form()
    data = dict(form_data)
    title = data.get('title', f"Questionnaire {datetime.now().strftime('%Y-%m-%d')}")
    status = data.get('status', 'draft')
    
    # Process form data to JSON
    json_data = json.dumps(data)
    
    # Check if updating existing questionnaire
    questionnaire_id = data.get('questionnaire_id')
    if questionnaire_id:
        questionnaire = db.query(Questionnaire).filter(
            Questionnaire.id == int(questionnaire_id),
            Questionnaire.user_id == current_user.id
        ).first()
        
        if not questionnaire:
            return JSONResponse(
                status_code=404,
                content={"error": "Questionnaire not found"}
            )
        
        questionnaire.title = title
        questionnaire.data = json_data
        questionnaire.status = status
    else:
        # Create new questionnaire
        questionnaire = Questionnaire(
            user_id=current_user.id,
            title=title,
            data=json_data,
            status=status
        )
        db.add(questionnaire)
    
    db.commit()
    db.refresh(questionnaire)
    
    return {
        "success": True,
        "message": "Questionnaire saved successfully",
        "questionnaire_id": questionnaire.id
    }

@app.get("/client/questionnaire/{questionnaire_id}", response_class=HTMLResponse)
async def load_questionnaire(
    request: Request,
    questionnaire_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    questionnaire = db.query(Questionnaire).filter(
        Questionnaire.id == questionnaire_id,
        Questionnaire.user_id == current_user.id
    ).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "client/questionnaire.html", 
        {
            "request": request, 
            "questionnaire": questionnaire, 
            "user": current_user,
            "now": datetime.now(),
            "messages": request.state.messages
        }
    )

# Admin routes with similar pattern for other endpoints
@app.get("/admin/dashboard", response_class=HTMLResponse)
async def admin_dashboard(
    request: Request,
    current_user: User = Depends(get_admin_user),
    db: Session = Depends(get_db)
):
    user_count = db.query(User).filter(User.role == 'client').count()
    post_count = db.query(BlogPost).count()
    questionnaire_count = db.query(Questionnaire).count()
    
    recent_submissions = db.query(
        Questionnaire, User.first_name, User.last_name
    ).join(User).filter(
        Questionnaire.status == 'submitted'
    ).order_by(Questionnaire.created_at.desc()).limit(5).all()
    
    return templates.TemplateResponse(
        "admin/dashboard.html", 
        {
            "request": request, 
            "user": current_user,
            "user_count": user_count,
            "post_count": post_count,
            "questionnaire_count": questionnaire_count,
            "recent_submissions": recent_submissions,
            "now": datetime.now(),
            "messages": request.state.messages
        }
    )

@app.get("/admin/blog/new", response_class=HTMLResponse, name="admin_blog_post_new")
async def admin_blog_post_new(request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for creating new blog posts."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get list of users who can be authors (admins and advisors)
    authors = db.query(User).filter(User.role.in_(['admin', 'advisor'])).all()
    return templates.TemplateResponse(
        "admin/admin_blog_post_new.html",
        {
            "request": request,
            "user": current_user,
            "authors": authors
        }
    )

@app.get("/admin/questionnaires", response_class=HTMLResponse, name="admin_questionnaires")
async def admin_questionnaires(request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing questionnaires."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get all questionnaires with user information
    questionnaires = db.query(
        Questionnaire, User.first_name, User.last_name
    ).join(User).all()
    
    return templates.TemplateResponse(
        "admin/questionnaires.html",
        {
            "request": request,
            "user": current_user,
            "questionnaires": questionnaires
        }
    )

@app.get("/admin/users", response_class=HTMLResponse, name="admin_users")
async def admin_users(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing users."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get all users with their questionnaire counts
    users = db.query(User).all()  # Using the injected db session correctly
    
    return templates.TemplateResponse(
        "admin/users.html",
        {
            "request": request,
            "user": current_user,
            "users": users
        }
    )

@app.get("/admin/users/new", response_class=HTMLResponse, name="admin_user_new")
async def admin_user_new(request: Request, current_user: dict = Depends(get_current_user)):
    """Admin page for creating a new user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    return templates.TemplateResponse(
        "admin/user-form.html",
        {
            "request": request,
            "user": current_user,
            # No user data since this is for creating a new user
        }
    )

@app.get("/admin/users/{user_id}", response_class=HTMLResponse, name="admin_user_view")
async def admin_user_view(
    user_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for viewing a specific user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the user by ID with their questionnaires
    user = db.query(User).filter(User.id == user_id).first()
    
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    
    return templates.TemplateResponse(
        "admin/user-view.html",
        {
            "request": request,
            "user": current_user,
            "viewed_user": user
        }
    )

@app.get("/admin/users/{user_id}/edit", response_class=HTMLResponse, name="admin_user_edit")
async def admin_user_edit(
    user_id: int, request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for editing a user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the user by ID
    user = db.query(User).filter(User.id == user_id).first()
    
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    
    return templates.TemplateResponse(
        "admin/user-form.html",
        {
            "request": request,
            "current_user": current_user,  # Pass as current_user to avoid name conflict
            "user": user  # This is the user being edited
        }
    )

# Add this API route for updating users
# @app.put("/api/admin/users/{user_id}", response_model=HTMLResponse)
# async def update_user(
#     user_id: int, 
#     request: Request, 
#     db: Session = Depends(get_db), 
#     current_user: User = Depends(get_current_user)
# ):
#     """Update an existing user."""
#     # Check if user is admin
#     if not current_user or current_user.role != 'admin':
#         return JSONResponse(
#             status_code=403,
#             content={"success": False, "message": "Not authorized"}
#         )
    
#     try:
#         # Parse form data
#         form_data = await request.form()
#         user_data = dict(form_data)
        
#         # Get user to update
#         user = db.query(User).filter(User.id == user_id).first()
#         if not user:
#             return JSONResponse(
#                 status_code=404,
#                 content={"success": False, "message": f"User with ID {user_id} not found"}
#             )
        
#         # Update basic fields
#         if "first_name" in user_data:
#             user.first_name = user_data["first_name"]
#         if "last_name" in user_data:
#             user.last_name = user_data["last_name"]
#         if "email" in user_data:
#             user.email = user_data["email"]
#         if "username" in user_data:
#             user.username = user_data["username"]
#         if "phone" in user_data:
#             user.phone = user_data["phone"]
#         if "role" in user_data:
#             user.role = user_data["role"]
#         if "admin_notes" in user_data:
#             user.admin_notes = user_data["admin_notes"]
#         if "is_active" in user_data:
#             user.is_active = user_data["is_active"].lower() == "true"
        
#         # Update password if provided
#         if "password" in user_data and user_data["password"]:
#             user.password_hash = get_password_hash(user_data["password"])
        
#         db.commit()
        
#         return {"success": True, "message": "User updated successfully"}
#     except Exception as e:
#         db.rollback()
#         return JSONResponse(
#             status_code=500,
#             content={"success": False, "message": f"Error updating user: {str(e)}"}
#         )

# Add this debug endpoint to inspect authentication headers
@app.get("/debug/auth")
async def debug_auth(request: Request):
    """Debug endpoint to check authentication headers"""
    headers = dict(request.headers)
    print(f"DEBUG AUTH - Request headers: {headers}", flush=True)
    
    auth_header = headers.get('authorization', 'Not found')
    print(f"DEBUG AUTH - Authorization header: {auth_header}", flush=True)
    
    token = None
    if auth_header.startswith('Bearer '):
        token = auth_header.replace('Bearer ', '')
    
    result = {
        'auth_header_exists': 'authorization' in headers,
        'auth_header': auth_header,
        'token_extracted': token is not None, 
        'all_headers': headers  # Added for debugging
    }
    
    # Try to decode the token if it exists
    if token:
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
            result['token_valid'] = True
            result['token_payload'] = {
                'sub': payload.get('sub'),
                'user_id': payload.get('user_id'),
                'role': payload.get('role'),
                'exp': payload.get('exp')
            }
            # Add more debugging info
            result['token_expiration'] = {
                'exp_timestamp': payload.get('exp'),
                'current_timestamp': int(datetime.now().timestamp()),
                'expires_in_seconds': payload.get('exp') - int(datetime.now().timestamp()) if payload.get('exp') else None,
                'is_expired': payload.get('exp') < int(datetime.now().timestamp()) if payload.get('exp') else True
            }
        except Exception as e:
            result['token_valid'] = False
            result['token_error'] = str(e)
    
    return result

# Create a new debug route for client templates
@app.get("/client/templates/dashboard", response_class=HTMLResponse)
async def client_templates_dashboard(request: Request):
    """Debug endpoint that renders the client dashboard without authentication"""
    print("Accessing client dashboard template directly (no auth)")
    return templates.TemplateResponse(
        "client/dashboard.html",
        {
            "request": request,
            "questionnaires": [],  # Empty list for debugging
            "user": {"username": "debug_user", "first_name": "Debug", "last_name": "User"},
            "now": datetime.now(),
            "messages": [{"text": "This is a debug view without authentication", "category": "warning"}]
        }
    )

@app.get("/admin/blog-posts", response_class=HTMLResponse, name="admin_blog_posts")
async def admin_blog_posts(request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing blog posts."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get all blog posts with authors
    posts = db.query(
        BlogPost.id, BlogPost.title, BlogPost.content, BlogPost.image_url,
        BlogPost.created_at, User.first_name, User.last_name
    ).join(User).order_by(BlogPost.created_at.desc()).all()
    
    return templates.TemplateResponse(
        "admin/blog-posts.html",
        {
            "request": request,
            "user": current_user,
            "posts": posts
        }
    )

@app.get("/admin/blog-posts/{post_id}/edit", response_class=HTMLResponse, name="admin_blog_post_edit")
async def admin_blog_post_edit(
    post_id: int, request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for editing a blog post."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the post by ID
    post = db.query(BlogPost).filter(BlogPost.id == post_id).first()
    
    if not post:
        raise HTTPException(status_code=404, detail="Blog post not found")
    
    # Get list of users who can be authors (admins and advisors)
    authors = db.query(User).filter(User.role.in_(['admin', 'advisor'])).all()
    
    return templates.TemplateResponse(
        "admin/blog-editor.html",
        {
            "request": request,
            "user": current_user,
            "post": post,
            "authors": authors
        }
    )

@app.get("/admin/questionnaire/{questionnaire_id}", response_class=HTMLResponse, name="admin_questionnaire_view")
async def admin_questionnaire_view(
    questionnaire_id: int, request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for viewing a specific questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the questionnaire by ID with user info
    questionnaire = db.query(
        Questionnaire, User.first_name, User.last_name
    ).join(User).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/questionnaire-view.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data
        }
    )

@app.get("/admin/questionnaire/{questionnaire_id}/review", response_class=HTMLResponse, name="admin_questionnaire_review")
async def admin_questionnaire_review(
    questionnaire_id: int, request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for reviewing and providing feedback on a questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the questionnaire by ID with user info
    questionnaire = db.query(
        Questionnaire, User.first_name, User.last_name
    ).join(User).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/questionnaire-review.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data
        }
    )

@app.get("/admin/analytics", response_class=HTMLResponse, name="admin_analytics")
async def admin_analytics(request: Request, current_user: dict = Depends(get_current_user)):
    """Admin analytics dashboard."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get analytics data
    # In a real application, you would fetch actual analytics data
    stats = {
        "new_users": 45,
        "user_growth_trend": 12.5,
        "questionnaire_count": 30,
        "questionnaire_trend": 8.3,
        "page_views": 1250,
        "page_views_trend": 15.2,
        "blog_views": 820,
        "blog_views_trend": 5.7
    }
    
    popular_pages = [
        {"url": "/", "views": 450, "avg_time": "2m 15s"},
        {"url": "/blog", "views": 325, "avg_time": "3m 45s"},
        {"url": "/services", "views": 287, "avg_time": "2m 30s"},
        {"url": "/contact", "views": 180, "avg_time": "1m 30s"}
    ]
    
    top_blog_posts = [
        {"title": "Retirement Planning Basics", "views": 150, "comments": 12, "avg_read_time": "5m 20s", "published_at": "Apr 15, 2023"},
        {"title": "Investment Strategies for Beginners", "views": 130, "comments": 8, "avg_read_time": "4m 45s", "published_at": "May 2, 2023"},
        {"title": "Tax Planning Tips", "views": 110, "comments": 6, "avg_read_time": "3m 30s", "published_at": "Jun 10, 2023"}
    ]
    
    return templates.TemplateResponse(
        "admin/analytics.html",
        {
            "request": request,
            "user": current_user,
            "stats": stats,
            "popular_pages": popular_pages,
            "top_blog_posts": top_blog_posts
        }
    )

@app.get("/admin/questionnaire/{questionnaire_id}/report", response_class=HTMLResponse, name="admin_generate_report")
async def admin_generate_report(
    questionnaire_id: int, request: Request, current_user: dict = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for generating a financial report based on questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Get the questionnaire by ID with user info
    questionnaire = db.query(Questionnaire).join(User).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/report-generator.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data
        }
    )

# Fix admin dashboard route
@app.get("/admin", response_class=HTMLResponse, name="admin_dashboard")
async def admin_dashboard(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin dashboard page."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session for database operations
    user_count = db.query(User).count()
    questionnaire_count = db.query(Questionnaire).count()
    post_count = db.query(BlogPost).count()
    pending_review_count = db.query(Questionnaire).filter(Questionnaire.status == 'submitted').count()
    
    # Get recent submissions
    recent_submissions = db.query(Questionnaire, User).join(User).filter(
        Questionnaire.status == 'submitted'
    ).order_by(Questionnaire.updated_at.desc()).limit(5).all()
    
    # Get recent users
    recent_users = db.query(User).order_by(User.created_at.desc()).limit(5).all()
    
    return templates.TemplateResponse(
        "admin/dashboard.html",
        {
            "request": request,
            "user": current_user,
            "user_count": user_count,
            "questionnaire_count": questionnaire_count,
            "post_count": post_count,
            "pending_review_count": pending_review_count,
            "recent_submissions": recent_submissions,
            "recent_users": recent_users,
            "now": datetime.now()
        }
    )

# Fix admin users route
@app.get("/admin/users", response_class=HTMLResponse, name="admin_users")
async def admin_users(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing users."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    users = db.query(User).options(
        joinedload(User.questionnaires)
    ).all()
    
    return templates.TemplateResponse(
        "admin/users.html",
        {
            "request": request,
            "user": current_user,
            "users": users,
            "now": datetime.now()
        }
    )

# Fix admin user view route
@app.get("/admin/users/{user_id}", response_class=HTMLResponse, name="admin_user_view")
async def admin_user_view(
    user_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for viewing a specific user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    viewed_user = db.query(User).options(
        joinedload(User.questionnaires)
    ).filter(User.id == user_id).first()
    
    if not viewed_user:
        raise HTTPException(status_code=404, detail="User not found")
    
    return templates.TemplateResponse(
        "admin/user-view.html",
        {
            "request": request,
            "user": current_user,
            "viewed_user": viewed_user,
            "now": datetime.now()
        }
    )

# Fix admin user edit route
@app.get("/admin/users/{user_id}/edit", response_class=HTMLResponse, name="admin_user_edit")
async def admin_user_edit(
    user_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for editing a user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    user = db.query(User).filter(User.id == user_id).first()
    
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    
    return templates.TemplateResponse(
        "admin/user-form.html",
        {
            "request": request,
            "current_user": current_user,
            "user": user,
            "now": datetime.now()
        }
    )

# Fix admin user new route
@app.get("/admin/users/new", response_class=HTMLResponse, name="admin_user_new")
async def admin_user_new(request: Request, current_user: User = Depends(get_current_user)):
    """Admin page for creating a new user."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    return templates.TemplateResponse(
        "admin/user-form.html",
        {
            "request": request,
            "user": current_user,
            "now": datetime.now()
        }
    )

# Fix admin blog posts route
@app.get("/admin/blog-posts", response_class=HTMLResponse, name="admin_blog_posts")
async def admin_blog_posts(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing blog posts."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    posts = db.query(BlogPost).options(
        joinedload(BlogPost.author)
    ).order_by(BlogPost.created_at.desc()).all()
    
    return templates.TemplateResponse(
        "admin/blog-posts.html",
        {
            "request": request,
            "user": current_user,
            "posts": posts,
            "now": datetime.now()
        }
    )

# Fix admin blog post edit route
@app.get("/admin/blog-posts/{post_id}/edit", response_class=HTMLResponse, name="admin_blog_post_edit")
async def admin_blog_post_edit(
    post_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for editing a blog post."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    post = db.query(BlogPost).filter(BlogPost.id == post_id).first()
    
    if not post:
        raise HTTPException(status_code=404, detail="Blog post not found")
    
    # Get list of users who can be authors (admins and advisors)
    authors = db.query(User).filter(or_(User.role == 'admin', User.role == 'advisor')).all()
    
    return templates.TemplateResponse(
        "admin/blog-editor.html",
        {
            "request": request,
            "user": current_user,
            "post": post,
            "authors": authors,
            "now": datetime.now()
        }
    )

# Fix admin blog post new route
@app.get("/admin/blog/new", response_class=HTMLResponse, name="admin_blog_post_new")
async def admin_blog_post_new(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for creating new blog posts."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    authors = db.query(User).filter(or_(User.role == 'admin', User.role == 'advisor')).all()
    
    return templates.TemplateResponse(
        "admin/admin_blog_post_new.html",
        {
            "request": request,
            "user": current_user,
            "authors": authors,
            "now": datetime.now()
        }
    )

# Fix admin questionnaires route
@app.get("/admin/questionnaires", response_class=HTMLResponse, name="admin_questionnaires")
async def admin_questionnaires(request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """Admin page for managing questionnaires."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    questionnaires = db.query(Questionnaire).options(
        joinedload(Questionnaire.user)
    ).order_by(Questionnaire.updated_at.desc()).all()
    
    return templates.TemplateResponse(
        "admin/questionnaires.html",
        {
            "request": request,
            "user": current_user,
            "questionnaires": questionnaires,
            "now": datetime.now()
        }
    )

# Fix admin questionnaire view route
@app.get("/admin/questionnaire/{questionnaire_id}", response_class=HTMLResponse, name="admin_questionnaire_view")
async def admin_questionnaire_view(
    questionnaire_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for viewing a specific questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    questionnaire = db.query(Questionnaire).options(
        joinedload(Questionnaire.user)
    ).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/questionnaire-view.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data,
            "now": datetime.now()
        }
    )

# Fix admin questionnaire review route
@app.get("/admin/questionnaire/{questionnaire_id}/review", response_class=HTMLResponse, name="admin_questionnaire_review")
async def admin_questionnaire_review(
    questionnaire_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for reviewing and providing feedback on a questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    questionnaire = db.query(Questionnaire).options(
        joinedload(Questionnaire.user)
    ).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/questionnaire-review.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data,
            "now": datetime.now()
        }
    )

# Fix admin analytics route
@app.get("/admin/analytics", response_class=HTMLResponse, name="admin_analytics")
async def admin_analytics(request: Request, current_user: User = Depends(get_current_user)):
    """Admin analytics dashboard."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Sample analytics data - in a production environment, you would fetch real data from the database
    stats = {
        "new_users": 45,
        "user_growth_trend": 12.5,
        "questionnaire_count": 30,
        "questionnaire_trend": 8.3,
        "page_views": 1250,
        "page_views_trend": 15.2,
        "blog_views": 820,
        "blog_views_trend": 5.7
    }
    
    popular_pages = [
        {"url": "/", "views": 450, "avg_time": "2m 15s"},
        {"url": "/blog", "views": 325, "avg_time": "3m 45s"},
        {"url": "/services", "views": 287, "avg_time": "2m 30s"},
        {"url": "/contact", "views": 180, "avg_time": "1m 30s"}
    ]
    
    top_blog_posts = [
        {"title": "Retirement Planning Basics", "views": 150, "comments": 12, "avg_read_time": "5m 20s", "published_at": "Apr 15, 2023"},
        {"title": "Investment Strategies for Beginners", "views": 130, "comments": 8, "avg_read_time": "4m 45s", "published_at": "May 2, 2023"},
        {"title": "Tax Planning Tips", "views": 110, "comments": 6, "avg_read_time": "3m 30s", "published_at": "Jun 10, 2023"}
    ]
    
    return templates.TemplateResponse(
        "admin/analytics.html",
        {
            "request": request,
            "user": current_user,
            "stats": stats,
            "popular_pages": popular_pages,
            "top_blog_posts": top_blog_posts,
            "now": datetime.now()
        }
    )

# Fix admin report generator route
@app.get("/admin/questionnaire/{questionnaire_id}/report", response_class=HTMLResponse, name="admin_generate_report")
async def admin_generate_report(
    questionnaire_id: int, request: Request, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)
):
    """Admin page for generating a financial report based on questionnaire."""
    if not current_user or current_user.role != 'admin':
        return RedirectResponse(url="/auth/login")
    
    # Use the provided db session
    questionnaire = db.query(Questionnaire).options(
        joinedload(Questionnaire.user)
    ).filter(Questionnaire.id == questionnaire_id).first()
    
    if not questionnaire:
        raise HTTPException(status_code=404, detail="Questionnaire not found")
    
    return templates.TemplateResponse(
        "admin/report-generator.html",
        {
            "request": request,
            "user": current_user,
            "questionnaire": questionnaire,
            "data": questionnaire.data,
            "now": datetime.now()
        }
    )

# If you're running this directly
if __name__ == "__main__":
    import uvicorn
    uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
