import asyncio
import contextlib
import logging
from pathlib import Path

import aiofiles
from fastapi import Depends, WebSocket
from fastapi.requests import Request
from fastapi.templating import Jinja2Templates
from starlette.templating import _TemplateResponse

from ...abstractions import R2RProviders, R2RServices
from ...config import R2RConfig
from .base_router import BaseRouterV3


class LogsRouter(BaseRouterV3):
    def __init__(
        self,
        providers: R2RProviders,
        services: R2RServices,
        config: R2RConfig,
    ):
        logging.info("Initializing LogsRouter")
        super().__init__(providers, services, config)
        CURRENT_DIR = Path(__file__).resolve().parent
        TEMPLATES_DIR = CURRENT_DIR.parent / "templates"
        self.templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
        self.services = services
        self.log_file = Path.cwd() / "logs" / "app.log"
        self.log_file.parent.mkdir(exist_ok=True)
        if not self.log_file.exists():
            self.log_file.touch(mode=0o666)

        # Start from the beginning of the file
        self.last_position = 0

    async def read_full_file(self) -> str:
        """Read the entire log file from the start."""
        if not self.log_file.exists():
            return "Initializing logging system..."

        try:
            async with aiofiles.open(self.log_file, mode="r") as f:
                # Start at beginning
                await f.seek(0)
                full_content = await f.read()
                # Move last_position to end of file after reading full content
                self.last_position = await f.tell()
                return full_content
        except Exception as e:
            logging.error(f"Error reading full logs: {str(e)}")
            return f"Error accessing full log file: {str(e)}"

    async def read_new_logs(self) -> str:
        """Read new logs appended after last_position."""
        if not self.log_file.exists():
            return "Initializing logging system..."

        try:
            async with aiofiles.open(self.log_file, mode="r") as f:
                await f.seek(self.last_position)
                new_content = await f.read()
                self.last_position = await f.tell()
                return new_content or ""
        except Exception as e:
            logging.error(f"Error reading logs: {str(e)}")
            return f"Error accessing log file: {str(e)}"

    def _setup_routes(self):
        @self.router.websocket(
            "/logs/stream",
            dependencies=[Depends(self.websocket_rate_limit_dependency)],
        )
        async def stream_logs(
            websocket: WebSocket,
            auth_user=Depends(
                self.providers.auth.websocket_auth_wrapper(superuser_only=True)
            ),
        ):
            if not auth_user:
                return
            await websocket.accept()
            try:
                # Send the entire file content upon initial connection
                full_logs = await self.read_full_file()
                if full_logs:
                    await websocket.send_text(full_logs)

                # Now send incremental updates only
                while True:
                    new_logs = await self.read_new_logs()
                    if new_logs:
                        await websocket.send_text(new_logs)
                    await asyncio.sleep(0.5)
            except Exception as e:
                logging.error(f"WebSocket error: {str(e)}")
            finally:
                with contextlib.suppress(Exception):
                    await websocket.close()

        @self.router.get(
            "/logs/viewer",
            dependencies=[Depends(self.rate_limit_dependency)],
        )
        async def get_log_viewer(request: Request) -> _TemplateResponse:
            return self.templates.TemplateResponse(
                "log_viewer.html", {"request": request}
            )
