#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
出库不结算数据模型
单表: shein_out_not_settle_data
使用SQLAlchemy定义表结构，支持批量upsert
"""

from sqlalchemy import create_engine, Column, Integer, String, Text, DateTime, Numeric, BigInteger, UniqueConstraint, Index
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.mysql import insert
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import time

import openpyxl

Base = declarative_base()

# Excel列名 → DB列名 映射 (顺序与Excel列一致)
EXCEL_COLUMN_MAPPING = [
    ('变动类型', 'display_change_type_name'),
    ('结算类型', 'settle_type_name'),
    ('供方货号', 'supplier_code'),
    ('SKC', 'skc'),
    ('平台SKU', 'sku'),
    ('商家SKU', 'supplier_sku'),
    ('属性集', 'suffix_zh'),
    ('数量', 'quantity'),
    ('单价', 'cost'),
    ('金额', 'amount'),
    ('币种', 'currency'),
    ('业务单号', 'business_no'),
    ('单据号', 'bill_no'),
    ('添加时间', 'add_time'),
    ('商家ID', 'supplier_id'),
    ('商家名称', 'supplier_name'),
    ('销售商家ID', 'sales_seller_id'),
    ('销售商家名称', 'sales_seller_name'),
    ('备注', 'remark'),
    ('活动信息', 'activity_info'),
]


class SheinOutNotSettleData(Base):
    """出库不结算表"""
    __tablename__ = 'shein_out_not_settle_data'

    id = Column(Integer, primary_key=True, autoincrement=True, comment='主键ID')
    display_change_type_name = Column(String(50), nullable=True, comment='变动类型')
    settle_type_name = Column(String(50), nullable=True, comment='结算类型')
    supplier_code = Column(String(100), nullable=True, comment='供方货号')
    skc = Column(String(100), nullable=True, comment='SKC')
    sku = Column(String(100), nullable=True, comment='平台SKU')
    supplier_sku = Column(String(100), nullable=True, comment='商家SKU')
    suffix_zh = Column(String(200), nullable=True, comment='属性集')
    quantity = Column(Integer, nullable=True, comment='数量')
    cost = Column(Numeric(12, 2), nullable=True, comment='单价')
    amount = Column(Numeric(12, 2), nullable=True, comment='金额')
    currency = Column(String(10), nullable=True, comment='币种')
    business_no = Column(String(200), nullable=True, comment='业务单号')
    bill_no = Column(String(200), nullable=True, comment='单据号')
    add_time = Column(String(30), nullable=True, comment='添加时间')
    supplier_id = Column(String(50), nullable=True, comment='商家ID')
    supplier_name = Column(String(200), nullable=True, comment='商家名称')
    sales_seller_id = Column(String(50), nullable=True, comment='销售商家ID')
    sales_seller_name = Column(String(200), nullable=True, comment='销售商家名称')
    remark = Column(Text, nullable=True, comment='备注')
    activity_info = Column(Text, nullable=True, comment='活动信息')
    store_username = Column(String(50), nullable=False, comment='店铺账号')
    store_name = Column(String(100), nullable=True, comment='店铺别名')
    created_at = Column(DateTime, default=datetime.now, comment='创建时间')

    __table_args__ = (
        UniqueConstraint('business_no', 'sku', 'store_username', name='uk_out_not_settle_unique'),
        Index('idx_out_not_settle_store', 'store_username'),
        Index('idx_out_not_settle_add_time', 'add_time'),
    )


class OutNotSettleDataManager:
    """
    出库不结算数据管理器
    """

    def __init__(self, database_url):
        self.engine = create_engine(
            database_url,
            echo=False,
            pool_size=20,
            max_overflow=10,
            pool_recycle=3600,
            pool_pre_ping=True,
        )
        self.Session = sessionmaker(bind=self.engine, autoflush=False)

    def create_table(self):
        """创建数据表"""
        Base.metadata.create_all(self.engine, tables=[SheinOutNotSettleData.__table__], checkfirst=True)
        print("[OK] 表 shein_out_not_settle_data 创建成功")

    def read_excel_data(self, excel_path, store_username, store_name):
        """
        从Excel文件读取出库不结算数据

        Args:
            excel_path: Excel文件路径
            store_username: 店铺账号
            store_name: 店铺别名
        Returns:
            list: 数据字典列表
        """
        wb = openpyxl.load_workbook(excel_path)
        ws = wb.active

        if ws.max_row is None or ws.max_row < 2:
            wb.close()
            return []

        rows = []
        for row in ws.iter_rows(values_only=True):
            rows.append(row)

        headers = [str(h).strip() if h else '' for h in rows[0]]

        col_map = dict(EXCEL_COLUMN_MAPPING)
        header_to_db = {}
        for idx, header in enumerate(headers):
            if header in col_map:
                header_to_db[idx] = col_map[header]

        records = []
        for row in rows[1:]:
            record = {}
            for idx, db_col in header_to_db.items():
                val = row[idx] if idx < len(row) else None
                if val is not None and str(val).strip() in ('', '/', '-'):
                    val = None
                # 商家ID可能是float，需要转为字符串
                if db_col in ('supplier_id', 'sales_seller_id') and val is not None:
                    val = str(int(val)) if isinstance(val, float) else str(val)
                record[db_col] = val
            record['store_username'] = store_username
            record['store_name'] = store_name
            record['created_at'] = datetime.now()
            records.append(record)

        wb.close()
        return records

    def upsert_data(self, records, batch_size=1000):
        """
        批量upsert出库不结算数据

        Args:
            records: 数据字典列表
            batch_size: 每批次大小
        """
        if not records:
            print("没有数据需要插入")
            return

        self.create_table()

        max_retries = 3
        with self.engine.connect() as conn:
            for i in range(0, len(records), batch_size):
                batch = records[i:i + batch_size]
                stmt = insert(SheinOutNotSettleData).values(batch)
                stmt = stmt.on_duplicate_key_update(
                    display_change_type_name=stmt.inserted.display_change_type_name,
                    settle_type_name=stmt.inserted.settle_type_name,
                    quantity=stmt.inserted.quantity,
                    cost=stmt.inserted.cost,
                    amount=stmt.inserted.amount,
                    remark=stmt.inserted.remark,
                )
                for retry in range(max_retries):
                    try:
                        conn.execute(stmt)
                        break
                    except OperationalError as e:
                        if 'Deadlock' in str(e) and retry < max_retries - 1:
                            print(f"[WARN] 死锁，重试 {retry + 1}/{max_retries}...")
                            time.sleep(1)
                            continue
                        raise
            conn.commit()

        print(f"[OK] 成功 upsert {len(records)} 条出库不结算数据")

    def import_from_excel_files(self, excel_paths, store_username, store_name):
        """
        从多个Excel文件导入数据

        Args:
            excel_paths: Excel文件路径列表
            store_username: 店铺账号
            store_name: 店铺别名
        """
        all_records = []
        for path in excel_paths:
            records = self.read_excel_data(path, store_username, store_name)
            all_records.extend(records)
            print(f"[INFO] 读取 {path}: {len(records)} 条记录")

        if all_records:
            self.upsert_data(all_records)
        else:
            print(f"[INFO] 没有出库不结算数据需要导入")
