import logging
import os
import sys
from pathlib import Path

import pandas as pd

from .. import report
from ..mediainfo import ffprobe
from ..tools import (change_width_height_mp4, convert_mp4_aac,
                     convert_mp4_wo_reencode, convert_only_audio)
from ..utils import (create_report_backup, exclude_all_files_from_folder,
                     get_file_name_dest)


def logging_config():

    log_file_name = "reencode_maker"
    logfilename = "log-" + log_file_name + ".txt"
    logging.basicConfig(
        level=logging.INFO,
        format=" %(asctime)s-%(levelname)s-%(message)s",
        handlers=[logging.FileHandler(logfilename, "w", "utf-8")],
    )
    # set up logging to console
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    # set a format which is simpler for console use
    formatter = logging.Formatter(" %(asctime)s-%(levelname)s-%(message)s")
    console.setFormatter(formatter)
    # add the handler to the root logger
    logging.getLogger("").addHandler(console)


def create_backup_metadata_columns(df):
    """Saves the current state of the columns below,
    adding the suffix "_origin":
    ["file_path_folder",
     "file_name",
     "file_size",
     "video_resolution_height",
     "video_resolution_width]

    Args:
        df (dataframe): report dataframe

    Returns:
        dataframe: dataframe updated with columns backup
    """

    # Ensure creation of column bellow
    list_backup_columns = (
        "file_path_folder",
        "file_name",
        "file_size",
        "video_resolution_height",
        "video_resolution_width",
    )

    for column_name in list_backup_columns:
        new_column_name = column_name + "_origin"
        if new_column_name not in df.columns:
            df[new_column_name] = df[column_name]
    return df


def get_next_video_to_reencode(path_file_report):

    # load dataframe
    try:
        df = pd.read_csv(path_file_report)
    except Exception as e:
        logging.error(f"Can't open file: {path_file_report}")
        logging.error(e)

    # create mask to reencode
    mask_df_to_reencode = ~df["video_resolution_to_change"].isna()
    mask_df_reencode_not_done = df["reencode_done"].isin([0])
    mask_df_to_reencode = mask_df_to_reencode & mask_df_reencode_not_done

    # filter df to reencode
    df_to_reencode = df.loc[mask_df_to_reencode, :]

    # check if there is videos to reencode. Return False if note
    qt_videos_to_reencode = df_to_reencode.shape[0]
    if qt_videos_to_reencode == 0:
        return False

    # get first line as dict
    df_to_reencode = df_to_reencode.reset_index(drop=True)
    dict_first_line = df_to_reencode.loc[0, :]
    return dict_first_line


def reencode_video(dict_, path_folder_encoded):
    """reencode videos

    Args:
        dict_ (dataframe): columns: [video_resolution_to_change,
                                     file_path_folder_origin,
                                     file_name_origin]
        path_folder_encoded (str): path_folder destination for reencoded videos
    Return:
        (boolean): False if error.
    """
    try:
        video_resolution_to_change = dict_["video_resolution_to_change"]
        size_width, size_height = video_resolution_to_change.split("x")
    except:
        path_file_origin = os.path.join(
            dict_["file_path_folder_origin"], dict_["file_name_origin"]
        )
        logging.error(
            "Parse. Column video_resolution_to_change: "
            + f'"{video_resolution_to_change}". '
            f"File:\n{path_file_origin}"
        )
        return False

    # TODO: Change to read only path_folder column
    file_folder_origin = dict_["file_path_folder_origin"]
    file_name_origin = dict_["file_name_origin"]
    path_file_origin = os.path.join(file_folder_origin, file_name_origin)
    # TODO: change to param path_folder
    file_name_dest = get_file_name_dest(
        Path(file_folder_origin), file_name_origin, "reencode_", "mp4"
    )

    path_folder_dest = path_folder_encoded
    path_file_dest = os.path.join(path_folder_dest, file_name_dest)

    video_codec = dict_["video_codec"]
    audio_codec = dict_["audio_codec"]
    format_name = dict_["format_name"]
    video_height = dict_["video_resolution_height"]
    video_width = dict_["video_resolution_width"]
    if (
        video_codec == "h264"
        and audio_codec == "aac"
        and video_height == size_height
        and video_width == size_width
    ):
        logging.info("Start conversion without reencode: %s", path_file_origin)
        convert_mp4_wo_reencode(path_file_origin, path_file_dest)
    elif video_codec == "h264" and format_name == "mov,mp4,m4a,3gp,3g2,mj2":
        logging.info("Start conversion only audio: %s", path_file_origin)
        convert_only_audio(path_file_origin, path_file_dest)
    elif video_height == size_height and video_width == size_width:
        logging.info("Start reencode: %s", path_file_origin)
        convert_mp4_aac(path_file_origin, path_file_dest)
    else:
        logging.info(
            "Start reencode changing resolution: %s", path_file_origin
        )
        change_width_height_mp4(
            path_file_origin, size_height, size_width, path_file_dest
        )


def ask_for_delete_old_videos_encode(path_folder_encoded):

    for _, _, files in os.walk(path_folder_encoded):

        list_file_name_encoded = list(files)

    len_list_file_name_encoded = len(list_file_name_encoded)
    if len_list_file_name_encoded > 0:
        print(
            f"\n{path_folder_encoded}\n"
            + "There is files in videos_encoded folder.\n"
            + "Do you wish delete them?"
        )
        answer_delete = input("(None for yes) Answer: ")

        if answer_delete == "":
            confirm_delete = input(
                "\nType Enter to delete all " + "video_encoded files."
            )
            if confirm_delete == "":
                exclude_all_files_from_folder(path_folder_encoded)
            else:
                pass


def update_file_report(path_file_report, dict_video_data, path_folder_encoded):

    try:
        df = pd.read_csv(path_file_report)
    except Exception as e:
        logging.error(f"Can't open file: {path_file_report}")
        logging.error(e)

    # find path_folder_dest and path_file_dest
    file_folder_origin = dict_video_data["file_path_folder_origin"]
    file_name_origin = dict_video_data["file_name_origin"]
    path_file_origin = os.path.join(file_folder_origin, file_name_origin)
    file_name_dest = get_file_name_dest(
        Path(file_folder_origin), file_name_origin, "reencode_", "mp4"
    )
    path_folder_dest = path_folder_encoded
    path_file_dest = os.path.join(path_folder_dest, file_name_dest)

    # Check if file_name_dest exist
    test_file_exist = os.path.isfile(path_file_dest)
    if test_file_exist is False:
        logging.error(
            "After reencode, when update, "
            + f"reencoded file not exist:\n{path_file_dest}"
        )
        sys.exit()

    # locate index video in df
    mask_file_folder = df["file_path_folder"].isin([file_folder_origin])
    mask_file_name = df["file_name_origin"].isin([file_name_origin])
    mask_line = mask_file_folder & mask_file_name
    df_filter = df.loc[mask_line, :]
    len_df_filter = len(df_filter)
    if len_df_filter != 1:
        logging.error(
            f"Need 1. Find {len_df_filter} line for "
            f"video: {path_file_origin}"
        )
        sys.exit()
    index_video = df_filter.index

    # update df
    df.loc[index_video, "file_path_folder"] = os.path.abspath(path_folder_dest)
    df.loc[index_video, "file_name"] = file_name_dest

    file_size = os.stat(path_file_dest).st_size
    video_resolution_to_change = dict_video_data["video_resolution_to_change"]

    df.loc[index_video, "file_size"] = file_size
    df.loc[index_video, "video_resolution"] = video_resolution_to_change
    df.loc[
        index_video, "video_resolution_width"
    ] = video_resolution_to_change.split("x")[0]
    df.loc[
        index_video, "video_resolution_height"
    ] = video_resolution_to_change.split("x")[1]

    # get video metadata
    dict_inf_ffprobe = {}
    inf_ffprobe = ffprobe(path_file_dest).get_output_as_dict()
    dict_inf_ffprobe["path_file"] = path_file_dest
    dict_inf_ffprobe["metadata"] = inf_ffprobe
    list_dict = report.generate([dict_inf_ffprobe])
    metadata = list_dict[0]
    df.loc[index_video, "total_bitrate"] = metadata["total_bitrate"]
    df.loc[index_video, "video_bitrate"] = metadata["video_bitrate"]
    df.loc[index_video, "video_codec"] = metadata["video_codec"]
    df.loc[index_video, "video_profile"] = metadata["video_profile"]
    df.loc[index_video, "format_name"] = metadata["format_name"]
    df.loc[index_video, "is_avc"] = metadata["is_avc"]
    df.loc[index_video, "audio_codec"] = metadata["audio_codec"]
    df.loc[index_video, "duration"] = metadata["duration"]
    df.loc[index_video, "duration_seconds"] = metadata["duration_seconds"]
    df.loc[index_video, "reencode_done"] = 1
    return df


def make_reencode(path_file_report, path_folder_videos_encoded):

    path_folder_encoded = path_folder_videos_encoded

    ask_for_delete_old_videos_encode(path_folder_encoded)

    df = pd.read_csv(path_file_report)
    # Ensure creation of column 'reencode_done'.
    if "reencode_done" not in df.columns:
        df["reencode_done"] = 0
        df = create_backup_metadata_columns(df)
        # Save reports
        df.to_csv(path_file_report, index=False)

    create_report_backup(
        df=df, path_file_report=path_file_report, tag="2_reencode"
    )

    need_reencode = True
    while need_reencode:
        return_next_video_to_reencode = get_next_video_to_reencode(
            path_file_report
        )
        if return_next_video_to_reencode is False:
            logging.info("\nThere are no videos to reencode")
            need_reencode = False
            continue

        # run reencode
        dict_video_data = return_next_video_to_reencode
        return_reencode_video = reencode_video(
            dict_video_data, path_folder_encoded
        )
        # if fails, terminate
        if return_reencode_video is False:
            sys.exit()
            return

        # after reencode, update metadata in report, with new videos generated
        df = update_file_report(
            path_file_report, dict_video_data, path_folder_encoded
        )

        # Save reports
        df.to_csv(path_file_report, index=False)
        create_report_backup(
            df=df, path_file_report=path_file_report, tag="2_reencode"
        )
    return df
