import os
import secrets
import subprocess
from asyncio import sleep
from tempfile import NamedTemporaryFile, TemporaryDirectory

from playwright.async_api import (
    ElementHandle,
    Locator,
    Page,
    async_playwright,
    expect,
)

from rvpc.auth.tokens.typing import UserToken


def frames_to_mp4(tmpdir: str) -> str:
    file_name = f"overlay_{secrets.token_urlsafe(16)}.mp4"

    while os.path.isfile(f"./rvpc/static/videos/{file_name}"):
        file_name = f"overlay_{secrets.token_urlsafe(16)}.mp4"

    subprocess.run(
        [
            "ffmpeg",
            "-y",
            "-framerate",
            str(24),
            "-i",
            f"{tmpdir}/frame-%04d.png",
            "-c:v",
            "libx264",
            f"./rvpc/static/videos/{file_name}",
        ],
        check=True,
    )

    return file_name


def edit_and_save(video_file: bytes, start: float, end: float) -> str:
    with NamedTemporaryFile(suffix=".webm", delete=False) as fp:
        fp.write(video_file)
        fp.flush()
        tpath = fp.name

    file_name = f"raw_{secrets.token_urlsafe(16)}.webm"

    while os.path.isfile(f"./rvpc/static/videos/{file_name}"):
        file_name = f"raw_{secrets.token_urlsafe(16)}.webm"

    try:
        subprocess.run(
            [
                "ffmpeg",
                "-y",
                "-ss",
                str(start),
                "-to",
                str(end),
                "-i",
                tpath,
                "-c",
                "copy",
                f"./rvpc/static/videos/{file_name}",
            ],
            check=True,
        )
    finally:
        os.remove(fp.name)
        return file_name


async def setup_playwright(p, token: str):
    browser = await p.chromium.launch(headless=True)
    ctx = await browser.new_context()
    page = await ctx.new_page()

    await ctx.add_cookies(
        [
            {
                "name": "user_session",
                "value": token,
                "httpOnly": True,
                "secure": False,
                "url": "http://127.0.0.1:8000",
            }
        ]
    )
    return browser, page


async def load_page(page: Page, video_id: str):
    await page.goto(f"http://127.0.0.1:8000/cards/your_card?id={video_id}")
    await sleep(0.5)
    await expect(
        page.locator(
            ".htmx-request, .htmx-settling, .htmx-swapping, .htmx-added"
        )
    ).to_have_count(0)


async def prepare_to_scrape(
    page: Page,
) -> tuple[ElementHandle | None, Locator]:
    rec_handle = await page.query_selector("#rec")

    await page.evaluate("rec => rec.pause()", rec_handle)
    await page.evaluate("rec => rec.currentTime = 0", rec_handle)
    card = page.locator("#card")

    return rec_handle, card


async def html_to_mp4(
    video_length: float, video_id: str, token: UserToken
) -> str:
    async with async_playwright() as p:
        browser, page = await setup_playwright(p, token)

        await load_page(page, video_id=video_id)

        total_frames = round(video_length * 24)
        rec_handle, card = await prepare_to_scrape(page)

        with TemporaryDirectory() as tmpdir:
            for i in range(total_frames):
                t = i / 24
                await page.evaluate("window")
                await page.evaluate(
                    """([rec, t]) => {
                        return new Promise((resolve) => {
                            rec.currentTime = t;
                            rec.onseeked = () => resolve();
                        });
                    }""",
                    [rec_handle, t],
                )
                await card.screenshot(path=f"{tmpdir}/frame-{i:04}.png")

            await browser.close()

            return frames_to_mp4(tmpdir=tmpdir)
