"""Main PyView application for FenLiu."""

import logging
import traceback
from contextlib import asynccontextmanager
from datetime import timezone

import pyview.pyview
from pyview import PyView
from pyview import defaultRootTemplate
from pyview.live_socket import UnconnectedSocket
from sqlalchemy import func
from starlette.responses import HTMLResponse
from starlette.responses import JSONResponse
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates

from fenliu.api import api_router
from fenliu.config import settings
from fenliu.database import create_tables
from fenliu.database import get_db
from fenliu.models import HashtagStream
from fenliu.models import Post

# Configure logging
logging.basicConfig(
    level=logging.INFO if not settings.debug else logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)

# Initialize Jinja2 templates
templates = Jinja2Templates(directory="src/fenliu/templates")

# PyView bug workaround: Patch liveview_container to handle UnconnectedSocket

_original_liveview_container = pyview.pyview.liveview_container


async def _patched_liveview_container(template, view_lookup, request):
    """Patched version of liveview_container that handles UnconnectedSocket."""
    try:
        # Try to get LiveView for the path
        url = request.url
        path = url.path
        # Use underscore prefix for unused variables
        _lv, _path_params = view_lookup.get(path)

        # If we get here, a LiveView was found
        s = UnconnectedSocket()

        # Check if socket has context attribute
        if not hasattr(s, "context"):
            # This is a regular HTTP request, not a WebSocket connection
            # Log the error and return a proper HTTP response
            logger.error(f"PyView bug: UnconnectedSocket has no context for path {path}")
            logger.error("This is likely a regular HTTP request being handled as LiveView")

            # Return a simple error response
            return HTMLResponse(
                "<h1>Internal Server Error</h1><p>PyView routing issue. Please try again.</p>",
                status_code=500,
            )

        # Continue with original logic if socket has context
        return await _original_liveview_container(template, view_lookup, request)

    except ValueError:
        # No LiveView found for this path - this is expected for regular HTTP routes
        # Let the request continue to regular HTTP routes
        raise
    except Exception as e:
        # Log any other errors
        logger.error(f"Error in patched liveview_container: {e}")
        logger.error(traceback.format_exc())
        raise


# Apply the patch
pyview.pyview.liveview_container = _patched_liveview_container  # type: ignore


@asynccontextmanager
async def lifespan(_app: PyView):
    """Lifespan context manager for PyView application."""
    # Startup: Create database tables
    logger.info("Starting FenLiu application...")
    logger.info(f"Database URL: {settings.database_url}")

    # Log that PyView patch is active
    logger.info("PyView bug workaround patch is active")

    try:
        create_tables()
        logger.info("Database tables created/verified")
    except Exception as e:
        logger.error(f"Failed to create database tables: {e}")
        raise

    logger.info(f"FenLiu {settings.app_version} started successfully")
    yield

    # Shutdown
    logger.info("Shutting down FenLiu application...")


# Create PyView application
app = PyView(
    lifespan=lifespan,
    debug=settings.debug,
)

# Mount static files directory
app.mount("/static", StaticFiles(directory="src/fenliu/static"), name="static")

# Customize root template
app.rootTemplate = defaultRootTemplate(
    title=f"{settings.app_name} v{settings.app_version}",
    title_suffix=" | LiveView",
)


# Regular HTTP routes (workaround for PyView LiveView bug)
@app.route("/")
async def dashboard(request):
    """Render the dashboard page."""
    # Extra protection against PyView bug
    try:
        db = next(get_db())
    except AttributeError as e:
        if "'UnconnectedSocket' object has no attribute 'context'" in str(e):
            # PyView bug intercepted by our patch, but just in case
            logger.error("PyView bug reached dashboard route handler")
            return HTMLResponse(
                "<h1>Internal Server Error</h1><p>PyView routing issue detected. Please refresh.</p>",
                status_code=500,
            )
        raise

    # Get statistics
    total_streams = db.query(HashtagStream).count()
    active_streams = db.query(HashtagStream).filter(HashtagStream.active).count()
    total_posts = db.query(Post).count()
    processed_posts = db.query(Post).filter(Post.processed).count()

    # Get recent posts
    recent_posts = db.query(Post).order_by(Post.fetched_at.desc()).limit(10).all()

    # Get active streams
    active_streams_list = db.query(HashtagStream).filter(HashtagStream.active).order_by(HashtagStream.hashtag).all()

    # Convert naive datetimes to timezone-aware UTC for display
    # SQLite stores naive datetimes but they represent UTC time
    def make_datetime_aware(dt):
        """Convert naive datetime to timezone-aware UTC datetime."""
        if dt is not None and dt.tzinfo is None:
            return dt.replace(tzinfo=timezone.utc)
        return dt

    # Process streams to make datetimes timezone-aware
    processed_streams = []
    for stream in active_streams_list:
        stream_dict = {c.name: getattr(stream, c.name) for c in stream.__table__.columns}
        stream_dict["last_check"] = make_datetime_aware(stream.last_check)
        stream_dict["created_at"] = make_datetime_aware(stream.created_at)
        stream_dict["updated_at"] = make_datetime_aware(stream.updated_at)
        processed_streams.append(stream_dict)

    context = {
        "request": request,
        "total_streams": total_streams,
        "active_streams": active_streams,
        "total_posts": total_posts,
        "processed_posts": processed_posts,
        "recent_posts": recent_posts,
        "active_streams_list": processed_streams,
        "app_name": settings.app_name,
        "app_version": settings.app_version,
    }
    return templates.TemplateResponse("dashboard.html", context)


@app.route("/streams")
async def streams_page(request):
    """Render the hashtag streams management page."""
    db = next(get_db())

    streams = db.query(HashtagStream).order_by(HashtagStream.created_at.desc()).all()

    # Convert naive datetimes to timezone-aware UTC for display
    # SQLite stores naive datetimes but they represent UTC time
    def make_datetime_aware(dt):
        """Convert naive datetime to timezone-aware UTC datetime."""
        if dt is not None and dt.tzinfo is None:
            return dt.replace(tzinfo=timezone.utc)
        return dt

    # Process streams to make datetimes timezone-aware
    processed_streams = []
    for stream in streams:
        stream_dict = {c.name: getattr(stream, c.name) for c in stream.__table__.columns}
        stream_dict["last_check"] = make_datetime_aware(stream.last_check)
        stream_dict["created_at"] = make_datetime_aware(stream.created_at)
        stream_dict["updated_at"] = make_datetime_aware(stream.updated_at)
        processed_streams.append(stream_dict)

    context = {
        "request": request,
        "streams": processed_streams,
        "app_name": settings.app_name,
        "app_version": settings.app_version,
        "default_instance": settings.default_instance,
    }
    return templates.TemplateResponse("hashtag_streams.html", context)


@app.route("/posts")
async def posts_page(request):
    """Render the posts management page."""
    db = next(get_db())

    posts = db.query(Post).order_by(Post.fetched_at.desc()).limit(50).all()

    # Convert naive datetimes to timezone-aware UTC for display
    # SQLite stores naive datetimes but they represent UTC time
    def make_datetime_aware(dt):
        """Convert naive datetime to timezone-aware UTC datetime."""
        if dt is not None and dt.tzinfo is None:
            return dt.replace(tzinfo=timezone.utc)
        return dt

    # Process posts to make datetimes timezone-aware
    processed_posts = []
    for post in posts:
        post_dict = {c.name: getattr(post, c.name) for c in post.__table__.columns}
        post_dict["created_at"] = make_datetime_aware(post.created_at)
        post_dict["fetched_at"] = make_datetime_aware(post.fetched_at)
        processed_posts.append(post_dict)

    context = {
        "request": request,
        "posts": processed_posts,
        "app_name": settings.app_name,
        "app_version": settings.app_version,
    }
    return templates.TemplateResponse("posts.html", context)


@app.route("/stats")
async def stats_page(request):
    """Render the statistics page."""
    db = next(get_db())

    # Get basic statistics
    total_streams = db.query(HashtagStream).count()
    active_streams = db.query(HashtagStream).filter(HashtagStream.active).count()
    total_posts = db.query(Post).count()
    processed_posts = db.query(Post).filter(Post.processed).count()

    # Calculate average spam score
    avg_result = db.query(func.avg(Post.spam_score)).filter(Post.processed).first()
    average_spam_score = float(avg_result[0]) if avg_result[0] is not None else None

    # Get top hashtags by post count
    top_hashtags = (
        db.query(
            HashtagStream.hashtag,
            HashtagStream.active,
            func.count(Post.id).label("post_count"),
        )
        .join(Post, HashtagStream.id == Post.stream_id, isouter=True)
        .group_by(HashtagStream.id)
        .order_by(func.count(Post.id).desc())
        .limit(10)
        .all()
    )

    context = {
        "request": request,
        "total_streams": total_streams,
        "active_streams": active_streams,
        "total_posts": total_posts,
        "processed_posts": processed_posts,
        "average_spam_score": average_spam_score,
        "top_hashtags": top_hashtags,
        "app_name": settings.app_name,
        "app_version": settings.app_version,
    }
    return templates.TemplateResponse("stats.html", context)


# Add API endpoints (RESTful)
app.mount("/api", api_router)


@app.route("/health")
async def health_check(_request):
    """Health check endpoint."""
    return JSONResponse(
        {
            "status": "healthy",
            "app": settings.app_name,
            "version": settings.app_version,
        }
    )


@app.route("/info")
async def app_info(_request):
    """Application information endpoint."""
    return JSONResponse(
        {
            "app": settings.app_name,
            "version": settings.app_version,
            "description": "Fediverse content stream filtering system",
            "inspiration": "Dujiangyan irrigation system (256 BC)",
        }
    )
