from fastapi import FastAPI, Request, Form, BackgroundTasks
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from functools import partial
import json, download, asyncio
from typing import Optional
import logging, os
from logging.handlers import TimedRotatingFileHandler

# Setup paths
DATA_DIR = os.getenv("DATA_DIR", "/data")
LOGS_DIR = os.path.join(DATA_DIR, "logs")
LOG_FILE = os.path.join(LOGS_DIR, "syllabus.log")

# Ensure logs directory exists
os.makedirs(LOGS_DIR, exist_ok=True)

# Ensure log file exists
if not os.path.exists(LOG_FILE):
    with open(LOG_FILE, "w") as f:
        f.write("")

# Set up logger
logger = logging.getLogger("syllabus")
logger.setLevel(logging.DEBUG)
logger.handlers = []  # Remove default handlers

# Use correct path here:
handler = TimedRotatingFileHandler(
    filename=LOG_FILE,
    when="midnight",
    interval=30,
    backupCount=12,
    encoding="utf-8",
    utc=False
)

formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

# App setup
app = FastAPI()
app.mount("/data", StaticFiles(directory=DATA_DIR), name="data")
templates = Jinja2Templates(directory="templates")
loop = asyncio.get_event_loop()

# Optional cache
cached_data = None

# Middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
    try:
        response = await call_next(request)
    except Exception as e:
        logger.exception(f"EXCEPTION: {request.method} {request.url} - {str(e)}")
        return JSONResponse(
            status_code=500,
            content={"detail": "Internal Server Error"},
        )

    logger.info(
        f"request_client={request.client.host}:{request.client.port}, "
        f"request_method={request.method}, request_url={request.url}, "
        f"status_code={response.status_code}"
    )
    return response





# api

# @app.post("/ebook/download", description="Download an ebook via a url.")
# async def ebookDownload(
#     background_tasks: BackgroundTasks,
#     url: str = Form(...),
#     author: str = Form(...)
#     ):
#     try:
#         background_tasks.add_task(download.ebook,url,author)
#         # download.dropout.show(show,season,episode)
#         return JSONResponse(status_code=200, content={"status": "success", "message": "Book downloaded."})
#     except Exception as e:
#         return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})

@app.get("/dropout/update")
async def dropoutUpdate(force: bool = False):
    global cached_data
    try:
        download.dropout.series(force)
        with open(f'{DATA_DIR}/dropout.json') as f:
            cached_data = json.load(f)
        return JSONResponse(status_code=200, content={"status": "success", "message": "Series grab complete."})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})

@app.get("/dropout/series")
async def dropout_series():
    global cached_data
    if cached_data is None:
        await dropoutUpdate()

    if cached_data is None:
        raise JSONResponse(status_code=404, detail="No data available")

    return cached_data  # FastAPI auto-converts to JSON

async def get_show_data(show: str, force: bool = False):
    global cached_data
    if cached_data is None:
        await dropoutUpdate()

    for item in cached_data:
        if show == item["SHOW"] or show == item["LINK"]:
            if "SEASONS" not in item or force is not False:
                item['SEASONS'] = download.grab.season(item['URL'])
            return item
    return None

def get_latest_season(item):
    seasons = item.get("SEASONS")
    if seasons and isinstance(seasons, list):
        try:
            numeric_seasons = [int(s) for s in seasons if str(s).isdigit()]
            if numeric_seasons:
                return max(numeric_seasons)
        except Exception as e:
            logging.error(f"Error getting latest season: {e}")
    return None

@app.post("/dropout/custom", description="")
async def dropout_download(
    background_tasks: BackgroundTasks,
    url: str = Form(...),
    directory: str = Form(...),
    prefix: Optional[str] = Form(None)
):
    # Ensure output directory exists
    os.makedirs(directory, exist_ok=True)

    try:
        background_tasks.add_task(download.dropout.custom, url, directory, prefix)
        return {"status": "success", "message": "Download started"}
    except Exception as e:
        raise JSONResponse(status_code=500, content=f"Download failed: {str(e)}")

@app.post("/dropout/download", description="Download an entire season from episode 1. Ignores behind the scenes and trailers.")
async def dropout_download(
    background_tasks: BackgroundTasks,
    show: str = Form(...),
    season: Optional[int] = Form(None),
    latest: bool = Form(True),
    archive: bool = Form(False),
    specials: bool = Form(False),
    episode_start: Optional[int] = Form(None)
):
    try:
        # Resolve latest season if requested
        if latest and season is None:
            show_data = await get_show_data(show, True)
            if not show_data:
                return JSONResponse(
                    status_code=404,
                    content={"status": "error", "message": "Show not found"}
                )

            season = get_latest_season(show_data)
            if season is None:
                return JSONResponse(
                    status_code=400,
                    content={"status": "error", "message": "No valid seasons found"}
                )

        # Ensure season is specified by now
        if season is None:
            return JSONResponse(
                status_code=400,
                content={"status": "error", "message": "Season is required unless 'latest' is used."}
            )
        
        task_msg = f"{'Adding to archive' if archive else 'Starting download'} for show '{show}', season {season}{' specials' if specials else ''}."
        logger.info(f"message={task_msg}")

        # Schedule the background task
        if archive:
            background_tasks.add_task(download.dropout.archive, show, season)
        else:
            background_tasks.add_task(download.dropout.show, show, season, specials, episode_start)

        return JSONResponse(
            status_code=200,
            content={
                "status": "success",
                "message": (task_msg)
            }
        )
    
    except Exception as e:
        logger.exception(f"Unhandled exception during /dropout/download: {e}")
        return JSONResponse(
            status_code=500,
            content={"status": "error", "message": "An unexpected error occurred."}
        )
    
# @app.post("/dropout/download/specials", description="Downloads a seasons behind the scenes and trailers, ignores main episodes.")
# async def dropoutDownload(
#     background_tasks: BackgroundTasks,
#     show: str = Form(...),
#     season: int = Form(...),
#     episode: Optional[int] = Form(None)
# ):
#     try:
#         logger.info(f'message=Received download request for specials of season {season} of {show}.')
#         background_tasks.add_task(download.dropout.specials,show,season,episode)
#         # download.dropout.show(show,season,episode)
#         return JSONResponse(status_code=200, content={"status": "success", "message": "Series downloaded."})
#     except Exception as e:
#         return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})

@app.post("/ydl")
async def ydl(background_tasks: BackgroundTasks, url: str = Form(...), location: str = Form(...)):
    try:
        background_tasks.add_task(download.youtube.ydl, url, location)
        # download.youtube.ydl(url,location)
            # grab.thumbnail(ydl,url,location)
        return JSONResponse(status_code=200, content={"status": "success", "message": "Video download completed."})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})





#web ui
@app.get("/", include_in_schema=False, response_class=HTMLResponse)
async def index(request: Request):
    global cached_data
    try:
        if cached_data is None:
            await dropoutUpdate()
        return templates.TemplateResponse("index.html", {"request": request, "data": cached_data})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})

@app.get("/show/{show}", include_in_schema=False, response_class=HTMLResponse)
async def index(request: Request, show: str):
    try:
        item = await get_show_data(show)
        if item:
            return templates.TemplateResponse("show.html", {"request": request, "show": item})
        else:
            return JSONResponse(status_code=404, content={"status": "error", "message": "Show not found"})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    
@app.get("/ydl", include_in_schema=False)
async def webpage(request: Request):
    try:
        return templates.TemplateResponse("ydl.html", {"request": request})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
    
@app.get("/dropout", include_in_schema=False)
async def webpage(request: Request):
    global cached_data
    if cached_data is None:
        await dropoutUpdate()
    try:
        return templates.TemplateResponse("dropout.html", {"request": request, "data": cached_data})
    except Exception as e:
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
