from django.db import transaction
from .queries import LocationQueries
from .updates import LocationUpdates 
from .algorithms import AllocationAlgorithm
import logging
import json
from collections import defaultdict
from .models import *
from bound.models import BoundBatchModel, BoundDetailModel, OutBatchModel, BoundListModel
import datetime
from django.utils import timezone

logger = logging.getLogger(__name__)

class AllocationService:
    @classmethod
    @transaction.atomic
    def allocate(cls, container_code, start_point):
        # logger.info(f"请求托盘:{container_code},请求位置:{start_point}")

        batch_info = LocationQueries.get_batch_info(container_code)
        if not batch_info :
            raise ValueError("无效的托盘或批次信息")
        if not batch_info['status']:
            if batch_info['class'] == 2:
                return cls._container_group_allocation(container_code,batch_info, start_point)
            else:
                return cls._container_scattered_allocation(container_code,batch_info, start_point)
        # if batch_info['status'] == 1 or batch_info['status'] == 0:
        #     return cls._first_allocation(container_code, batch_info, start_point)
        # elif batch_info['status'] == 2:
        return cls._subsequent_allocation(container_code, batch_info, start_point)
        # else:
        #     raise ValueError("非法的批次状态")

    @classmethod
    @transaction.atomic
    def _first_allocation(cls, container_code, batch_info, start_point):
        total , scatter_total = LocationQueries.get_pallet_count_by_batch(container_code)
        current_total = LocationQueries.get_pallet_count(container_code)
        if not total:
                raise ValueError("无效的托盘或批次信息")
        LocationUpdates.update_pallet_count(total, batch_info['number'])
        if current_total == 0:
            
            layer_cap = LocationQueries.get_group_capacity()
            pressure = LocationQueries.get_current_pressure()

            solution, new_pressure = AllocationAlgorithm.generate_plan(total-scatter_total, layer_cap, pressure)
        
            if not solution:
                raise RuntimeError("无法生成有效分配方案")

            formatted = json.loads(AllocationAlgorithm.format_solution(solution))
            LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
            # 使用距离最近的库位,增加切换
            locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
            return cls._execute_allocation(locations, start_point,container_code,batch_info)
        else:
            return cls._subsequent_allocation(container_code, batch_info, start_point)

    @classmethod
    def _container_group_allocation(cls,container_code, batch_info, start_point):
        total = 1
        # batch_info['number'] = 'ContainerGroup'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
        if not total:
            raise ValueError("无效的托盘或批次信息")
       
        layer_cap = LocationQueries.get_group_capacity()
        pressure = LocationQueries.get_current_pressure()

        solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
        if not solution:
            raise RuntimeError("无法生成有效分配方案")

        formatted = json.loads(AllocationAlgorithm.format_solution(solution))
        LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
        # 使用距离最近的库位,增加切换
        locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
        return cls._execute_allocation_no_batch(locations, start_point,container_code,batch_info)
    
    @classmethod
    def _container_scattered_allocation(cls,container_code, batch_info, start_point):
        total = 1
        # batch_info['number'] = 'ContainerScattered'+str(container_code)+str(timezone.now().strftime('%Y%m%d'))
        layer_cap = LocationQueries.get_group_capacity()
        pressure = LocationQueries.get_current_pressure()

        solution, new_pressure = AllocationAlgorithm.generate_plan(total, layer_cap, pressure)
        if not solution:
            raise RuntimeError("无法生成有效分配方案")

        formatted = json.loads(AllocationAlgorithm.format_solution(solution))
        LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
        # 使用距离最近的库位,增加切换
        locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'],start_point, container_code)
        return cls._execute_allocation(locations, start_point,container_code,batch_info)

    @classmethod
    def _execute_allocation_no_batch(cls, solution, start_point,container_code,batch_info):
        LocationUpdates.update_group_status_reserved(solution)
        print(f"[0] 库位组状态预定成功!")
        location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
        if not location_min_value:
            raise ValueError("[1] 任务分配失败,无可用库位")
        print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")

        location_min_value.status = 'reserved'
        location_min_value.save()
        print(f"[2] 库位状态更新成功!")

        update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
        if not update_location_group_status:
            raise ValueError("[3] 库位组状态更新失败")
        print(f"[3] 库位组状态更新成功!")

        update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
        if not update_location_group_batch:
            raise ValueError("[5] 批次信息更新失败")
        print(f"[5] 批次信息更新成功!")

        update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
        if not update_location_container_link:
            raise ValueError("[6] 托盘与库位关联失败")
        print(f"[6] 托盘与库位关联成功!")
 
        update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
        if not update_location_container_detail:
            raise ValueError("[7] 托盘详情状态更新失败")
        print(f"[7] 托盘详情状态更新成功!")

        allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
       
        return location_min_value,allocation_target_location, batch_info

    @classmethod
    def _execute_allocation(cls, solution, start_point,container_code,batch_info):

        LocationUpdates.update_group_status_reserved(solution)
        print(f"[0] 库位组状态预定成功!")
        location_min_value = AllocationService.get_location_list_remainder(solution,container_code,batch_info)
        if not location_min_value:
            raise ValueError("[1] 任务分配失败,无可用库位")
        print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")

        location_min_value.status = 'reserved'
        location_min_value.save()
        print(f"[2] 库位状态更新成功!")

        update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
        if not update_location_group_status:
            raise ValueError("[3] 库位组状态更新失败")
        print(f"[3] 库位组状态更新成功!")

        update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
        if not update_batch_status:
            raise ValueError("[4] 批次状态更新失败")
        print(f"[4] 批次状态更新成功!")

        update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
        if not update_location_group_batch:
            raise ValueError("[5] 批次信息更新失败")
        print(f"[5] 批次信息更新成功!")

        update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
        if not update_location_container_link:
            raise ValueError("[6] 托盘与库位关联失败")
        print(f"[6] 托盘与库位关联成功!")
 
        update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
        if not update_location_container_detail:
            raise ValueError("[7] 托盘详情状态更新失败")
        print(f"[7] 托盘详情状态更新成功!")

        allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
       
        return location_min_value,allocation_target_location, batch_info

    @classmethod
    def _move_allocation(cls, start_point,target_point,container_code):
        batch_info = LocationQueries.get_batch_info(container_code)
        start_location = AllocationAlgorithm.generate_WMS_location(start_point)
        target_location = AllocationAlgorithm.generate_WMS_location(target_point)
        if not start_location or not target_location:
            raise ValueError("无效的起始或目标位置")
        if start_location.location_code == target_location.location_code:
            raise ValueError("起始位置与目标位置相同")  
        
        location_min_value = AllocationService.get_location_group_remainder(target_location.location_group)
        if not location_min_value:
            raise ValueError("[1] 任务分配失败,无可用库位")
        print(f"[1] 任务安排到第{location_min_value.c_number}个库位:{location_min_value}")
        start_location.status = 'available'
        start_location.save()
    
        location_min_value.status = 'reserved'
        location_min_value.save()
        print(f"[2] 库位状态更新成功!")

        update_location_group_status=LocationUpdates.update_location_group_status(location_min_value.location_code)
        update_start_location_group_status= LocationUpdates.update_location_group_status(start_location.location_code)    
        if not update_location_group_status or not update_start_location_group_status:
            raise ValueError("[3] 库位组状态更新失败")
        print(f"[3] 库位组状态更新成功!")

        # update_batch_status = LocationUpdates.update_batch_status(container_code, 2)
        # if not update_batch_status:
        #     raise ValueError("[4] 批次状态更新失败")
        # print(f"[4] 批次状态更新成功!")

        update_location_group_batch = LocationUpdates.update_location_group_batch(location_min_value,batch_info['number'])
        if not update_location_group_batch:
            raise ValueError("[5] 批次信息更新失败")
        print(f"[5] 批次信息更新成功!")

        update_start_location_container_link = LocationUpdates.disable_link_container(start_location.location_code, container_code)
        update_location_container_link = LocationUpdates.link_container(location_min_value.location_code, container_code)
        if not update_location_container_link or not update_start_location_container_link:
            raise ValueError("[6] 托盘与库位关联失败")
        print(f"[6] 托盘与库位关联成功!")
 
        update_location_container_detail = LocationUpdates.update_container_detail_status(container_code, 2)
        if not update_location_container_detail:
            raise ValueError("[7] 托盘详情状态更新失败")
        print(f"[7] 托盘详情状态更新成功!")

        allocation_target_location = AllocationAlgorithm.generate_WCS_location(location_min_value)
  
        return location_min_value,allocation_target_location, batch_info

    @classmethod
    def _subsequent_allocation(cls, container_code, batch_info, start_point):

        total , scatter_total = LocationQueries.get_pallet_count_by_batch(container_code)
        LocationUpdates.update_pallet_count(total, batch_info['number'])
        locations = LocationQueries.get_left_location_group(batch_info['number'])
        if not locations:
            layer_cap = LocationQueries.get_group_capacity()
            pressure = LocationQueries.get_current_pressure()
            finish_task_sum = sum(LocationQueries.get_current_finish_task(container_code,batch_info)) 
            print(f"当前已完成任务: {finish_task_sum}, 总任务数: {total},散盘任务数: {scatter_total}")
            if total-scatter_total <= 0:
                total = 1  + scatter_total
            solution, new_pressure = AllocationAlgorithm.generate_plan(total-scatter_total, layer_cap, pressure)
            if not solution:
                raise RuntimeError("无法生成有效分配方案")

            formatted = json.loads(AllocationAlgorithm.format_solution(solution))
            LocationUpdates.save_allocation_plan(batch_info['number'], formatted, new_pressure)
            # 使用距离最近的库位,增加切换
            locations = AllocationAlgorithm.allocation_plan_left_right(formatted,batch_info['number'] ,start_point, container_code)
            return cls._execute_allocation(locations, start_point,container_code,batch_info)
        else:
            return cls._execute_allocation(locations, start_point,container_code,batch_info)

    @transaction.atomic
    def get_location_list_remainder(location_group_list,container_code,batch_info):
        """
        获取可用库位的c_number列表
        :param location_list: 库位对象列表
        :return: 可用库位编号列表
        """
        if not location_group_list:
            return None

        current_task = LocationQueries.get_current_finish_task(container_code,batch_info)
        print(f"[1]当前已完成任务: {current_task}")
        # 按压力排序
        sorted_pressure = sorted(
            [(0, current_task[0]), (1, current_task[1]), (2, current_task[2])],
            key=lambda x: (-x[1], x[0])
        )
        # 交换第一和第二个元素的位置
        sorted_pressure[0], sorted_pressure[1] = sorted_pressure[1], sorted_pressure[0]
        print(f"[2]任务排序: {sorted_pressure}")
        print(f"[3]当前选择:{sorted_pressure[0][0]+1}")
        location_type_dict = json.loads(LocationQueries.divide_solution_by_layer(location_group_list))
        # print(f"库位类型分配方案: {location_type_dict}")
   
        for layer, _ in sorted_pressure:
            if not location_type_dict.get(str(layer+1)):
                continue
            # print(f"当前层: {layer+1}")
            # print(f"当前层库位组: {location_type_dict[str(layer+1)].keys()}")
            for group_id in location_type_dict[str(layer+1)].keys():
                location_group = LocationGroupModel.objects.filter(
                    id=group_id,
                ).first()
                if not location_group:
                    continue
                location_list = location_group.location_items.filter(
                    status='available'
                ).all().order_by('c_number')
                if not location_list:
                    print(f"当前层库位组 {location_group.group_code} 可用库位: None")
                    continue
               # 提取所有库位的 c_number
                c_numbers = [loc.c_number for loc in location_list]
                print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")
                # 更新任务完成数目
                current_task[layer] = current_task[layer] + 1
                LocationUpdates.update_current_finish_task(container_code,current_task)
                return location_list[0]
            
    @transaction.atomic
    def get_location_group_remainder(location_group_code):
        """
        获取可用库位的c_number列表
        :param location_group: 库位组
                        :return: 可用库位编号列表
        """
        location_group = LocationGroupModel.objects.filter(
            group_code = location_group_code,
        ).first()
        if not location_group:
            return None
        location_list = location_group.location_items.filter(
            status='available'
        ).all().order_by('c_number')
        if not location_list:
            print(f"当前层库位组 {location_group.group_code} 可用库位: None")
         
        # 提取所有库位的 c_number
        c_numbers = [loc.c_number for loc in location_list]
        print(f"当前层库位组 {location_group.group_code} 可用库位: {c_numbers}")

        return location_list[0]