"""
资产分配业务服务层
"""
from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session, selectinload
from app.crud.allocation import allocation_order, allocation_item
from app.crud.asset import asset
from app.schemas.allocation import (
    AllocationOrderCreate,
    AllocationOrderUpdate,
    AllocationOrderApproval
)
from app.core.exceptions import NotFoundException, BusinessException


class AllocationService:
    """资产分配服务类"""

    async def get_order(
        self,
        db: Session,
        order_id: int
    ) -> Dict[str, Any]:
        """获取分配单详情"""
        # 使用selectinload预加载关联数据，避免N+1查询
        from app.models.allocation import AllocationOrder
        from app.models.organization import Organization
        from app.models.user import User
        from app.models.allocation import AllocationItem

        obj = db.query(
            AllocationOrder
        ).options(
            selectinload(AllocationOrder.items),
            selectinload(AllocationOrder.source_organization.of_type(Organization)),
            selectinload(AllocationOrder.target_organization.of_type(Organization)),
            selectinload(AllocationOrder.applicant.of_type(User)),
            selectinload(AllocationOrder.approver.of_type(User)),
            selectinload(AllocationOrder.executor.of_type(User))
        ).filter(
            AllocationOrder.id == order_id
        ).first()

        if not obj:
            raise NotFoundException("分配单")

        # 加载关联信息
        return self._load_order_relations(db, obj)

    def get_orders(
        self,
        db: Session,
        skip: int = 0,
        limit: int = 20,
        order_type: Optional[str] = None,
        approval_status: Optional[str] = None,
        execute_status: Optional[str] = None,
        applicant_id: Optional[int] = None,
        target_organization_id: Optional[int] = None,
        keyword: Optional[str] = None
    ) -> tuple:
        """获取分配单列表"""
        items, total = allocation_order.get_multi(
            db=db,
            skip=skip,
            limit=limit,
            order_type=order_type,
            approval_status=approval_status,
            execute_status=execute_status,
            applicant_id=applicant_id,
            target_organization_id=target_organization_id,
            keyword=keyword
        )

        # 加载关联信息
        items_with_relations = [self._load_order_relations(db, item) for item in items]

        return items_with_relations, total

    async def create_order(
        self,
        db: Session,
        obj_in: AllocationOrderCreate,
        applicant_id: int
    ):
        """创建分配单"""
        # 验证资产存在性和状态
        assets = []
        for asset_id in obj_in.asset_ids:
            asset_obj = asset.get(db, asset_id)
            if not asset_obj:
                raise NotFoundException(f"资产ID {asset_id}")
            assets.append(asset_obj)

        # 验证资产状态是否允许分配
        for asset_obj in assets:
            if not self._can_allocate(asset_obj.status, obj_in.order_type):
                raise BusinessException(
                    f"资产 {asset_obj.asset_code} 当前状态为 {asset_obj.status}，不允许{self._get_order_type_name(obj_in.order_type)}操作"
                )

        # 生成分配单号
        order_code = await self._generate_order_code(db, obj_in.order_type)

        # 创建分配单
        db_obj = allocation_order.create(
            db=db,
            obj_in=obj_in,
            order_code=order_code,
            applicant_id=applicant_id
        )

        return self._load_order_relations(db, db_obj)

    def update_order(
        self,
        db: Session,
        order_id: int,
        obj_in: AllocationOrderUpdate,
        updater_id: int
    ):
        """更新分配单"""
        db_obj = allocation_order.get(db, order_id)
        if not db_obj:
            raise NotFoundException("分配单")

        # 只有草稿或待审批状态可以更新
        if db_obj.approval_status not in ["pending", "draft"]:
            raise BusinessException("只有待审批状态的分配单可以更新")

        return allocation_order.update(db, db_obj, obj_in, updater_id)

    async def approve_order(
        self,
        db: Session,
        order_id: int,
        approval_in: AllocationOrderApproval,
        approver_id: int
    ):
        """审批分配单"""
        db_obj = allocation_order.get(db, order_id)
        if not db_obj:
            raise NotFoundException("分配单")

        # 检查状态
        if db_obj.approval_status != "pending":
            raise BusinessException("该分配单已审批，无法重复审批")

        # 审批
        db_obj = allocation_order.approve(
            db=db,
            db_obj=db_obj,
            approval_status=approval_in.approval_status,
            approver_id=approver_id,
            approval_remark=approval_in.approval_remark
        )

        # 如果审批通过，执行分配逻辑
        if approval_in.approval_status == "approved":
            await self._execute_allocation_logic(db, db_obj)

        return self._load_order_relations(db, db_obj)

    async def execute_order(
        self,
        db: Session,
        order_id: int,
        executor_id: int
    ):
        """执行分配单"""
        db_obj = allocation_order.get(db, order_id)
        if not db_obj:
            raise NotFoundException("分配单")

        # 检查状态
        if db_obj.approval_status != "approved":
            raise BusinessException("该分配单未审批通过，无法执行")
        if db_obj.execute_status == "completed":
            raise BusinessException("该分配单已执行完成")

        # 执行分配单
        db_obj = allocation_order.execute(db, db_obj, executor_id)

        return self._load_order_relations(db, db_obj)

    def cancel_order(
        self,
        db: Session,
        order_id: int
    ) -> bool:
        """取消分配单"""
        db_obj = allocation_order.get(db, order_id)
        if not db_obj:
            raise NotFoundException("分配单")

        # 检查状态
        if db_obj.execute_status == "completed":
            raise BusinessException("已完成的分配单无法取消")

        allocation_order.cancel(db, db_obj)
        return True

    def delete_order(
        self,
        db: Session,
        order_id: int
    ) -> bool:
        """删除分配单"""
        db_obj = allocation_order.get(db, order_id)
        if not db_obj:
            raise NotFoundException("分配单")

        # 只有草稿或已取消的可以删除
        if db_obj.approval_status not in ["draft", "rejected", "cancelled"]:
            raise BusinessException("只能删除草稿、已拒绝或已取消的分配单")

        return allocation_order.delete(db, order_id)

    def get_order_items(
        self,
        db: Session,
        order_id: int
    ) -> List:
        """获取分配单明细"""
        # 验证分配单存在
        if not allocation_order.get(db, order_id):
            raise NotFoundException("分配单")

        return allocation_item.get_by_order(db, order_id)

    def get_statistics(
        self,
        db: Session,
        applicant_id: Optional[int] = None
    ) -> Dict[str, int]:
        """获取分配单统计信息"""
        return allocation_order.get_statistics(db, applicant_id)

    async def _execute_allocation_logic(
        self,
        db: Session,
        order_obj
    ):
        """执行分配逻辑（审批通过后自动执行）"""
        # 根据单据类型执行不同的逻辑
        if order_obj.order_type == "allocation":
            await self._execute_allocation(db, order_obj)
        elif order_obj.order_type == "transfer":
            await self._execute_transfer(db, order_obj)
        elif order_obj.order_type == "recovery":
            await self._execute_recovery(db, order_obj)
        elif order_obj.order_type == "maintenance":
            await self._execute_maintenance_allocation(db, order_obj)
        elif order_obj.order_type == "scrap":
            await self._execute_scrap_allocation(db, order_obj)

    async def _execute_allocation(self, db: Session, order_obj):
        """执行资产分配"""
        # 更新明细状态为执行中
        allocation_item.batch_update_execute_status(db, order_obj.id, "executing")

        # 获取明细
        items = allocation_item.get_by_order(db, order_obj.id)

        # 更新资产状态
        from app.services.asset_service import asset_service
        from app.schemas.asset import AssetStatusTransition

        for item in items:
            try:
                # 变更资产状态
                await asset_service.change_asset_status(
                    db=db,
                    asset_id=item.asset_id,
                    status_transition=AssetStatusTransition(
                        new_status=item.to_status,
                        remark=f"分配单: {order_obj.order_code}"
                    ),
                    operator_id=order_obj.applicant_id
                )

                # 更新明细状态为完成
                allocation_item.update_execute_status(db, item.id, "completed")
            except Exception as e:
                # 更新明细状态为失败
                allocation_item.update_execute_status(
                    db,
                    item.id,
                    "failed",
                    failure_reason=str(e)
                )

    async def _execute_transfer(self, db: Session, order_obj):
        """执行资产调拨"""
        # 调拨逻辑与分配类似，但需要记录调出和调入网点
        await self._execute_allocation(db, order_obj)

    async def _execute_recovery(self, db: Session, order_obj):
        """执行资产回收"""
        # 回收逻辑
        await self._execute_allocation(db, order_obj)

    async def _execute_maintenance_allocation(self, db: Session, order_obj):
        """执行维修分配"""
        # 维修分配逻辑
        await self._execute_allocation(db, order_obj)

    async def _execute_scrap_allocation(self, db: Session, order_obj):
        """执行报废分配"""
        # 报废分配逻辑
        await self._execute_allocation(db, order_obj)

    def _load_order_relations(
        self,
        db: Session,
        obj
    ) -> Dict[str, Any]:
        """加载分配单关联信息"""
        from app.models.user import User
        from app.models.organization import Organization

        result = {
            "id": obj.id,
            "order_code": obj.order_code,
            "order_type": obj.order_type,
            "title": obj.title,
            "source_organization_id": obj.source_organization_id,
            "target_organization_id": obj.target_organization_id,
            "applicant_id": obj.applicant_id,
            "approver_id": obj.approver_id,
            "approval_status": obj.approval_status,
            "approval_time": obj.approval_time,
            "approval_remark": obj.approval_remark,
            "expect_execute_date": obj.expect_execute_date,
            "actual_execute_date": obj.actual_execute_date,
            "executor_id": obj.executor_id,
            "execute_status": obj.execute_status,
            "remark": obj.remark,
            "created_at": obj.created_at,
            "updated_at": obj.updated_at
        }

        # 加载关联信息
        if obj.source_organization_id:
            source_org = db.query(Organization).filter(
                Organization.id == obj.source_organization_id
            ).first()
            if source_org:
                result["source_organization"] = {
                    "id": source_org.id,
                    "org_name": source_org.org_name,
                    "org_type": source_org.org_type
                }

        if obj.target_organization_id:
            target_org = db.query(Organization).filter(
                Organization.id == obj.target_organization_id
            ).first()
            if target_org:
                result["target_organization"] = {
                    "id": target_org.id,
                    "org_name": target_org.org_name,
                    "org_type": target_org.org_type
                }

        if obj.applicant_id:
            applicant = db.query(User).filter(User.id == obj.applicant_id).first()
            if applicant:
                result["applicant"] = {
                    "id": applicant.id,
                    "real_name": applicant.real_name,
                    "username": applicant.username
                }

        if obj.approver_id:
            approver = db.query(User).filter(User.id == obj.approver_id).first()
            if approver:
                result["approver"] = {
                    "id": approver.id,
                    "real_name": approver.real_name,
                    "username": approver.username
                }

        if obj.executor_id:
            executor = db.query(User).filter(User.id == obj.executor_id).first()
            if executor:
                result["executor"] = {
                    "id": executor.id,
                    "real_name": executor.real_name,
                    "username": executor.username
                }

        # 加载明细
        items = allocation_item.get_by_order(db, obj.id)
        result["items"] = [
            {
                "id": item.id,
                "asset_id": item.asset_id,
                "asset_code": item.asset_code,
                "asset_name": item.asset_name,
                "from_status": item.from_status,
                "to_status": item.to_status,
                "execute_status": item.execute_status,
                "failure_reason": item.failure_reason
            }
            for item in items
        ]

        return result

    def _can_allocate(self, asset_status: str, order_type: str) -> bool:
        """判断资产是否可以分配"""
        # 库存中或使用中的资产可以分配
        if order_type in ["allocation", "transfer"]:
            return asset_status in ["in_stock", "in_use"]
        elif order_type == "recovery":
            return asset_status == "in_use"
        elif order_type == "maintenance":
            return asset_status in ["in_stock", "in_use"]
        elif order_type == "scrap":
            return asset_status in ["in_stock", "in_use", "maintenance"]
        return False

    def _get_order_type_name(self, order_type: str) -> str:
        """获取单据类型中文名"""
        type_names = {
            "allocation": "分配",
            "transfer": "调拨",
            "recovery": "回收",
            "maintenance": "维修",
            "scrap": "报废"
        }
        return type_names.get(order_type, "操作")

    async def _generate_order_code(self, db: Session, order_type: str) -> str:
        """生成分配单号"""
        from datetime import datetime
        import random
        import string

        # 单据类型前缀
        prefix_map = {
            "allocation": "AL",
            "transfer": "TF",
            "recovery": "RC",
            "maintenance": "MT",
            "scrap": "SC"
        }
        prefix = prefix_map.get(order_type, "AL")

        # 日期部分
        date_str = datetime.now().strftime("%Y%m%d")

        # 序号部分（4位随机数）
        sequence = "".join(random.choices(string.digits, k=4))

        # 组合单号: AL202501240001
        order_code = f"{prefix}{date_str}{sequence}"

        # 检查是否重复，如果重复则重新生成
        while allocation_order.get_by_code(db, order_code):
            sequence = "".join(random.choices(string.digits, k=4))
            order_code = f"{prefix}{date_str}{sequence}"

        return order_code


# 创建全局实例
allocation_service = AllocationService()
