from django.contrib import auth
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.authentication import BasicAuthentication, TokenAuthentication
from .serializers import UserRegisterSerializer

from django.middleware.csrf import get_token
from django.contrib.auth import login

from api.utils import *
from api.models import File, Mission, Plan, Algorithm

import json
import logging 

logger = logging.getLogger('prepare')

class UploadFileAPI(APIView):
    def get(self, request):
        user = request.user
        history = File.objects.getHistory(user=user)
        if history == FAILED:
            return failed(message="获取文件上传历史失败")
        else:
            return success(data=history)
        
    def post(self, request):
        user = request.user
        # 获取上传的文件对象
        nodeFileName = request.data.get('nodeFileName')
        edgeFileName = request.data.get('edgeFileName')
        nodeFile = request.data.get('nodes')
        edgeFile = request.data.get('edges')

        # 检查文件用途并进行响应处理
        file_usage = request.data.get('usage')
        if file_usage == 'input':
            pass
        else:
            return failed(message=filename + "上传失败 暂时只支持上传图处理原料文件")
        
        # 检查文件类型并进行相应处理
        file_type = request.data.get('type')
        for filename in [nodeFileName, edgeFileName]:
            
            if file_type == 'csv':
                if filename.split('.')[-1] != 'csv':
                    return failed(message=filename + "上传失败 文件类型和文件后缀名不匹配")
            else:
                return failed(message=filename + "上传失败 暂不支持CSV之外文件")

        successUploadedFiles = []
        pre_file = None
        for filename, fileData in [[edgeFileName, edgeFile], [nodeFileName, nodeFile]]:
            # 处理数据库中记录
            file = File()
            file.name = filename
            file.user = request.user
            file.type = file_type
            file.usage = file_usage
            # 一定先edge再node
            if pre_file == None:
                file.content = 'edge'
            else:
                file.content = 'node'
            file.save()
            # 尝试保存文件
            result = file.storage(fileData)
            if result != OK:
                file.delete()
                # 第二个文件错误，同样删除第一个文件
                if pre_file:
                    pre_file.delete()
                if result == FILE_ALREADY_EXIST:
                    return failed(message=filename + "上传失败 已存在同名文件", data="已存在同名文件", code=400)
                elif result == FILE_FAILED_CREATE_DIR:
                    return failed(message=filename + "上传失败 文件目录创建失败", data="文件目录创建失败", code=400)
                else:
                    return failed(message=filename + "上传失败 因未知原因文件上传失败", data="因未知原因文件上传失败", code=400)
            # 第二个文件也成功时,将两个文件互相关联
            if pre_file:
                file.associate = pre_file
                pre_file.associate = file
                file.saveWithInfo()
                pre_file.saveWithInfo()
            else:
                pre_file = file
            # 两个文件均上传成功后，检测文件内容是否符合规范
        if file.checkIllegal() and pre_file.checkIllegal():
            # 确保正确后创建mission，并将信息返回
            mission = Mission()
            mission.nodeFile = file
            mission.edgeFile = pre_file
            mission.user = user
            mission.save()
            successUploadedFiles.append({
                "id": mission.id,
                "name": mission.name,
                "content": "mission",
            })
            
            # 初步对文件内容进行分析，获取节点和边的基本数据
            for file in [file, pre_file]:
                successUploadedFiles.append({
                    "id": file.id,
                    "name": file.name,
                    "content": file.content,
                    "nodes": file.own_file_info.nodes,
                    "sNodes": file.own_file_info.sNodes,
                    "dNodes": file.own_file_info.dNodes,
                    "iNodes": file.own_file_info.iNodes,
                    "edges": file.own_file_info.edges,
                })
            return success(message="文件上传成功", data=successUploadedFiles, code=200)
        else:
            return failed(message="文件合法性检查失败")
    
    def delete(self, request):
        user = request.user
        file = File.objects.get(id=request.data.get('id'))

        result = file.deleteStorage()
        if result == OK:
            return success(message="删除文件成功")
        elif result == False:
            return failed(messgae="删除文件失败")


class InputFileAPI(APIView):
    def post(self, request):
        user = request.user
        nodes = request.data.get('nodes')
        edges = request.data.get('edges')
        logger.info(nodes)
        logger.info(edges)
        # 保存节点文件
        # 当前设置输入的节点信息仅包含一个名称
        nodesFile = File.objects.create(type='csv', usage='input', user=user, content='node')
        try:
            nodesCsvList = []
            for node in nodes:
                nodeList = [int(node['id']), str(node['type'])]
                for key in [k for k in node if k not in ['id', 'type']]:
                    nodeList.append(str(key) + ':' +str(node[key]))
                nodesCsvList.append(nodeList)
            logger.info(nodesCsvList)
            nodesFile.generate(nodesCsvList)
        except Exception:
            # 回退操作
            logger.error(f"从输入信息创建节点文件失败 Error:{Exception}")
            nodesFile.delete()
        # 保存边文件
        edgesFile = File.objects.create(type='csv', usage='input', user=user, content='edge')
        try:
            edgesCsvList = []
            for edge in edges:
                # 边中应至少有source和target两个参数
                edgeList = [int(edge['source']), int(edge['target'])]
                for key in [k for k in edge if k not in ['source', 'target']]:
                    edgeList.append(str(key) + ':' + str(edge[key]))
                edgesCsvList.append(edgeList)
            logger.info(edgesCsvList)
            edgesFile.generate(edgesCsvList)
        except Exception:
            # 回退操作
            logger.error(f"从输入信息创建边文件失败 Error:{Exception}")
            nodesFile.delete()
            edgesFile.delete()
        
        # 节点和边均保存成功，可以互相关联并创建mission
        nodesFile.associate = edgesFile
        edgesFile.associate = nodesFile
        # 计算节点和边的信息，生成fileInfo
        nodesFile.saveWithInfo()
        edgesFile.saveWithInfo()
        # 构造mission并返回完整信息
        successUploadedFiles = []
        if nodesFile.checkIllegal() and edgesFile.checkIllegal():
            # 确保正确后创建mission，并将信息返回
            mission = Mission()
            mission.nodeFile = nodesFile
            mission.edgeFile = edgesFile
            mission.user = user
            mission.save()
            successUploadedFiles.append({
                "id": mission.id,
                "name": mission.name,
                "content": "mission",
            })
            
            # 初步对文件内容进行分析，获取节点和边的基本数据
            for file in [nodesFile, edgesFile]:
                successUploadedFiles.append({
                    "id": file.id,
                    "name": file.name,
                    "content": file.content,
                    "nodes": file.own_file_info.nodes,
                    "sNodes": file.own_file_info.sNodes,
                    "dNodes": file.own_file_info.dNodes,
                    "iNodes": file.own_file_info.iNodes,
                    "edges": file.own_file_info.edges,
                })
            return success(message="文件上传成功", data=successUploadedFiles, code=200)
        else:
            logger.error("节点和边文件的合法性检测未通过")
            nodesFile.delete()
            edgesFile.delete()
            return failed(message="节点和边信息输入失败，节点或边信息未通过合法性检测")


class PlanAPI(APIView):
    def post(self, request):
        user = request.user
        plans = request.data.get('plans')
        overWritePlans = False
        try:
            mission = Mission.objects.get(id=request.data.get('mission'))
        except Mission.DoesNotExist:
            print("处理规划所属任务不存在")
            return failed(message="未找到规划任务所属任务")
        # 重复提交同一Mission的规划将覆盖
        if mission.own_plans.exists():
            overWritePlans = True
            for plan in mission.own_plans.all():
                plan.delete()
        # 全部放入矩阵
        planMat = [[{}, {}, {}] for i in range(10)]
        # 找出所有并行的源头
        origins = []
        for plan in plans:
            if plan['row'] == 0:
                origins.append(plan)
            planMat[plan['row']][plan['col']] = plan
        rootPlan = Plan(mission=mission, parent=None, algorithm=None, user=user)
        rootPlan.save()

        originPlans = []
        # 将新建的plan的id都要返回
        createdPlans = []
        # 添加第一代规划
        for pJson in origins:
            # p.algorithm应该用来新建plan，先占位
            # childPlan = Plan(mission=mission, parent=rootPlan, algorithm=p['algorithm'], user=user)
            try:
                algorithm = Algorithm.objects.get(name=pJson['algorithm'])
            except Algorithm.DoesNotExist:
                print("Not Exist Algorithm")
                return failed(message=str(pJson['id']) + "规划选定算法不存在")
            pModel = Plan(mission=mission, parent=rootPlan, algorithm=algorithm, user=user)
            pModel.save()
            createdPlans.append({
                'id': pModel.id,
                'col': pJson['col'],
                'row': pJson['row'],
            })
            # p是json数据，headPlan是保存的PlanModel
            originPlans.append([pJson, pModel])

        # 添加后续规划
        while originPlans:
            pJson, pModel = originPlans.pop()
            for childPos in pJson['children']:
                childJson = planMat[childPos['row']][childPos['col']]
                try:
                    algorithm = Algorithm.objects.get(name=childJson['algorithm'])
                except Algorithm.DoesNotExist:
                    print("Not Exist Algorithm")
                    return failed(message=str(pJson['id']) + "规划选定算法不存在")
                childModel = Plan(mission=mission, parent=pModel, algorithm=algorithm, user=user)
                childModel.save()
                # 将新建的plan加入列表
                createdPlans.append({
                    'id': childModel.id,
                    'col': childPos['col'],
                    'row': childPos['row'],
                    'parentId': pModel.id,
                })
                originPlans.append([childJson, childModel])
        

        if overWritePlans:
            return success(message="规划已覆盖", data=createdPlans)
        else:
            return success(message="规划已提交", data=createdPlans)

class MissionsAPI(APIView):
    def get(self, request):
        user =  request.user
        missions = Mission.objects.filter(user=user).all()
        response = []
        for mission in missions:
            nodesInfo = mission.nodeFile.own_file_info
            edgesInfo = mission.edgeFile.own_file_info
            response.append({
                'id': mission.id,
                'name': mission.name,
                'createTime': mission.create_time,
                'nodesInfo': {
                    'S': nodesInfo.sNodes,
                    'D': nodesInfo.dNodes,
                    'I': nodesInfo.iNodes,
                },
                'edgesInfo': {
                    'num': edgesInfo.edges,
                },
                # 防止近义词错误
                'status': mission.state,
                'state': mission.state,
            })
        return success(data=response)