# -*- encoding: utf-8 -*-
"""
@File    : AgentOrderController.py
@Time    : 2024/3/14 10:53
@Author  : stephen
@Email   : zhangdongming@asj6.wecom.work
@Software: PyCharm
"""
import threading
import time
from datetime import datetime, timedelta
from decimal import Decimal, ROUND_DOWN

from django.http import QueryDict
from django.views import View

from AgentModel.models import AgentDevice, AgentCloudServicePackage, AgentCustomerPackage, AgentDeviceOrder, \
    AgentDeviceOrderInstallment, AgentAccount
from Ansjer.config import LOGGER
from Model.models import Order_Model, Store_Meal, UnicomCombo
from Object.ResponseObject import ResponseObject
from Object.TokenObject import TokenObject
from Service.CommonService import CommonService


class AgentOrderView(View):
    def get(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        return self.validation(request.GET, request, operation)

    def post(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        return self.validation(request.POST, request, operation)

    def delete(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        delete = QueryDict(request.body)
        if not delete:
            delete = request.GET
        return self.validation(delete, request, operation)

    def put(self, request, *args, **kwargs):
        request.encoding = 'utf-8'
        operation = kwargs.get('operation')
        put = QueryDict(request.body)
        return self.validation(put, request, operation)

    def validation(self, request_dict, request, operation):
        response = ResponseObject()
        tko = TokenObject(
            request.META.get('HTTP_AUTHORIZATION'),
            returntpye='pc')
        if operation == 'addOrder':  # 添加代理商订单
            order_id = request_dict.get('order_id', None)
            uid = request_dict.get('uid', None)
            order_type = request_dict.get('order_type', None)
            package_id = request_dict.get('package_id', None)

            self.check_agent_service_package(order_id, uid, int(package_id))
            return response.json(0)

    @classmethod
    def check_agent_service_package(cls, order_id, uid, package_id):
        """
        检查是否代理服务套餐
        @param package_id: 套餐id
        @param order_id: 订单ID
        @param uid: UID
        @return: True | False
        """
        try:
            serial_number = CommonService.get_serial_number_by_uid(uid)
            a_device_qs = AgentDevice.objects.filter(serial_number=serial_number) \
                .values('ac_id', 'type', 'status')
            LOGGER.info(f'******check_agent_service_package检查是否代理*****orderId:{order_id}')

            if not a_device_qs.exists():
                return False
            LOGGER.info(f'******check_agent_service_package当前设备属于代理商*****serial_number:{serial_number}')

            asy = threading.Thread(target=cls.save_agent_package,
                                   args=(order_id, serial_number, a_device_qs[0]['ac_id'], package_id))
            asy.start()
            return True
        except Exception as e:
            LOGGER.info('*****AgentOrderView.check_agent_service_package:errLine:{}, errMsg:{}'
                        .format(e.__traceback__.tb_lineno, repr(e)))
        return False

    @classmethod
    def save_agent_package(cls, order_id, serial_number, ac_id, package_id):
        """
        保存代理套餐
        """
        try:
            order_qs = Order_Model.objects.filter(orderID=order_id, status=1).values('price', 'payTime', 'order_type')
            if not order_qs.exists():
                LOGGER.info(f'******save_agent_package当前代理客户未添加此套餐******ac_id:{ac_id},package_id:{package_id}')
                return

            order_type = order_qs[0]['order_type']

            package_type = 2 if order_type in [2, 3, 5] else 1  # 判断订单信息是云存还是4G

            package_id = int(package_id)
            agent_package_qs = AgentCloudServicePackage.objects.filter(type=package_type, package_id=package_id,
                                                                       status=1)
            if not agent_package_qs.exists():
                LOGGER.info(f'******save_agent_package当前套餐未设置代理******order_id:{order_id},serial_number:{serial_number}')
                return

            agent_package = agent_package_qs.first()  # 代理云服务套餐
            LOGGER.info(f'******save_agent_package代理套餐******service_name:{agent_package_qs.first().service_name}')
            acp_qs = AgentCustomerPackage.objects.filter(ac_id=ac_id, cs_id=agent_package.id).values('id')
            if not acp_qs.exists():
                LOGGER.info(f'******save_agent_package当前代理客户未添加此套餐******ac_id:{ac_id},package_id:{package_id}')
                return

            # 组装数据
            now_time = int(time.time())

            pay_price = Decimal(order_qs[0]['price']).quantize(Decimal('0.00'))
            profit = cls.calculate_order_profit(agent_package, pay_price)

            dict_data = {'ac_id': ac_id, 'serial_number': serial_number, 'csp_id': agent_package.id,
                         'order_id': order_id, 'status': 1, 'profit_amount': pay_price, 'profit': profit,
                         'pay_time': order_qs[0]['payTime'], 'created_time': now_time, 'updated_time': now_time}
            agent_order_obj = AgentDeviceOrder.objects.create(**dict_data)

            # 保存分期结算记录
            cls.save_order_installment(agent_order_obj.id, package_type, package_id, profit, ac_id)

            LOGGER.info(f'******save_agent_package代理订单存表结束：{dict_data}')
        except Exception as e:
            LOGGER.info('*****AgentOrderView.save_agent_package:errLine:{}, errMsg:{}'
                        .format(e.__traceback__.tb_lineno, repr(e)))

    @classmethod
    def calculate_order_profit(cls, agent_package, price):
        """
        计算利润
        @param agent_package: 套餐配置
        @param price: 支付价格
        @return: 利润
        """
        profit = 0
        price = Decimal(price).quantize(Decimal('0.00'))
        if agent_package.profit_type == 1:
            profit = agent_package.profit
        elif agent_package.profit_type == 2:
            profit_value = Decimal(agent_package.profit).quantize(Decimal('0.00'))
            cost = Decimal(agent_package.cost).quantize(Decimal('0.00'))
            profit = (price - cost) * (profit_value / 100)
            profit = profit.quantize(Decimal('0.00'))
        return profit

    @classmethod
    def get_settlement_interval(cls, package_type, package_id):
        try:
            if package_type == 1:  # 云存
                store_qs = Store_Meal.objects.filter(id=package_id).values('day', 'bucket_id', 'expire',
                                                                           'icloud_store_meal_id')
                if not store_qs.exists():
                    return []

                # 根据套餐周期计算往后每个月26号作为结算时间
                return cls.get_future_timestamps(store_qs[0]['expire'])

            elif package_type == 2:  # 4G
                combo4g_qs = UnicomCombo.objects.filter(id=package_id).values('expiration_days', 'expiration_type')
                if not combo4g_qs.exists():
                    return []

                # 目前4G套餐都是基于按天类型创建
                if combo4g_qs[0]['expiration_type'] == 0 and combo4g_qs[0]['expiration_days'] > 0:
                    months = int(combo4g_qs[0]['expiration_days'] / 30)

                    # 根据套餐周期计算往后每个月26号作为结算时间
                    return cls.get_future_timestamps(months)
        except Exception as e:
            LOGGER.info('*****AgentOrderView.get_settlement_interval:errLine:{}, errMsg:{}'
                        .format(e.__traceback__.tb_lineno, repr(e)))
            return []

    @staticmethod
    def get_future_timestamps(months):
        """
        生成未来若干个月的第一个月的26号11点的timestamp列表。

        参数：
        months -- 未来需要生成timestamp的月份数量

        返回值：
        timestamps -- 包含未来months个月第一个月的26号11点的timestamp的列表
        """
        current_time = datetime.now()  # 获取当前时间，注意这会是系统当前时区的时间
        current_month = current_time.month
        current_year = current_time.year

        timestamps = []
        for _ in range(months):
            # 如果当前月已经是需要生成的月份，则年份和月份不变
            if current_month == 1 and _ == 0:
                next_year = current_year
                next_month = current_month + 1
            else:
                # 计算下一个月的年和月
                if current_month == 12:
                    next_month = 1
                    next_year = current_year + 1
                else:
                    next_month = current_month + 1
                    next_year = current_year

                    # 生成下个月的26号11点的时间点
            next_date = datetime(next_year, next_month, 26, 11, 0, 0)

            # 如果生成的日期超过了当月的实际天数（比如2月没有26号），则需要回退到当月的最后一天
            last_day_of_month = (datetime(next_year, next_month, 1) + timedelta(days=31)).replace(day=1) - timedelta(
                days=1)
            if next_date > last_day_of_month:
                next_date = last_day_of_month.replace(hour=11, minute=0, second=0, microsecond=0)

            timestamps.append(int(next_date.timestamp()))

            # 更新当前月份和年份为下一次循环使用
            current_month = next_month
            current_year = next_year

        return timestamps

    @staticmethod
    def distribute_commission(commission, periods):
        # 转换佣金和期数为Decimal类型，并设置精度
        commission = Decimal(str(commission)).quantize(Decimal('0.01'))
        periods = Decimal(periods)

        # 每期基础金额（向下取整到最接近的0.01）
        base_amount = (commission / periods).quantize(Decimal('0.01'), rounding=ROUND_DOWN)

        # 初始化每期分配的金额列表
        distributed_amounts = [base_amount] * int(periods)

        # 计算按照基础金额分配后的总和
        total_distributed = sum(distributed_amounts)

        # 计算剩余需要分配的金额
        remainder = commission - total_distributed

        # 分配剩余金额
        if remainder > Decimal('0'):
            # 从第一期开始分配剩余金额
            for i in range(len(distributed_amounts)):
                if remainder >= Decimal('0.01'):
                    distributed_amounts[i] += Decimal('0.01')
                    remainder -= Decimal('0.01')
                else:
                    # 如果剩余金额不足0.01，则将其全部加到当前期
                    distributed_amounts[i] += remainder
                    break

        return distributed_amounts

    @classmethod
    def save_order_installment(cls, agent_order_id, package_type, package_id, profit, ac_id=None):
        """
        保存代理订单分期信息

        :param cls: 类方法的约定参数
        :param agent_order_id: 代理订单ID
        :param package_type: 套餐类型
        :param package_id: 套餐ID
        :param profit: 利润总额
        :return: 无返回值
        :param ac_id: 代理客户ID
        """
        try:
            # 根据包裹类型和ID获取结算时间间隔列表
            time_list = cls.get_settlement_interval(package_type, package_id)
            period_number = len(time_list)  # 计算分期总数

            # 输入合理性检查
            if period_number == 0 or profit <= 0:
                LOGGER.info(f'Invalid input parameters: period_number={period_number}, profit={profit}')
                return

            n_time = int(datetime.now().timestamp())  # 获取当前时间戳
            # 利润总额按分期数平均分配
            amount_list = cls.distribute_commission(profit, period_number)

            installment_list = []
            # 遍历分期数，生成分期记录列表
            for time_point in range(period_number):
                installment_list.append(AgentDeviceOrderInstallment(ado_id=agent_order_id,
                                                                    period_number=period_number,
                                                                    ac_id=ac_id,
                                                                    amount=amount_list[time_point],
                                                                    due_date=time_list[time_point],
                                                                    status=1,
                                                                    created_time=n_time,
                                                                    updated_time=n_time))

            # 分批处理大量数据，避免数据库压力过大
            batch_size = 100
            for i in range(0, len(installment_list), batch_size):
                AgentDeviceOrderInstallment.objects.bulk_create(
                    installment_list[i:i + batch_size]
                )

            # 记录创建完成的日志
            LOGGER.info(f'*****AgentOrderView.save_OrderInstallment分期结算记录创建完成:{len(installment_list)} records')

        except Exception as e:
            # 记录异常信息
            LOGGER.error('*****AgentOrderView.save_OrderInstallment:errLine:{}, errMsg:{}'
                         .format(e.__traceback__.tb_lineno, repr(e)))

    @staticmethod
    def update_periodic_settlement():
        """
        更新周期结算信息

        返回值:
        - 无返回值
        """
        try:
            # 根据条件查询需要更新结算信息的订单分期记录
            adoi_qs = AgentDeviceOrderInstallment.objects.filter(status=1, due_date__lte=int(time.time()))
            if not adoi_qs:
                # 如果没有找到符合条件的记录，直接返回
                return

            ids = []
            a_account_list = []
            adoi_set = set()
            n_time = int(time.time())
            for item in adoi_qs:
                # 准备分期记录的id列表和账户记录列表
                ids.append(item.id)
                adoi_set.add(item.ado_id)
                a_account_list.append(AgentAccount(ac_id=item.ac_id, amount=item.amount,
                                                   remark=f'周期结算',
                                                   status=1, created_time=n_time,
                                                   updated_time=n_time))

            batch_size = 100

            # 分批更新分期记录状态
            for i in range(0, len(ids), batch_size):
                AgentDeviceOrderInstallment.objects.filter(id__in=ids[i:i + batch_size]) \
                    .update(status=2, settlement_time=n_time, updated_time=n_time)

            # 分批创建账户记录
            for i in range(0, len(a_account_list), batch_size):
                AgentAccount.objects.bulk_create(a_account_list[i:i + batch_size])

            # 检查是否所有分期都已结算，如果是，则更新订单状态为已结算
            for ado in adoi_set:
                adoi_qs = AgentDeviceOrderInstallment.objects.filter(ado_id=ado, status=1)
                if not adoi_qs.exists():
                    AgentDeviceOrder.objects.filter(id=ado, status=1) \
                        .update(status=2, settlement_time=n_time, updated_time=n_time)

        except Exception as e:
            # 记录异常信息
            LOGGER.error(
                f'*****AgentOrderView.update_periodic_settlement:errLine:{e.__traceback__.tb_lineno}, errMsg:{str(e)}')
