import cv2
import numpy as np
import tempfile
import os
import requests

from collections import defaultdict
from script.writeJSON import writeReport

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import (
    Input,
    Conv2D,
    MaxPooling2D,
    Dropout,
    Flatten,
    Dense
)


emotion_dict = {
    0: "Angry",
    1: "Disgusted",
    2: "Fearful",
    3: "Happy",
    4: "Neutral",
    5: "Sad",
    6: "Surprised"
}


# ==========================
# MODEL
# ==========================

def build_model():

    model = Sequential([

        Input(shape=(48, 48, 1)),

        Conv2D(
            32,
            (3, 3),
            activation="relu"
        ),

        Conv2D(
            64,
            (3, 3),
            activation="relu"
        ),

        MaxPooling2D(),

        Dropout(0.25),

        Conv2D(
            128,
            (3, 3),
            activation="relu"
        ),

        MaxPooling2D(),

        Conv2D(
            128,
            (3, 3),
            activation="relu"
        ),

        MaxPooling2D(),

        Dropout(0.25),

        Flatten(),

        Dense(
            1024,
            activation="relu"
        ),

        Dropout(0.5),

        Dense(
            7,
            activation="softmax"
        )

    ])

    model.load_weights(
        "model/emotion_model.h5"
    )

    return model


# ==========================
# DOWNLOAD
# ==========================

def download(url, suffix):

    temp = tempfile.NamedTemporaryFile(
        suffix=suffix,
        delete=False
    )

    response = requests.get(
        url,
        stream=True,
        timeout=60
    )

    response.raise_for_status()

    for chunk in response.iter_content(
        1024 * 1024
    ):
        temp.write(chunk)

    temp.close()

    return temp.name


# ==========================
# ANALYSIS
# ==========================

def videoAnalyze(
    vid,
    vid_url,
    profile_url=None
):

    temp_files = []

    try:

        print("\nSTART ANALYSIS")

        model = build_model()

        video = download(
            vid_url,
            ".mp4"
        )

        temp_files.append(
            video
        )

        cap = cv2.VideoCapture(
            video
        )

        detector = cv2.CascadeClassifier(
            "haarcascades/haarcascade_frontalface_default.xml"
        )

        emotions = defaultdict(int)

        total_frames = 0
        processed_frames = 0
        faces_detected = 0
        emotion_frames = 0

        FRAME_SKIP = 5

        while True:

            ret, frame = cap.read()

            if not ret:
                break

            total_frames += 1

            # PROCESS EVERY 5TH FRAME
            if total_frames % FRAME_SKIP != 0:
                continue

            processed_frames += 1

            gray = cv2.cvtColor(
                frame,
                cv2.COLOR_BGR2GRAY
            )

            faces = detector.detectMultiScale(

                gray,

                scaleFactor=1.1,

                minNeighbors=7,

                minSize=(100, 100)

            )

            if len(faces) == 0:
                continue

            x, y, w, h = max(

                faces,

                key=lambda f: f[2] * f[3]

            )

            faces_detected += 1

            face = gray[
                y:y+h,
                x:x+w
            ]

            try:

                face = cv2.equalizeHist(
                    face
                )

                face = cv2.resize(
                    face,
                    (48, 48)
                )

                face = (
                    face.astype(
                        np.float32
                    )
                    / 255.0
                )

                face = face.reshape(
                    1,
                    48,
                    48,
                    1
                )

                pred = model.predict(
                    face,
                    verbose=0
                )[0]

                confidence = float(
                    np.max(pred)
                )

                # REQUIRE STRONG CONFIDENCE

                if confidence < 0.65:
                    continue

                emotion = emotion_dict[
                    np.argmax(pred)
                ]

                emotions[
                    emotion
                ] += 1

                emotion_frames += 1

                if emotion_frames % 20 == 0:

                    print(
                        f"Frame {total_frames} | "
                        f"{emotion} | "
                        f"{round(confidence,2)}"
                    )

            except Exception as e:

                print(
                    "Prediction failed:",
                    e
                )

        cap.release()

        dominant = "NO_FACE"

        percentage = 0

        if emotion_frames > 0:

            dominant = max(

                emotions,

                key=emotions.get

            )

            percentage = round(

                emotions[
                    dominant
                ]

                * 100

                / emotion_frames,

                1

            )

        report = {

            "video": vid,

            "status": "done",

            "total_frames": total_frames,

            "processed_frames": processed_frames,

            "faces_detected": faces_detected,

            "emotion_frames": emotion_frames,

            "dominant_emotion": dominant,

            "emotion_percentage": percentage,

            "emotions": dict(
                emotions
            )

        }

        writeReport(
            vid,
            report
        )

        print(
            "\nREPORT SAVED"
        )

    except Exception as e:

        writeReport(

            vid,

            {

                "status": "failed",

                "error": str(e)

            }

        )

        print(
            "\nFAILED:",
            e
        )

    finally:

        for f in temp_files:

            try:

                os.remove(
                    f
                )

            except:
                pass