from flask import Blueprint, jsonify, request, send_file
from app.extensions import db
# ★★★ 修复点：必须引入 datetime，否则下方更新时间时会报错 500 ★★★
from datetime import datetime, timedelta
from app.utils.decorators import permission_required
import uuid as uuid_module
import io
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill

# 导入模型
from app.models.inbound.buy import StockBuy
from app.models.inbound.stocktake import StocktakeDraft
from app.models.transaction import TransBorrow
from app.models.base import MaterialBase

# 尝试导入用户模型
try:
    from app.models.system import SysUser
except ImportError:
    SysUser = None

# 尝试导入半成品和成品
try:
    from app.models.inbound.semi import StockSemi
except ImportError:
    StockSemi = None

try:
    from app.models.inbound.product import StockProduct
except ImportError:
    StockProduct = None


def _normalize_user_id(user_id):
    """规范化 user_id，确保是有效字符串"""
    if not user_id or not isinstance(user_id, str) or len(user_id) > 100:
        return 'admin'
    return user_id.strip()


# 尝试导入半成品和成品
try:
    from app.models.inbound.semi import StockSemi
except ImportError:
    StockSemi = None

try:
    from app.models.inbound.product import StockProduct
except ImportError:
    StockProduct = None

from app.services.print.network_print_service import NetworkPrintService

bp = Blueprint('stock_ops', __name__)


# ============================================================
# 辅助函数：获取库存记录
# ============================================================
def get_stock_record(source_table, stock_id):
    """根据库存类型和ID获取库存记录"""
    if source_table == 'stock_buy' and StockBuy:
        return StockBuy.query.get(stock_id)
    elif source_table == 'stock_semi' and StockSemi:
        return StockSemi.query.get(stock_id)
    elif source_table == 'stock_product' and StockProduct:
        return StockProduct.query.get(stock_id)
    return None


def get_stock_info(uuid_or_barcode):
    """
    根据 uuid 或 barcode 查询库存信息
    返回: (item, source_table, stock_id)
    """
    # 1. 成品
    if StockProduct:
        item = StockProduct.query.filter(
            db.or_(StockProduct.barcode == uuid_or_barcode, StockProduct.sku == uuid_or_barcode)
        ).first()
        if item:
            return (item, 'stock_product', item.id)

    # 2. 半成品
    if StockSemi:
        item = StockSemi.query.filter(
            db.or_(StockSemi.barcode == uuid_or_barcode, StockSemi.sku == uuid_or_barcode)
        ).first()
        if item:
            return (item, 'stock_semi', item.id)

    # 3. 采购件
    if StockBuy:
        item = StockBuy.query.filter(
            db.or_(StockBuy.barcode == uuid_or_barcode, StockBuy.sku == uuid_or_barcode)
        ).first()
        if item:
            return (item, 'stock_buy', item.id)

    return (None, None, None)


@bp.route('/all', methods=['GET'])
@permission_required('inventory_stocktake')
def get_all_stock():
    """
    获取所有库存 > 0 的物品
    """
    try:
        # 1. 采购件
        materials = []
        if StockBuy:
            materials = StockBuy.query.filter(StockBuy.stock_quantity > 0).all()

        # 2. 半成品
        semis = []
        if StockSemi:
            try:
                semis = StockSemi.query.filter(StockSemi.stock_quantity > 0).all()
            except Exception:
                semis = []

        # 3. 成品
        products = []
        if StockProduct:
            try:
                products = StockProduct.query.filter(StockProduct.stock_quantity > 0).all()
            except Exception:
                products = []

        return jsonify({
            "materials": [item.to_dict() for item in materials],
            "semis": [item.to_dict() for item in semis],
            "products": [item.to_dict() for item in products]
        }), 200
    except Exception as e:
        print(f"Error: {e}")
        return jsonify({"message": f"查询库存失败: {str(e)}"}), 500


# --- 草稿箱接口 ---

@bp.route('/draft/list', methods=['GET'])
@permission_required('inventory_stocktake')
def get_drafts():
    """
    获取当前用户的盘点进度
    支持过滤: session_id
    """
    session_id = request.args.get('session_id')

    query = StocktakeDraft.query

    if session_id:
        query = query.filter_by(session_id=session_id)

    drafts = query.order_by(StocktakeDraft.scan_time.desc()).all()
    return jsonify([d.to_dict() for d in drafts]), 200


@bp.route('/draft/add', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def add_draft():
    """
    扫码同步 (支持更新数量)
    如果 session_id 不存在则创建新的会话
    
    差异计算逻辑调整：
    - adjusted_stock_qty = 账面总库存 - 借出未还数量
    - diff_qty = 实盘数量 - adjusted_stock_qty
    """
    try:
        data = request.json
        user_id = _normalize_user_id(data.get('user_id', 'admin'))
        uuid = data.get('uuid')
        quantity = float(data.get('quantity', 1))
        session_id = data.get('session_id')

        if not uuid:
            return jsonify({"message": "UUID不能为空"}), 400

        # 如果没有 session_id，创建新的
        if not session_id:
            session_id = f"STK-{datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid_module.uuid4().hex[:6]}"

        # 获取库存信息
        item, source_table, stock_id = get_stock_info(uuid)
        if not item:
            return jsonify({"message": "未找到对应的库存记录"}), 404

        # 账面总库存
        stock_qty = float(item.stock_quantity) if item.stock_quantity else 0
        
        # 计算借出未还数量 (quantity - returned_quantity)
        borrowed_result = db.session.query(
            db.func.sum(TransBorrow.quantity - TransBorrow.returned_quantity)
        ).filter(
            TransBorrow.source_table == source_table,
            TransBorrow.stock_id == stock_id,
            TransBorrow.is_returned == False
        ).scalar()
        total_borrowed = float(borrowed_result) if borrowed_result else 0
        
        # 调整后的账面可用库存 = 账面总库存 - 借出未还数量
        adjusted_stock_qty = stock_qty - total_borrowed

        # 查找是否已存在
        draft = StocktakeDraft.query.filter_by(user_id=user_id, uuid=uuid, session_id=session_id).first()

        if draft:
            # 如果已存在，更新数量和时间
            draft.quantity = quantity
            draft.scan_time = datetime.now()
            draft.stock_qty = adjusted_stock_qty
            draft.diff_qty = quantity - adjusted_stock_qty
            draft.source_table = source_table
            draft.stock_id = stock_id
        else:
            # 如果不存在，创建新的
            draft = StocktakeDraft(
                user_id=user_id,
                uuid=uuid,
                quantity=quantity,
                session_id=session_id,
                stock_qty=adjusted_stock_qty,
                diff_qty=quantity - adjusted_stock_qty,
                source_table=source_table,
                stock_id=stock_id
            )
            db.session.add(draft)

        db.session.commit()
        return jsonify({
            "message": "Saved",
            "session_id": session_id,
            "draft_id": draft.id,
            "adjusted_stock_qty": adjusted_stock_qty,
            "total_borrowed": total_borrowed
        }), 200
    except Exception as e:
        print(f"Add Draft Error: {e}")
        db.session.rollback()
        return jsonify({"message": str(e)}), 500


@bp.route('/draft/clear', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def clear_draft():
    """
    清除盘点草稿
    支持清除指定 session_id 的记录，或清除所有记录
    """
    data = request.json
    session_id = data.get('session_id')

    try:
        query = StocktakeDraft.query

        if session_id:
            # 清除指定会话
            query = query.filter_by(session_id=session_id)

        count = query.delete()
        db.session.commit()

        return jsonify({"message": f"已清除 {count} 条记录", "count": count}), 200
    except Exception as e:
        db.session.rollback()
        return jsonify({"message": str(e)}), 500


@bp.route('/draft/start-new', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def start_new_session():
    """
    开始新一轮盘点
    清空整张草稿表，返回新的 session_id
    """
    try:
        # 清空整张草稿表
        deleted_count = StocktakeDraft.query.delete()
        db.session.commit()

        # 生成新的 session_id
        new_session_id = f"STK-{datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid_module.uuid4().hex[:6]}"

        return jsonify({
            "message": f"已清除 {deleted_count} 条旧记录",
            "session_id": new_session_id,
            "cleared_count": deleted_count
        }), 200
    except Exception as e:
        db.session.rollback()
        return jsonify({"message": str(e)}), 500


# --- 盘点结束与差异报告 ---

@bp.route('/finish', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def finish_stocktake():
    """
    结束盘点
    直接返回成功，前端会跳转到差异列表
    草稿数据保留在表中
    """
    return jsonify({
        "message": "盘点已结束",
        "code": 200
    }), 200


@bp.route('/variance-report', methods=['GET'])
@permission_required('inventory_stocktake')
def get_variance_report():
    """
    获取盘点差异报告
    返回所有有差异的记录（diff_qty != 0）
    """
    session_id = request.args.get('session_id')

    try:
        query = StocktakeDraft.query

        if session_id:
            query = query.filter_by(session_id=session_id)

        # 只返回有差异的记录
        drafts = query.filter(StocktakeDraft.diff_qty != 0).order_by(
            StocktakeDraft.scan_time.desc(),
            StocktakeDraft.diff_qty.desc()
        ).all()

        # 补充库存详情
        result = []
        for draft in drafts:
            draft_dict = draft.to_dict()

            # 获取库存详情
            if draft.stock_id and draft.source_table:
                stock = get_stock_record(draft.source_table, draft.stock_id)
                if stock:
                    draft_dict['stock_name'] = getattr(stock, 'material_name', None) or \
                                               getattr(stock, 'product_name', None) or ''
                    draft_dict['stock_spec'] = getattr(stock, 'spec_model', '') or \
                                               getattr(stock, 'standard', '') or ''
                    draft_dict['stock_location'] = getattr(stock, 'warehouse_location', '') or ''
                    draft_dict['stock_unit'] = getattr(stock, 'unit', '个')

            result.append(draft_dict)

        return jsonify({
            "list": result,
            "total": len(result)
        }), 200
    except Exception as e:
        print(f"Error: {e}")
        return jsonify({"message": str(e)}), 500


# --- 单条库存调整 (手动平账) ---

@bp.route('/adjust', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def adjust_stock():
    """
    单条库存调整接口
    接收指定的草稿 ID，执行以下操作：
    1. 根据 diff_qty 调整库存 (stock_quantity 和 available_quantity)
    2. 生成流水账记录 (盘盈入库 / 盘亏出库)
    3. 标记草稿为 is_processed=True
    
    支持两种模式：
    - 有草稿模式：通过 draft_id 或 stock_id+source_table 查找草稿
    - 无草稿模式：直接传入 stock_id + diff_qty + source_table（未扫码直接盘亏）
    """
    data = request.json
    draft_id = data.get('draft_id')
    stock_id = data.get('stock_id')
    diff_qty = data.get('diff_qty')
    source_table = data.get('source_table')
    operator_name = data.get('operator_name', 'System')
    remark = data.get('remark', '')

    if not draft_id and not stock_id:
        return jsonify({"message": "draft_id 或 stock_id 不能同时为空"}), 400

    try:
        # 1. 尝试获取草稿
        draft = StocktakeDraft.query.get(draft_id) if draft_id else None
        if not draft and stock_id and source_table:
            draft = StocktakeDraft.query.filter_by(
                stock_id=stock_id,
                source_table=source_table
            ).first()
        elif not draft and stock_id:
            draft = StocktakeDraft.query.filter_by(stock_id=stock_id).first()

        # 2. 核心逻辑分支
        if draft:
            # 有草稿模式
            stock_id = draft.stock_id
            source_table = draft.source_table
            diff_qty = float(draft.diff_qty)
        else:
            # 无草稿模式（未扫码直接盘亏）
            if diff_qty is None or source_table is None or not stock_id:
                return jsonify({"message": "未扫码物资平账缺失必要参数（需提供 diff_qty 和 source_table）"}), 400
            diff_qty = float(diff_qty)

        # 3. 获取并校验真实的库存记录
        stock = get_stock_record(source_table, stock_id)
        if not stock:
            return jsonify({"message": "平账失败：物理库存记录已不存在"}), 404

        # 4. 计算调整
        if diff_qty > 0:
            # 盘盈：增加库存
            new_stock_qty = float(stock.stock_quantity or 0) + diff_qty
            new_avail_qty = float(stock.available_quantity or 0) + diff_qty
            action_type = '盘盈入库'
        elif diff_qty < 0:
            # 盘亏：减少库存
            abs_diff = abs(diff_qty)
            current_avail = float(stock.available_quantity or 0)
            if current_avail < abs_diff:
                return jsonify({
                    "message": f"可用库存不足，当前可用: {current_avail}，需要减少: {abs_diff}"
                }), 400

            new_stock_qty = float(stock.stock_quantity or 0) - abs_diff
            new_avail_qty = current_avail - abs_diff
            action_type = '盘亏出库'
        else:
            return jsonify({"message": "差异为0，无需调整"}), 400

        # 5. 执行库存调整
        stock.stock_quantity = new_stock_qty
        stock.available_quantity = new_avail_qty

        # 6. 生成流水账记录
        from app.models.outbound import TransOutbound

        # 生成唯一单号
        adj_no_suffix = draft.id if draft else f"MANUAL-{datetime.now().strftime('%Y%m%d%H%M%S')}"
        trans_record = TransOutbound(
            outbound_no=f"STKADJ-{datetime.now().strftime('%Y%m%d%H%M%S')}-{adj_no_suffix}",
            sku=stock.sku,
            source_table=source_table,
            stock_id=stock_id,
            barcode=getattr(stock, 'barcode', ''),
            quantity=abs(diff_qty),
            unit_price=getattr(stock, 'pre_tax_unit_price', 0) or getattr(stock, 'manual_cost', 0) or 0,
            outbound_type=action_type,  # 盘盈入库 / 盘亏出库
            consumer_name='盘点调整',
            operator_name=operator_name,
            remark=f"{action_type} - 盘点差异调整，备注: {remark}",
            outbound_time=datetime.now()
        )
        db.session.add(trans_record)

        # 7. 删除草稿记录（仅在有草稿时）
        if draft:
            db.session.delete(draft)

        db.session.commit()

        return jsonify({
            "message": f"{action_type}成功",
            "action_type": action_type,
            "diff_qty": diff_qty,
            "new_stock_qty": new_stock_qty,
            "new_avail_qty": new_avail_qty,
            "draft_id": draft_id if draft_id else (draft.id if draft else None)
        }), 200

    except Exception as e:
        db.session.rollback()
        print(f"Adjust Stock Error: {e}")
        return jsonify({"message": str(e)}), 500


@bp.route('/borrowed-quantities', methods=['POST'])
@permission_required('inventory_stocktake')
def get_borrowed_quantities():
    """批量获取借出未还数量"""
    data = request.json.get('items', [])
    result = {}
    for item in data:
        source = item.get('source_table')
        stock_id = item.get('stock_id')
        if source and stock_id is not None:
            qty = TransBorrow.get_borrowed_quantity(source, stock_id)
            result[f"{source}_{stock_id}"] = qty
    return jsonify(result), 200


# --- 打印接口 ---

@bp.route('/print/selection', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def print_selection():
    try:
        data = request.json
        items = data.get('items', [])
        if not items: return jsonify({"message": "未选择任何物品"}), 400
        printer = NetworkPrintService()
        success, msg = printer.print_outbound_selection(items)
        return jsonify({"message": "打印指令已发送" if success else msg}), 200 if success else 500
    except Exception as e:
        return jsonify({"message": str(e)}), 500


@bp.route('/print/stocktake', methods=['POST'])
@permission_required('inventory_stocktake:operation')
def print_stocktake():
    try:
        data = request.json
        printer = NetworkPrintService()
        success, msg = printer.print_stocktake_report(data)
        return jsonify({"message": "盘点报告已发送" if success else msg}), 200 if success else 500
    except Exception as e:
        return jsonify({"message": str(e)}), 500


@bp.route('/export-stocktake', methods=['GET'])
@permission_required('inventory_stocktake:operation')
def export_stocktake():
    """
    导出盘点报告 Excel
    包含3个Sheet:
    1. 盘点差异明细 (diff_qty != 0)
    2. 账实相符明细 (diff_qty == 0)
    3. 外借在用资产明细 (未归还的借出记录)
    """
    try:
        # 创建工作簿
        wb = Workbook()
        wb.remove(wb.active)
        
        # 定义样式
        header_font = Font(bold=True, size=11, color="FFFFFF")
        header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
        header_alignment = Alignment(horizontal="center", vertical="center")
        thin_border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )
        
        def get_material_info(source_table, stock_id):
            """获取物料基本信息"""
            if source_table == 'stock_buy':
                stock = StockBuy.query.get(stock_id)
            elif source_table == 'stock_semi':
                stock = StockSemi.query.get(stock_id) if StockSemi else None
            elif source_table == 'stock_product':
                stock = StockProduct.query.get(stock_id) if StockProduct else None
            else:
                return {'name': '-', 'sku': '-', 'spec': '-', 'unit': '-', 'location': '-'}

            if not stock:
                return {'name': '-', 'sku': '-', 'spec': '-', 'unit': '-', 'location': '-'}

            # 安全获取 sku
            stock_sku = getattr(stock, 'sku', None) or getattr(stock, 'SKU', None) or '-'

            # 使用 base_id 关联查询物料基础表
            material = None
            base_id = getattr(stock, 'base_id', None)
            if base_id:
                material = MaterialBase.query.get(base_id)

            # 规格型号：从 MaterialBase 的 spec_model 字段获取
            spec = getattr(material, 'spec_model', None) if material else '-'
            if not spec or spec == '-':
                spec = getattr(stock, 'spec_model', None) or getattr(stock, 'standard', None) or '-'

            # 库位：从库存表的 warehouse_location 字段获取
            location = getattr(stock, 'warehouse_location', None) or '-'

            return {
                'name': material.name if material else stock_sku,
                'sku': stock_sku,
                'spec': spec,
                'unit': getattr(stock, 'unit', None) or '个',
                'location': location
            }

        def get_user_name(user_id):
            """获取用户真实姓名
            SysUser.username 存储格式为 "真实姓名/登录账号" (例如: 张三/zhangsan01)
            """
            if not SysUser or not user_id:
                return str(user_id) if user_id else '-'
            try:
                user = None
                # 尝试通过ID或用户名查找
                if str(user_id).isdigit():
                    user = SysUser.query.get(int(user_id))
                if not user:
                    user = SysUser.query.filter(SysUser.username.like(f"%/{user_id}")).first()
                if not user:
                    user = SysUser.query.filter_by(username=str(user_id)).first()
                
                if not user:
                    return str(user_id)
                
                # 解析 username 格式: "张三/zhangsan01" -> 取前面的真实姓名
                raw_username = getattr(user, 'username', None) or str(user_id)
                if '/' in raw_username:
                    return raw_username.split('/')[0]
                return raw_username
            except:
                return str(user_id)

        def to_beijing_time(dt):
            """转换为北京时间（+8小时）"""
            if not dt:
                return ''
            try:
                if isinstance(dt, str):
                    return dt[:19]
                return (dt + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S')
            except:
                return str(dt)[:19]
        
        def set_header_row(ws, headers):
            for col, header in enumerate(headers, 1):
                cell = ws.cell(row=1, column=col, value=header)
                cell.font = header_font
                cell.fill = header_fill
                cell.alignment = header_alignment
                cell.border = thin_border
        
        # ===== Sheet 1: 盘点全景汇总表 (放在最前面) =====
        ws1 = wb.create_sheet("盘点全景汇总表", 0)
        summary_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点状态", "盘点人", "盘点时间"]
        set_header_row(ws1, summary_headers)
        master_row_idx = 2  # 汇总表行计数器

        # ===== Sheet 2: 盘点差异明细 =====
        ws2 = wb.create_sheet("盘点差异明细")
        diff_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
        set_header_row(ws2, diff_headers)

        diff_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty != 0).all()
        for row_idx, draft in enumerate(diff_drafts, 2):
            mat_info = get_material_info(draft.source_table, draft.stock_id)
            # 写入 Sheet 2 (差异明细)
            ws2.cell(row=row_idx, column=1, value=mat_info['name']).border = thin_border
            ws2.cell(row=row_idx, column=2, value=mat_info['sku']).border = thin_border
            ws2.cell(row=row_idx, column=3, value=mat_info['spec']).border = thin_border
            ws2.cell(row=row_idx, column=4, value=mat_info['location']).border = thin_border
            ws2.cell(row=row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
            ws2.cell(row=row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
            ws2.cell(row=row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
            ws2.cell(row=row_idx, column=8, value=get_user_name(draft.user_id)).border = thin_border
            ws2.cell(row=row_idx, column=9, value=to_beijing_time(draft.scan_time)).border = thin_border
            # 同时写入 Sheet 1 (汇总表)
            ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border
            ws1.cell(row=master_row_idx, column=2, value=mat_info['sku']).border = thin_border
            ws1.cell(row=master_row_idx, column=3, value=mat_info['spec']).border = thin_border
            ws1.cell(row=master_row_idx, column=4, value=mat_info['location']).border = thin_border
            ws1.cell(row=master_row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=8, value="有差异").border = thin_border
            ws1.cell(row=master_row_idx, column=9, value=get_user_name(draft.user_id)).border = thin_border
            ws1.cell(row=master_row_idx, column=10, value=to_beijing_time(draft.scan_time)).border = thin_border
            master_row_idx += 1

        # ===== Sheet 3: 账实相符明细 =====
        ws3 = wb.create_sheet("账实相符明细")
        normal_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "盘点人", "盘点时间"]
        set_header_row(ws3, normal_headers)

        normal_drafts = StocktakeDraft.query.filter(StocktakeDraft.diff_qty == 0).all()
        for row_idx, draft in enumerate(normal_drafts, 2):
            mat_info = get_material_info(draft.source_table, draft.stock_id)
            # 写入 Sheet 3 (账实相符)
            ws3.cell(row=row_idx, column=1, value=mat_info['name']).border = thin_border
            ws3.cell(row=row_idx, column=2, value=mat_info['sku']).border = thin_border
            ws3.cell(row=row_idx, column=3, value=mat_info['spec']).border = thin_border
            ws3.cell(row=row_idx, column=4, value=mat_info['location']).border = thin_border
            ws3.cell(row=row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
            ws3.cell(row=row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
            ws3.cell(row=row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
            ws3.cell(row=row_idx, column=8, value=get_user_name(draft.user_id)).border = thin_border
            ws3.cell(row=row_idx, column=9, value=to_beijing_time(draft.scan_time)).border = thin_border
            # 同时写入 Sheet 1 (汇总表)
            ws1.cell(row=master_row_idx, column=1, value=mat_info['name']).border = thin_border
            ws1.cell(row=master_row_idx, column=2, value=mat_info['sku']).border = thin_border
            ws1.cell(row=master_row_idx, column=3, value=mat_info['spec']).border = thin_border
            ws1.cell(row=master_row_idx, column=4, value=mat_info['location']).border = thin_border
            ws1.cell(row=master_row_idx, column=5, value=float(draft.stock_qty or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=6, value=float(draft.quantity or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=7, value=float(draft.diff_qty or 0)).border = thin_border
            ws1.cell(row=master_row_idx, column=8, value="正常").border = thin_border
            ws1.cell(row=master_row_idx, column=9, value=get_user_name(draft.user_id)).border = thin_border
            ws1.cell(row=master_row_idx, column=10, value=to_beijing_time(draft.scan_time)).border = thin_border
            master_row_idx += 1

        # ===== Sheet 4: 外借在用资产明细 =====
        ws4 = wb.create_sheet("外借在用资产明细")
        borrow_headers = ["借出单号", "借用人", "物料名称", "SKU", "规格型号", "借出总数", "已还数量", "待还数量", "借出时间", "预计归还时间"]
        set_header_row(ws4, borrow_headers)
        
        # 查询未归还的借出记录
        unreturned_borrows = TransBorrow.query.filter(TransBorrow.is_returned == False).all()
        for row_idx, borrow in enumerate(unreturned_borrows, 2):
            mat_info = get_material_info(borrow.source_table, borrow.stock_id)
            total_qty = float(borrow.quantity or 0)
            returned_qty = float(borrow.returned_quantity or 0)
            pending_qty = total_qty - returned_qty

            ws3.cell(row=row_idx, column=1, value=borrow.borrow_no or '').border = thin_border
            ws3.cell(row=row_idx, column=2, value=borrow.borrower_name or '').border = thin_border
            ws3.cell(row=row_idx, column=3, value=mat_info['name']).border = thin_border
            ws3.cell(row=row_idx, column=4, value=mat_info['sku']).border = thin_border
            ws3.cell(row=row_idx, column=5, value=mat_info['spec']).border = thin_border
            ws3.cell(row=row_idx, column=6, value=total_qty).border = thin_border
            ws3.cell(row=row_idx, column=7, value=returned_qty).border = thin_border
            ws3.cell(row=row_idx, column=8, value=pending_qty).border = thin_border
            ws3.cell(row=row_idx, column=9, value=to_beijing_time(borrow.borrow_time)).border = thin_border
            ws3.cell(row=row_idx, column=10, value='无限期' if not borrow.expected_return_time else to_beijing_time(borrow.expected_return_time)).border = thin_border

        # ===== Sheet 5: 未盘点明细(疑似漏盘) =====
        # 逻辑：获取已盘点的集合，遍历库存表，找出未盘点且有库存的物资
        ws5 = wb.create_sheet("未盘点明细(疑似漏盘)")
        unscanned_headers = ["物料名称", "SKU", "规格型号", "库位", "调整后账面数", "实盘数", "差异数", "状态"]
        set_header_row(ws5, unscanned_headers)

        # 获取已盘点的 (source_table, stock_id) 集合
        all_drafts = StocktakeDraft.query.all()
        scanned_set = {(d.source_table, d.stock_id) for d in all_drafts}

        def get_borrowed_qty(source_table, stock_id):
            """获取某库存的借出未还数量"""
            try:
                borrowed = TransBorrow.query.filter(
                    TransBorrow.source_table == source_table,
                    TransBorrow.stock_id == stock_id,
                    TransBorrow.is_returned == False
                ).all()
                total = sum((b.quantity or 0) - (b.returned_quantity or 0) for b in borrowed)
                return total
            except:
                return 0

        unscanned_items = []

        # 遍历 StockBuy
        for stock in StockBuy.query.all():
            key = ('stock_buy', stock.id)
            if key in scanned_set:
                continue
            stock_qty = float(stock.stock_quantity or 0)
            if stock_qty <= 0:
                continue
            # 扣除外借数量
            borrowed_qty = get_borrowed_qty('stock_buy', stock.id)
            expected_qty = stock_qty - borrowed_qty
            if expected_qty > 0:
                mat_info = get_material_info('stock_buy', stock.id)
                unscanned_items.append({
                    'name': mat_info['name'],
                    'sku': mat_info['sku'],
                    'spec': mat_info['spec'],
                    'location': mat_info['location'],
                    'stock_qty': expected_qty,
                    'actual_qty': 0,
                    'diff_qty': -expected_qty,
                    'status': '未盘点'
                })

        # 遍历 StockSemi
        if StockSemi:
            for stock in StockSemi.query.all():
                key = ('stock_semi', stock.id)
                if key in scanned_set:
                    continue
                stock_qty = float(stock.stock_quantity or 0)
                if stock_qty <= 0:
                    continue
                borrowed_qty = get_borrowed_qty('stock_semi', stock.id)
                expected_qty = stock_qty - borrowed_qty
                if expected_qty > 0:
                    mat_info = get_material_info('stock_semi', stock.id)
                    unscanned_items.append({
                        'name': mat_info['name'],
                        'sku': mat_info['sku'],
                        'spec': mat_info['spec'],
                        'location': mat_info['location'],
                        'stock_qty': expected_qty,
                        'actual_qty': 0,
                        'diff_qty': -expected_qty,
                        'status': '未盘点'
                    })

        # 遍历 StockProduct
        if StockProduct:
            for stock in StockProduct.query.all():
                key = ('stock_product', stock.id)
                if key in scanned_set:
                    continue
                stock_qty = float(stock.stock_quantity or 0)
                if stock_qty <= 0:
                    continue
                borrowed_qty = get_borrowed_qty('stock_product', stock.id)
                expected_qty = stock_qty - borrowed_qty
                if expected_qty > 0:
                    mat_info = get_material_info('stock_product', stock.id)
                    unscanned_items.append({
                        'name': mat_info['name'],
                        'sku': mat_info['sku'],
                        'spec': mat_info['spec'],
                        'location': mat_info['location'],
                        'stock_qty': expected_qty,
                        'actual_qty': 0,
                        'diff_qty': -expected_qty,
                        'status': '未盘点'
                    })

        # 写入未盘点明细
        for row_idx, item in enumerate(unscanned_items, 2):
            # 写入 Sheet 5 (未盘点明细)
            ws5.cell(row=row_idx, column=1, value=item['name']).border = thin_border
            ws5.cell(row=row_idx, column=2, value=item['sku']).border = thin_border
            ws5.cell(row=row_idx, column=3, value=item['spec']).border = thin_border
            ws5.cell(row=row_idx, column=4, value=item['location']).border = thin_border
            ws5.cell(row=row_idx, column=5, value=item['stock_qty']).border = thin_border
            ws5.cell(row=row_idx, column=6, value=item['actual_qty']).border = thin_border
            ws5.cell(row=row_idx, column=7, value=item['diff_qty']).border = thin_border
            ws5.cell(row=row_idx, column=8, value=item['status']).border = thin_border
            # 同时写入 Sheet 1 (汇总表) - 盘点人和时间留空
            ws1.cell(row=master_row_idx, column=1, value=item['name']).border = thin_border
            ws1.cell(row=master_row_idx, column=2, value=item['sku']).border = thin_border
            ws1.cell(row=master_row_idx, column=3, value=item['spec']).border = thin_border
            ws1.cell(row=master_row_idx, column=4, value=item['location']).border = thin_border
            ws1.cell(row=master_row_idx, column=5, value=item['stock_qty']).border = thin_border
            ws1.cell(row=master_row_idx, column=6, value=item['actual_qty']).border = thin_border
            ws1.cell(row=master_row_idx, column=7, value=item['diff_qty']).border = thin_border
            ws1.cell(row=master_row_idx, column=8, value="未盘点").border = thin_border
            ws1.cell(row=master_row_idx, column=9, value="").border = thin_border
            ws1.cell(row=master_row_idx, column=10, value="").border = thin_border
            master_row_idx += 1

        # 调整列宽
        for ws in [ws1, ws2, ws3, ws4, ws5]:
            for col in ws.columns:
                max_length = 0
                col_letter = col[0].column_letter
                for cell in col:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                ws.column_dimensions[col_letter].width = min(max_length + 2, 30)
        
        # 生成文件
        output = io.BytesIO()
        wb.save(output)
        output.seek(0)
        
        filename = f"盘点报告_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
        return send_file(
            output,
            mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            as_attachment=True,
            download_name=filename
        )
    except Exception as e:
        print(f"Export Stocktake Error: {e}")
        import traceback
        traceback.print_exc()
        return jsonify({"message": f"导出失败: {str(e)}"}), 500
