import random
import subprocess

import numpy as np


def get_frames_ffmpeg(
    video_path,
    size=None,
    fps=None,
    start_time=None,
    duration=None,
):
    """Extract frames using ffmpeg.

    Args:
        video_path: Path to input video
        size: Tuple of (width, height) for output frames
        fps: Target fps for output frames
        start_time: Start time in seconds to begin extraction from
        duration: Duration in seconds to extract (if None, extract until end)
    """
    if size is None:
        width, height = video_dimensions(video_path)
    else:
        width, height = size

    cmd = [
        "ffmpeg",
        "-hide_banner",
        "-loglevel",
        "error",
    ]

    if start_time is not None:
        cmd.extend(["-ss", str(start_time)])

    cmd.extend(["-i", video_path])

    if duration is not None:
        cmd.extend(["-t", str(duration)])

    if size is not None or fps is not None:
        cmd.append("-vf")
    vf = []
    if fps is not None:
        vf.append(f"fps={fps}")
    if size is not None:
        width, height = size
        vf.append(f"scale={width}:{height}")
    if vf:
        cmd.append(",".join(vf))

    cmd.extend(
        [
            "-f",
            "image2pipe",
            "-pix_fmt",
            "rgb24",
            "-vcodec",
            "rawvideo",
            "-",
        ]
    )

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    frame_size = width * height * 3

    try:
        while True:
            frame_bytes = proc.stdout.read(frame_size)
            if len(frame_bytes) != frame_size:
                break
            frame = np.frombuffer(frame_bytes, np.uint8).reshape((height, width, 3))
            yield frame
    finally:
        proc.stdout.close()
        proc.wait()


def get_video_duration(video_path: str) -> float:
    """Return video duration in seconds using ffprobe."""
    cmd = [
        "ffprobe",
        "-v",
        "error",
        "-select_streams",
        "v:0",
        "-show_entries",
        "format=duration",
        "-of",
        "csv=p=0",
        video_path,
    ]
    output = subprocess.check_output(cmd, timeout=10).decode("utf-8").strip()
    return float(output)


def get_video_frame_count(video_path: str) -> int:
    """Return total frame count using ffprobe."""
    cmd = [
        "ffprobe",
        "-v",
        "error",
        "-select_streams",
        "v:0",
        "-count_frames",
        "-show_entries",
        "stream=nb_read_frames",
        "-of",
        "csv=p=0",
        video_path,
    ]
    output = subprocess.check_output(cmd, timeout=30).decode("utf-8").strip()
    return int(output)


def get_video_fps(video_path: str) -> float:
    """Return video fps using ffprobe.

    Uses avg_frame_rate instead of r_frame_rate to handle VFR videos correctly.
    """
    cmd = [
        "ffprobe",
        "-v",
        "error",
        "-select_streams",
        "v:0",
        "-show_entries",
        "stream=avg_frame_rate",
        "-of",
        "csv=p=0",
        video_path,
    ]
    output = subprocess.check_output(cmd, timeout=10).decode("utf-8").strip()
    num, den = map(int, output.split("/"))
    return num / den


def video_dimensions(video_path: str) -> tuple[int, int]:
    probe_cmd = [
        "ffprobe",
        "-v",
        "error",
        "-select_streams",
        "v:0",
        "-show_entries",
        "stream=width,height",
        "-of",
        "csv=p=0:s=x",
        video_path,
    ]
    output = subprocess.check_output(probe_cmd, timeout=10).decode("utf-8")
    width, height = map(int, output.strip().split("x")[:2])
    return width, height


def download_s3_file(s3_path: str, local_path: str):
    """Download video from S3 to local path"""
    import boto3

    cli = boto3.client("s3")
    bucket, key = s3_path.replace("s3://", "").split("/", 1)
    cli.download_file(bucket, key, local_path)


def generate_unique_run_name() -> str:
    """Generate unique run name with city-animal format"""

    cities = [
        "london",
        "porto",
        "telaviv",
        "newyork",
        "losangeles",
        "chicago",
        "seattle",
        "boston",
        "miami",
        "vancouver",
        "toronto",
        "sydney",
        "melbourne",
        "brisbane",
        "auckland",
        "singapore",
        "hongkong",
        "vienna",
        "tokyo",
        "paris",
        "berlin",
        "madrid",
        "rome",
        "amsterdam",
        "stockholm",
        "copenhagen",
        "prague",
        "budapest",
        "warsaw",
        "barcelona",
        "milan",
        "munich",
        "zurich",
        "geneva",
        "brussels",
        "lisbon",
        "dublin",
        "helsinki",
        "oslo",
        "reykjavik",
        "athens",
        "istanbul",
        "moscow",
        "kyiv",
        "bucharest",
    ]

    animals = [
        "mongoose",
        "falcon",
        "tiger",
        "rabbit",
        "squid",
        "zebra",
        "leopard",
        "lynx",
        "wolf",
        "fox",
        "bear",
        "eagle",
        "hawk",
        "owl",
        "raven",
        "cobra",
        "viper",
        "panther",
        "jaguar",
        "cheetah",
        "lion",
        "dolphin",
        "shark",
        "whale",
        "octopus",
        "seahorse",
        "turtle",
        "penguin",
        "flamingo",
        "hummingbird",
        "sparrow",
        "robin",
    ]

    city = random.choice(cities)
    animal = random.choice(animals)

    return f"{city}-{animal}"
