import datetime
import os
import random
import re
import string
import subprocess
import time
import json
import logging

from django.conf import settings
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

from program_app.helpers import generate_filename, python_output_with_input, c_output_with_input, cpp_output_with_input, \
    java_output_with_input, javascript_output_with_input, evl_nodejs_api, nodejs_code_eval, dart_output_with_input,\
    golang_output_with_input, c_sharp_output_with_input, execute_react_js, example_react_code, example_selenium, \
    get_static_data, get_code_executed_by_openai

logger = logging.getLogger('program_execute')

@csrf_exempt
def execute_programs(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        try:
            language = data.get("language")
            code = data.get('code')
            if language == 'python':
                output = python_output(code)
            elif language == 'c':
                output = c_output(code)
            elif language == 'c++':
                output = cpp_output(code)
            elif language == 'java':
                output = java_output(code)
            elif language == 'golang':
                output = golang_output(code)
            elif language == 'dart':
                output = dart_output(code)
            else:
                output = 'Program language not supported.'
            response = {'status': 'Success', 'status_code': 200, 'message': output}
        except Exception as ex:
            response = {'status': 'Failure', 'status_code': 401,
                        'message': 'Data issue, Please try again.'}
    else:
        response = {'status': 'Failure', 'status_code': 405, 'message': 'Method Not Allowed.'}
    return HttpResponse(json.dumps(response), content_type='application/javascript')


def python_output(code):
    file_name = generate_filename() + '.py'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['python3', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
        try:
            os.remove(file_name)
        except:
            pass
    except:
        output = ''
    return str(output)
    # return str(output, encoding='UTF-8')


def java_output(code):
    output = ''
    file_name = generate_filename() + '.java'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['java', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
    except:
        output = ''
    finally:
        try:
            os.remove(file_name)
        except Exception as ex:
            pass

    return str(output)


def c_output(code):
    try:
        file_name = generate_filename()
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name + ".c", 'w')
        f.write(code)
        f.close()
        compl = subprocess.Popen(['gcc', '-o', file_name, file_name + '.c'], stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
        compl_output = compl.communicate()[0]
        if b'warning' in compl_output.lower():
            output = compl_output + b'\n'
            try:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n = 0
                while 1:
                    if out.poll() is not None:
                        output += out.stdout.read()
                        break
                    time.sleep(1)
                    n += 1
                    if n >= 5:
                        out.kill()
            except:
                output = compl_output

        else:
            if compl_output:
                output = compl_output
            else:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n1 = 0
                while 1:
                    if out.poll() is not None:
                        output = out.stdout.read()
                        break
                    time.sleep(1)
                    n1 += 1
                    if n1 >= 5:
                        out.kill()
        try:
            os.remove(file_name + '.c')
            os.remove(file_name)
            os.remove(file_name + '.exe')
        except:
            pass
    except:
        output = ''
    return str(output)


def cpp_output(code):
    try:
        file_name = generate_filename()
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name + ".cpp", 'w')
        f.write(code)
        f.close()
        compl = subprocess.Popen(['g++', '-o', file_name, file_name + '.cpp'], stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
        compl_output = compl.communicate()[0]
        if b'warning' in compl_output.lower():
            output = compl_output + b'\n'
            try:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n = 0
                while 1:
                    if out.poll() is not None:
                        output += out.stdout.read()
                        break
                    time.sleep(1)
                    n += 1
                    if n >= 5:
                        out.kill()
            except:
                output = compl_output
        else:
            if compl_output:
                output = compl_output
            else:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n1 = 0
                while 1:
                    if out.poll() is not None:
                        output = out.stdout.read()
                        break
                    time.sleep(1)
                    n1 += 1
                    if n1 >= 5:
                        out.kill()
        try:
            os.remove(file_name + '.cpp')
            os.remove(file_name)
            os.remove(file_name + '.exe')
        except:
            pass
    except:
        output = ''
    return str(output)



@csrf_exempt
def evaluate_code(request):
    n = datetime.datetime.now()
    if request.method == 'POST':
        data = json.loads(request.body)
        try:
            language = data.get("language")
            code = data.get('code')
            selenium_script = data.get('selenium_script')
            std_in1 = data.get('std_in1')
            std_out1 = data.get('std_out1')
            std_in2 = data.get('std_in2')
            std_out2 = data.get('std_out2')
            std_in3 = data.get('std_in3')
            std_out3 = data.get('std_out3')
            try:
                assessment_save_tag = data.get('assessment_save_tag')
                assessment_save_tag = int(assessment_save_tag)
            except:
                assessment_save_tag = 0
            output2 = [0, '']
            output3 = [0, '']
            message = ''
            testcase_data = {}
            if language in ["c#", "ruby", "php", "typescript"]:
                data = {
                    "language": language, "code": code, "std_in1": std_in1, "std_out1": std_out1,
                    "std_in2": std_in2, "std_out2": std_out2, "std_in3": std_in3, "std_out3": std_out3
                }
                final_output, avg_score, error_tag = get_code_executed_by_openai(**data)
                if assessment_save_tag:
                    new_score, group_data = get_static_data(language, code)
                else:
                    new_score = group_data = None
                if not error_tag:
                    res = {'status': 'Success', 'status_code': 200, 'score': avg_score, 'message': str(final_output),
                           'new_score': new_score, 'group_analysis': group_data}
                else:
                    message = "Kindly save the program it will be evaluated at submission time!"
                    res = {'status': 'Success', 'status_code': 200, 'score': avg_score, 'message': message,
                           'new_score': new_score, 'group_analysis': group_data}
                return HttpResponse(json.dumps(res), content_type='application/javascript')
            elif language == 'python':
                logger.info("Python code execution started")
                t1 = datetime.datetime.now()
                output = python_output_with_input(code, std_in1, std_out1)
                testcase_data['testcase-1'] = {'Input': std_in1, 'expected-output': std_out1, 'user-output': output}
                if std_in2 and std_out2:
                    output2 = python_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = python_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                t2 = datetime.datetime.now()
                logger.info("Python code execution completed")
                logger.info(f"Time taken(sec) for execution: {(t2 - t1)}")

            elif language == 'c':
                logger.info("C code execution started")
                tc1 = datetime.datetime.now()
                output = c_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = c_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = c_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                tc2 = datetime.datetime.now()
                logger.info("C code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tc2 - tc1)}")
            elif language == 'c++':
                logger.info("C++ code execution started")
                tcpp1 = datetime.datetime.now()
                output = cpp_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = cpp_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = cpp_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                tcpp2 = datetime.datetime.now()
                logger.info("C++ code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tcpp2 - tcpp1)}")
            elif language == 'java':
                logger.info("Java code execution started")
                tj1 = datetime.datetime.now()
                output = java_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = java_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = java_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                tj2 = datetime.datetime.now()
                logger.info("Java code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tj2 - tj1)}")
            elif language == 'javascript':
                logger.info("Javascript code execution started")
                tjs1 = datetime.datetime.now()
                output = javascript_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = javascript_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = javascript_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                tjs2 = datetime.datetime.now()
                logger.info("Javascript code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tjs2 - tjs1)}")
            elif language == 'node.js' or language == 'nodejs':
                logger.info("Node.js code execution started")
                tnode1 = datetime.datetime.now()
                input_output_list = [(std_in1, std_out1)]
                if std_in2 and std_out2:
                    input_output_list.append((std_in2, std_out2))
                if std_in3 and std_out3:
                    input_output_list.append((std_in3, std_out3))
                output = nodejs_code_eval(code, input_output_list)
                tnode2 = datetime.datetime.now()
                logger.info("Node.js code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tnode2 - tnode1)}")

            elif language.lower() == 'c#':
                logger.info("C# code execution started")
                tjs1 = datetime.datetime.now()
                output = c_sharp_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = c_sharp_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = c_sharp_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                tjs2 = datetime.datetime.now()
                logger.info("C# code execution completed")
                logger.info(f"Time taken(sec) for execution: {(tjs2 - tjs1)}")


            elif language == "dart":
                logger.info("dart code execution started")
                t1 = datetime.datetime.now()
                output = dart_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = dart_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = dart_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                t2 = datetime.datetime.now()
                logger.info("Python code execution completed")
                logger.info(f"Time taken(sec) for execution: {(t2 - t1)}")
            elif language == "golang" or language == "go":
                logger.info("golang code execution started")
                t1 = datetime.datetime.now()
                output = golang_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = golang_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = golang_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
                t2 = datetime.datetime.now()
                logger.info("golang code execution completed")
                logger.info(f"Time taken(sec) for execution: {(t2 - t1)}")

            elif language == "angularjs" or language == "angular js":
                logger.info("angularjs code execution started")
                t1 = datetime.datetime.now()
                output = execute_react_js(code, selenium_script)
                t2 = datetime.datetime.now()
                logger.info("angularjs code execution completed")
                logger.info(f"Time taken(sec) for execution: {(t2 - t1)}")
            elif language == "reactjs" or language == "react js":
                logger.info("reactjs code execution started")
                t1 = datetime.datetime.now()
                output = execute_react_js(code, selenium_script)
                t2 = datetime.datetime.now()
                logger.info("reactjs code execution completed")
                logger.info(f"Time taken(sec) for execution: {(t2 - t1)}")
            else:
                output = [0, '']
                message = 'Program language not supported.'
                logger.info(f"Program language not supported. language: {language}")
            if language == 'node.js' or language == 'nodejs':
                avg_score = output[0]
                final_output = output[1]
            else:
                final_score_list = []
                if language == "angularjs" or language == "angular js" or language == "reactjs" or language == "react js":
                    final_output = "Test Case 1: \n "
                else:
                    final_output = "Test Case 1: \n Input: " + str(std_in1).replace('\n', '&') + "\n Output: " + str(output[1]) + "\n Expected Output: " + str(std_out1) + "\n"
                if output[0]:
                    if language == "angularjs" or language == "angular js" or language == "reactjs" or language == "react js":
                        if output[0] in ['Time Limit Exceeded']:
                            final_output += "Test Case Status: inconclusive \n\n"
                        else:
                            final_output += "Test Case Status: Passed\n\n"
                    else:
                        final_output += "Test Case Status: Passed\n\n"

                else:
                    final_output += "Test Case Status: Failed\n\n"
                final_score_list.append(output[0])
                if std_in2 and std_out2:
                    final_output += "Test Case 2: \n Input: " + str(std_in2).replace('\n', '&') + "\n Output: " + str(output2[1]) + "\n Expected Output: " + str(std_out2) + "\n"
                    if output2[0]:
                        final_output += "Test Case Status: Passed\n\n"
                    else:
                        final_output += "Test Case Status: Failed\n\n"
                    final_score_list.append(output2[0])
                if std_in3 and std_out3:
                    final_output += "Test Case 3: \n Input: " + str(std_in3).replace('\n', ' & ') + "\n Output: " + str(output3[1]) + "\n Expected Output: " + str(std_out3) + "\n"
                    if output3[0]:
                        final_output += "Test Case Status: Passed\n\n"
                    else:
                        final_output += "Test Case Status: Failed\n\n"
                    final_score_list.append(output3[0])

                if final_score_list:
                    if all(final_score_list):
                        try:
                            avg_score = sum(final_score_list)/len(final_score_list)
                        except:
                            avg_score = 0
                    else:
                        avg_score = 0
                else:
                    avg_score = 0



                final_output = re.sub(r'File ".+.py",', 'File "file.py",', final_output)
            if assessment_save_tag:
                new_score, group_data = get_static_data(language, code)
            else:
                new_score = group_data = None

            if message:
                response = {'status': 'Success', 'status_code': 200, 'message': message}
            else:
                response = {'status': 'Success', 'status_code': 200, 'score': avg_score, 'message': str(final_output),
                            'new_score': new_score, 'group_analysis': group_data}
        except Exception as ex:
            logger.info(f"in evaluate_code---- : error: {ex}")
            response = {'status': 'Failure', 'status_code': 401,
                        'message': 'Data issue, Please try again.'}

    else:
        response = {'status': 'Failure', 'status_code': 405, 'message': 'Method Not Allowed.'}


    # data = json.loads(request.body)
    # code = data.get('code')
    # std_in = data.get('std_in')
    # std_out = data.get('std_out')
    # r = python_output_with_input(code, std_in, std_out)
    # res = {'message': r}
    nn = datetime.datetime.now()
    logger.info("in evaluate_code() -- end")
    logger.info(f'final response in evaluate_code() - : {response}')
    logger.info(f"Time taken(sec) for evaluate_code(): {(nn - n)}")
    return HttpResponse(json.dumps(response), content_type='application/javascript')


def eval_nodejs(request):
    code = """
const express = require('express');
const app = express();

const books = [
  {
    id: 1,
    title: "The Hitchhiker's Guide to the Galaxy",
    author: "Douglas Adams"
  },
  {
    id: 2,
    title: "To Kill a Mockingbird",
    author: "Harper Lee"
  },
  {
    id: 3,
    title: "1984",
    author: "George Orwell"
  }
];

app.get('/books', (req, res) => {
   
  res.json(books);
});

module.exports = app;"""
    # response = evl_nodejs_api()
    input = {
          "method": "GET",
          "url": "/books"
        }
    output = {
  "http-code": 200,
  "output-json": [
          {
            "id": 1,
            "title": "The Hitchhiker's Guide to the Galaxy",
            "author": "Douglas Adams"
          },
          {
            "id": 2,
            "title": "To Kill a Mockingbird",
            "author": "Harper Lee"
          },
          {
            "id": 3,
            "title": "1984",
            "author": "George Orwell"
          }
  ]
}

    response = str(nodejs_code_eval(code, [(input, output), (input, output), (input, output)]))

    return HttpResponse(json.dumps(response), content_type='application/javascript')


def dart_output(code):
    file_name = generate_filename() + '.dart'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['dart', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
        try:
            os.remove(file_name)
        except:
            pass
    except:
        output = ''
    return str(output)

def golang_output(code):
    file_name = generate_filename() + '.go'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['go',' run', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
        try:
            os.remove(file_name)
        except:
            pass
    except:
        output = ''
    return str(output)

@csrf_exempt
def execute_react_code(request):
    code = request.POST.get("code")
    code = example_react_code()
    selenium_script = example_selenium()
    output = execute_react_js(code, selenium_script)

    response = {'output': str(output)}
    return HttpResponse(json.dumps(response), content_type='application/javascript')


