import os
import logging
import requests

from PIL import Image
from io import BytesIO

from pytube import YouTube
from termcolor import colored

from django.views import View
from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import redirect,render

from apps.process.models import (File, Media, Status, UploadType,ProcessedMedia)
from apps.process.utils import (yt_down_allow, generate_name, generate_thumbnail)
from apps.process.tasks import (convert)
from celery.result import AsyncResult

from apps.subscriptions.utils import (valid_extensions, file_extension_valid, retrieve_limit_of_size, retrieve_limit_of_duration, has_reached_upload_limit)


# Create a logger for this file
logger = logging.getLogger(__file__)

# Create your views here.


def is_ajax(request):
    return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'

def normal_upload(serialize_user, media_path, file, fileName, existingPath, end, nextSlice, parts):
    #
    if file=="" or fileName=="" or existingPath=="" or end=="" or nextSlice=="":
        #
        res = {'data':'Invalid Request', 'status': 'failed'}
        return res
    else:
        if existingPath == 'null':
            # Saving File To Server DB
            FileFolder = File(existingPath = generate_name(fileName), eof=end, name=fileName)
            FileFolder.save()

            try:
                media = Media.objects.get(original_file=FileFolder , user=serialize_user)
            except Media.DoesNotExist:
                media = Media(original_file=FileFolder,title=fileName , user=serialize_user, upload_source=UploadType.Local)

            # Path Where The File Will Be Saved
            path = media_path + FileFolder.existingPath
            # Start Saveing The Fist Part Of The Sended File
            with open(path, 'wb+') as destination:
                destination.write(file)
            # TesT if The File Is Completed
            if int(end):
                # Update/Create Media Object For The user How Upladed This File
                media.status = Status.Processing
                media.thumbnail.save(f"thumbnail.jpg", BytesIO(generate_thumbnail(FileFolder.existingPath)), save=True)
                media.size = os.path.getsize(path)
                print(media.size)
                media.save()
                # start removing sound
                print(" start celery task ")
                convert_task = convert.apply_async(
                    args=[FileFolder.existingPath, parts, media.id_media, serialize_user.id],
                    # priority=settings.PRIORITY_STACK[user_plan]
                    )
                task_response = AsyncResult(convert_task.id)
                #  get final_video_id add it to dict res
                logger.info(f"Final Video ID:{task_response.get()}")
                res = {
                    'data':'Uploaded Successfully',
                    'existingPath':FileFolder.existingPath,
                    'status': 'success',
                    'final_video_id':task_response.get(),
                    "model_id":FileFolder.id_file,
                    "media_id":media.id_media
                    }
            else:
                # Update/Create Media Object For The user How Upladed This File
                media.status = Status.Uploading
                media.size = os.path.getsize(path)
                media.save()
                #
                res = {'existingPath':FileFolder.existingPath, 'status': 'uploading'}
            return res
        else:
            #
            path = media_path + existingPath
            #
            model_id = File.objects.get(existingPath=existingPath)
            #
            media = Media.objects.get(original_file=model_id)#, user=request.user)
            #
            if model_id.name == fileName:
                #
                if not model_id.eof:
                    #
                    with open(path, 'ab+') as destination:
                        destination.write(file)
                    #
                    if int(end):
                        model_id.eof = int(end)
                        model_id.save()
                        # Update/Create Media Object For The user How Upladed This File
                        media.status = Status.Processing

                        media.thumbnail.save(f"thumbnail.jpg", BytesIO(generate_thumbnail(existingPath)), save=True)
                        media.size = os.path.getsize(path)
                        media.save()
                        # start removing sound
                        convert_task = convert.apply_async(
                            args=[model_id.existingPath, parts, media.id_media,serialize_user.id],
                            # priority=settings.PRIORITY_STACK[user_plan]
                            )
                        task_response = AsyncResult(convert_task.id)
                        #  get final_video_id add it to dict res
                        logger.info(f"Final Video ID:{task_response.get()}")
                        res = {
                            'data':'Uploaded Successfully',
                            'existingPath':model_id.existingPath,
                            'status': 'success',
                            'final_video_id':task_response.get(),
                            'task_id':convert_task.id,
                            "model_id":model_id.id_file,
                            "media_id":media.id_media
                            }
                    else:
                        #
                        # Update/Create Media Object For The user How Upladed This File
                        media.status = Status.Uploading
                        media.size = os.path.getsize(path)
                        media.save()
                        #
                        res = {'existingPath':model_id.existingPath, 'status': 'uploading','model_id':model_id.id_file}
                    return res
                else:
                    #
                    # Update/Create Media Object For The user How Upladed This File
                    media.status = Status.Failed
                    media.size = os.path.getsize(f"media/original_videos/{FileFolder.existingPath}")
                    media.save()
                    #
                    return {'data':'EOF found. Invalid request','status': 'failed'}
            else:
                # Update/Create Media Object For The user How Upladed This File
                media.status = Status.Failed
                media.size = os.path.getsize(path)
                media.save()
                return {'data':'No such file exists in the existingPath', 'status': 'failed'}


def youtube_upload(serialize_user, media_path, video_id, parts):

    try:
        # media = Media.objects.get(id_media=video_id, user=request.user)
        media = Media.objects.filter(youtube_id=video_id)[:1].get()  # .first() [:1] .last()
    except Media.DoesNotExist:
        media = Media(youtube_id=video_id, user=serialize_user, upload_source=UploadType.YouTube)

    if media.original_file and media.original_file != None and media.audio_path != None:
        new_media = Media(
            youtube_id    = video_id,
            user          = serialize_user,
            title         = media.title,
            author        = media.author,
            original_file = media.original_file,
            thumbnail     = media.thumbnail,
            audio_path    = media.audio_path,
            size          = media.size,
            status        = Status.Processing,
            upload_source =UploadType.YouTube
        )
        new_media.save()
        convert.apply_async(args=[new_media.audio_path, parts, new_media.id_media, serialize_user.id])
        return {"data":"inserted Video is already downloaded and it will be proccesed", 'status': 'success'}
    else:
        media.user = serialize_user
        FileFolder = File(existingPath = f"{media_path}{video_id}.mp4")
        try:
            # getting link from frontend
            try:
                video = YouTube(f"https://www.youtube.com/watch?v={video_id}")
            except VideoUnavailable:
                logger.info(f'Video {video} is unavaialable, skipping.')
                return {'status':'error','message':f'Video {video} is unavaialable'}
            # youtube_path = f"{settings.MEDIA_ROOT}/origin_videos"
            audio_path = f"{settings.MEDIA_ROOT}/original_audios"

            # Downlaod thumbnail
            if not os.path.exists("media/thumbnails/"):
                os.makedirs("media/thumbnails/")

            # thumbnail :: get response from youtube
            response = requests.get(f"https://i.ytimg.com/vi/{video_id}/maxresdefault.jpg",verify=False)

            # check if folder with media-file-name exists
            # avoid video path check
            if not os.path.exists(audio_path):
                os.mkdir(audio_path)
        

            # downloads video
            stream = video.streams.order_by('resolution').desc().first()
            #
            FileFolder.name = f"{video.title}.mp4"

            stream.download(output_path=media_path,filename=f"{video_id}.mkv")
            # create instance File
            FileFolder.eof = True
            FileFolder.save()

            # downloads audio
            audio = video.streams.filter(only_audio=True).desc().first()
            audio.download(audio_path,filename=f'{video_id}.mp3')
           
            # download(self, output_path=None, filename=None):

            #
            os.system(f"ffmpeg -hide_banner -y -i {media_path}{video_id}.mkv -i {audio_path}/{video_id}.mp3 -map 0:v -map 1:a -c:v copy -shortest {media_path}{video_id}.mp4")
            os.remove(f"{media_path}{video_id}.mkv")

            # Add Video-ID In Session To Use It For YouTube-Upload
            # request.session['video_id'] = video_id

            # Save Video-Media-Info In Our DataBase

            media.title = video.title
            media.author = video.author
            # media.publish_date = video.publish_date.strftime("%Y-%m-%d")
            # media.duration = video.length
            media.original_file = FileFolder

            # media.thumbnail = thumbnail_path
            media.thumbnail.save(f"{video_id}_thumbnail.jpg", BytesIO(response.content), save=True)

            media.audio_path =  f"{audio_path}/{video_id}.mp3" # f"{video_id}.mp3"
            media.size = os.path.getsize(FileFolder.existingPath)
            media.status = Status.Processing
            media.save()


            print(colored(media,'red'))
            print(colored('calling celery...',"blue"))

            convert.apply_async(args=[media.audio_path, parts, media.id_media,serialize_user.id])
            return {"data":"video downloaded and it will be proccesed", 'status': 'success'}
        except Exception as e:
            FileFolder.eof = False
            FileFolder.save()
            return {'status':'failed','message':'video not downloaded'}


class ProcessView(View):
    """ Process view """
    def get(self, request, *args, **kwargs):
        # code to process a GET request:
        if not request.user.is_authenticated:
            return redirect('accounts:signin')

    def post(self, request, *args, **kwargs):
        # code to process a POST request
        if not request.user.is_authenticated:
            return redirect('accounts:signin')
        # code to process a POST request
        if is_ajax(request):

            #
            serialize_user = request.user
            response = {}

            # limit checks (nbr_videos_per_plan, file_duration, file_size, file_extention, )


            # Check if current user has reached the limit of plan videos per day
            has_reached_limit = has_reached_upload_limit(serialize_user)
            # logging data_duration
            logger.info(has_reached_limit)
            if has_reached_limit["status"]:
                # Check if current user has reached the limit of plan video duration
                data_duration = retrieve_limit_of_duration(float(request.POST['file_duration']), serialize_user)
                # logging data_duration
                logger.info(data_duration)
                if data_duration["status"]:

                    # Cheking if the path to upload video exist
                    media_path = "media/original_videos/"
                    if not os.path.exists(media_path):
                        os.makedirs(media_path)

                    if request.POST['upload_type'] == "normal":
                        # Cheking if current video has reached the limit of plan video_size
                        data_size = retrieve_limit_of_size(float(request.POST['file_size'])*0.001, serialize_user)
                        # logging data_duration
                        logger.info(data_size)
                        # if data_size["status"]:
                            # check file size and duration
                            # if file_extension_valid(request):
                                # response = {'status':'success','message':'video has a valid Extention'}
                            # handle normal upload
                        file         = request.FILES['file'].read()
                        fileName     = request.POST['filename']
                        existingPath = request.POST['existingPath']
                        end          = request.POST['end']
                        nextSlice    = request.POST['nextSlice']
                        parts        = request.POST['parts']
                        
                        duration     = float(request.POST['file_duration'])
                        #
                        response = normal_upload(serialize_user, media_path, file, fileName, existingPath, end, nextSlice, parts)
                        logger.info("Parts: ", parts)
                            # else:
                                # response = {'status':'failed','message':'Video Exention not valid'}
                        # else :
                        #     response =  {'data':'current User has reached the limit of plan video duration.', 'status': 'failed', 'suggest':f"{data_duration['message']} \n Upgrade Your Plan Or Contact The Support For More Detailes."}

                    if request.POST['upload_type'] == "youtube" and yt_down_allow(serialize_user.email):
                        # check file size and duration (Api)
                        video_id = request.POST.get('video_id')
                        parts = request.POST.get("parts")
                        #
                        response = youtube_upload(serialize_user, media_path, video_id, parts)
                else :
                    response =  {'data':'current User has reached the limit of plan video duration.', 'status': 'failed', 'suggest':f"{data_duration['message']} \n Upgrade Your Plan Or Contact The Support For More Detailes."}
            else :
               response =  {'data':'User has reached the limit of videos per day.', 'status': 'failed', 'suggest':'Upgrade Your Plan Or Contact The Support For More Detailes.'}
            #
            return JsonResponse(response)


# add new voice enhacement but without limits
class VoiceEnhancementView(View):
    def get(self, request, *args, **kwargs):
        # code to process a GET request:
        if not request.user.is_authenticated:
            return redirect('accounts:signin')
        return render(request,'enhancement.html')

    def post(self, request, *args, **kwargs):
        # code to process a POST request
        if not request.user.is_authenticated:
            return redirect('accounts:signin')

        # code to process a POST request
        if is_ajax(request):

            logger.info("Recieve from Enhancement views")
            serialize_user = request.user
            response = {}

            # limit checks (nbr_videos_per_plan, file_duration, file_size, file_extention, )


            # Check if current user has reached the limit of plan videos per day
            has_reached_limit = has_reached_upload_limit(serialize_user)
            # logging data_duration
            logger.info(has_reached_limit)

            # if has_reached_limit["status"]:
            # Check if current user has reached the limit of plan video duration
            # data_duration = retrieve_limit_of_duration(float(request.POST['file_duration']), serialize_user)
            # logging data_duration
            # logger.info(data_duration)
            # if data_duration["status"]:

            # Cheking if the path to upload video exist
            media_path = "media/original_videos/"
            if not os.path.exists(media_path):
                os.makedirs(media_path)

            if request.POST['upload_type'] == "normal":
                # Cheking if current video has reached the limit of plan video_size
                data_size = retrieve_limit_of_size(float(request.POST['file_size'])*0.001, serialize_user)
                # logging data_duration
                logger.info(data_size)
                # if data_size["status"]:
                    # check file size and duration
                    # if file_extension_valid(request):
                        # response = {'status':'success','message':'video has a valid Extention'}
                    # handle normal upload
                file         = request.FILES['file'].read()
                fileName     = request.POST['filename']
                existingPath = request.POST['existingPath']
                end          = request.POST['end']
                nextSlice    = request.POST['nextSlice']
                parts        = request.POST['parts']
                duration     = float(request.POST['file_duration'])
                
                #
                # !
                user_plan = request.user.subscription.plan.plan_type

                response = normal_upload(serialize_user, media_path, file, fileName, existingPath, end, nextSlice, parts,) # user_plan)
                        # else:
                            # response = {'status':'failed','message':'Video Exention not valid'}
                    # else :
                    #     response =  {'data':'current User has reached the limit of plan video duration.', 'status': 'failed', 'suggest':f"{data_duration['message']} \n Upgrade Your Plan Or Contact The Support For More Detailes."}

                if request.POST['upload_type'] == "youtube" and yt_down_allow(serialize_user.email):
                    # check file size and duration (Api)
                    video_id = request.POST.get('video_id')
                    parts = request.POST.get("parts")
                    #
                    response = youtube_upload(serialize_user, media_path, video_id, parts)
            # else :
            #     response =  {'data':'current User has reached the limit of plan video duration.', 'status': 'failed', 'suggest':f"{data_duration['message']} \n Upgrade Your Plan Or Contact The Support For More Detailes."}
            # else :
            #    response =  {'data':'User has reached the limit of videos per day.', 'status': 'failed', 'suggest':'Upgrade Your Plan Or Contact The Support For More Detailes.'}
            #
            return JsonResponse(response)

class CancelMediaView(View):
    def post(self,request,*args,**kwargs):
        pass