#include "ffmpeg_cli.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>

#define MAX_CMD_LEN 4096
#define MAX_ERROR_LEN 512

static char last_error[MAX_ERROR_LEN] = {0};

static void set_error(const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    vsnprintf(last_error, MAX_ERROR_LEN, fmt, args);
    va_end(args);
}

static bool file_exists(const char* path) {
    struct stat st;
    return stat(path, &st) == 0;
}

static bool ensure_dir(const char* path) {
    struct stat st;
    if (stat(path, &st) == 0) {
        return S_ISDIR(st.st_mode);
    }
    return mkdir(path, 0755) == 0;
}

static int run_command(const char* cmd) {
    int ret = system(cmd);
    if (ret == -1) {
        set_error("Failed to execute command: %s", strerror(errno));
        return -1;
    }
    return WEXITSTATUS(ret);
}

ImageConvertOpts ffmpeg_image_opts_default(void) {
    return (ImageConvertOpts){
        .quality = 80,
        .max_width = 0,
        .max_height = 0,
        .preserve_aspect = true,
    };
}

HlsConvertOpts ffmpeg_hls_opts_default(void) {
    return (HlsConvertOpts){
        .segment_duration = 6,
        .video_bitrate = 0,
        .audio_bitrate = 128,
        .max_width = 0,
        .max_height = 0,
        .generate_thumbnail = true,
    };
}

bool ffmpeg_is_available(void) {
    return system("ffmpeg -version > /dev/null 2>&1") == 0;
}

const char* ffmpeg_last_error(void) {
    return last_error;
}

FfmpegResult ffmpeg_image_to_webp(const char* input_path, const char* output_path, ImageConvertOpts* opts) {
    if (!input_path || !output_path) {
        set_error("Invalid arguments: input_path and output_path required");
        return FFMPEG_ERR_INVALID_ARGS;
    }

    if (!file_exists(input_path)) {
        set_error("Input file not found: %s", input_path);
        return FFMPEG_ERR_INPUT_NOT_FOUND;
    }

    ImageConvertOpts default_opts = ffmpeg_image_opts_default();
    if (!opts) opts = &default_opts;

    char cmd[MAX_CMD_LEN];
    char scale_filter[256] = "";

    // Build scale filter if resizing
    if (opts->max_width > 0 || opts->max_height > 0) {
        int w = opts->max_width > 0 ? opts->max_width : -1;
        int h = opts->max_height > 0 ? opts->max_height : -1;

        if (opts->preserve_aspect) {
            if (w > 0 && h > 0) {
                snprintf(scale_filter, sizeof(scale_filter),
                    "-vf \"scale='min(%d,iw)':min'(%d,ih)':force_original_aspect_ratio=decrease\"", w, h);
            } else if (w > 0) {
                snprintf(scale_filter, sizeof(scale_filter), "-vf \"scale='min(%d,iw)':-1\"", w);
            } else {
                snprintf(scale_filter, sizeof(scale_filter), "-vf \"scale=-1:'min(%d,ih)'\"", h);
            }
        } else {
            snprintf(scale_filter, sizeof(scale_filter), "-vf \"scale=%d:%d\"", w, h);
        }
    }

    snprintf(cmd, sizeof(cmd),
        "ffmpeg -y -i \"%s\" %s -quality %d \"%s\" 2>/dev/null",
        input_path, scale_filter, opts->quality, output_path);

    int ret = run_command(cmd);
    if (ret != 0) {
        set_error("FFmpeg conversion failed with code %d", ret);
        return FFMPEG_ERR_PROCESS_FAILED;
    }

    if (!file_exists(output_path)) {
        set_error("Output file was not created");
        return FFMPEG_ERR_OUTPUT_FAILED;
    }

    return FFMPEG_OK;
}

FfmpegResult ffmpeg_video_to_hls(const char* input_path, const char* output_dir, const char* name, HlsConvertOpts* opts) {
    if (!input_path || !output_dir || !name) {
        set_error("Invalid arguments: input_path, output_dir, and name required");
        return FFMPEG_ERR_INVALID_ARGS;
    }

    if (!file_exists(input_path)) {
        set_error("Input file not found: %s", input_path);
        return FFMPEG_ERR_INPUT_NOT_FOUND;
    }

    if (!ensure_dir(output_dir)) {
        set_error("Failed to create output directory: %s", output_dir);
        return FFMPEG_ERR_OUTPUT_FAILED;
    }

    HlsConvertOpts default_opts = ffmpeg_hls_opts_default();
    if (!opts) opts = &default_opts;

    char cmd[MAX_CMD_LEN];
    char scale_filter[256] = "";
    char video_bitrate[64] = "";
    char audio_bitrate[64] = "";

    // Build scale filter if resizing
    if (opts->max_width > 0 || opts->max_height > 0) {
        int w = opts->max_width > 0 ? opts->max_width : -2;
        int h = opts->max_height > 0 ? opts->max_height : -2;
        snprintf(scale_filter, sizeof(scale_filter), "-vf \"scale=%d:%d\"", w, h);
    }

    // Video bitrate
    if (opts->video_bitrate > 0) {
        snprintf(video_bitrate, sizeof(video_bitrate), "-b:v %dk", opts->video_bitrate);
    }

    // Audio bitrate
    snprintf(audio_bitrate, sizeof(audio_bitrate), "-b:a %dk",
        opts->audio_bitrate > 0 ? opts->audio_bitrate : 128);

    char playlist_path[1024];
    char segment_pattern[1024];
    snprintf(playlist_path, sizeof(playlist_path), "%s/%s.m3u8", output_dir, name);
    snprintf(segment_pattern, sizeof(segment_pattern), "%s/%s_%%03d.ts", output_dir, name);

    // Main HLS conversion command
    snprintf(cmd, sizeof(cmd),
        "ffmpeg -y -i \"%s\" "
        "-c:v libx264 -preset fast -crf 22 %s %s "
        "-c:a aac %s "
        "-f hls "
        "-hls_time %d "
        "-hls_list_size 0 "
        "-hls_segment_filename \"%s\" "
        "\"%s\" 2>/dev/null",
        input_path,
        scale_filter, video_bitrate,
        audio_bitrate,
        opts->segment_duration,
        segment_pattern,
        playlist_path);

    int ret = run_command(cmd);
    if (ret != 0) {
        set_error("FFmpeg HLS conversion failed with code %d", ret);
        return FFMPEG_ERR_PROCESS_FAILED;
    }

    // Generate thumbnail if requested
    if (opts->generate_thumbnail) {
        char thumb_path[1024];
        snprintf(thumb_path, sizeof(thumb_path), "%s/%s_thumb.jpg", output_dir, name);
        ffmpeg_video_thumbnail(input_path, thumb_path, 1);
    }

    return FFMPEG_OK;
}

FfmpegResult ffmpeg_video_thumbnail(const char* input_path, const char* output_path, int timestamp_seconds) {
    if (!input_path || !output_path) {
        set_error("Invalid arguments: input_path and output_path required");
        return FFMPEG_ERR_INVALID_ARGS;
    }

    if (!file_exists(input_path)) {
        set_error("Input file not found: %s", input_path);
        return FFMPEG_ERR_INPUT_NOT_FOUND;
    }

    char cmd[MAX_CMD_LEN];
    snprintf(cmd, sizeof(cmd),
        "ffmpeg -y -i \"%s\" -ss %d -vframes 1 -q:v 2 \"%s\" 2>/dev/null",
        input_path, timestamp_seconds, output_path);

    int ret = run_command(cmd);
    if (ret != 0) {
        set_error("FFmpeg thumbnail generation failed with code %d", ret);
        return FFMPEG_ERR_PROCESS_FAILED;
    }

    return FFMPEG_OK;
}

FfmpegResult ffmpeg_get_duration(const char* input_path, double* duration_out) {
    if (!input_path || !duration_out) {
        set_error("Invalid arguments");
        return FFMPEG_ERR_INVALID_ARGS;
    }

    if (!file_exists(input_path)) {
        set_error("Input file not found: %s", input_path);
        return FFMPEG_ERR_INPUT_NOT_FOUND;
    }

    char cmd[MAX_CMD_LEN];
    snprintf(cmd, sizeof(cmd),
        "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"%s\" 2>/dev/null",
        input_path);

    FILE* fp = popen(cmd, "r");
    if (!fp) {
        set_error("Failed to run ffprobe");
        return FFMPEG_ERR_PROCESS_FAILED;
    }

    char output[64];
    if (fgets(output, sizeof(output), fp) != NULL) {
        *duration_out = atof(output);
    } else {
        *duration_out = 0;
    }

    pclose(fp);
    return FFMPEG_OK;
}
