#!/usr/bin/env python3
import argparse
import subprocess
import sys


def build_sql(days: int, lead_days: int, warn_days: int, limit: int, include_safe: bool) -> str:
    include_safe_bit = 1 if include_safe else 0
    return f"""
USE [JSDataMiddlePlatform];
SET NOCOUNT ON;

DECLARE @lookback_days int = {days};
DECLARE @lead_days int = {lead_days};
DECLARE @warn_days int = {warn_days};
DECLARE @limit int = {limit};
DECLARE @include_safe bit = {include_safe_bit};
DECLARE @start_time datetime2 = DATEADD(day, -@lookback_days, GETDATE());
DECLARE @end_time datetime2 = GETDATE();

IF OBJECT_ID('tempdb..#ship_base') IS NOT NULL
    DROP TABLE #ship_base;

SELECT
    NULLIF(a.undefined10, '') AS sku_id,
    NULLIF(a.undefined2, '') AS order_id,
    NULLIF(a.undefined12, '') AS product_id,
    ISNULL(NULLIF(a.undefined8, ''), 'unknown') AS shop_name,
    CASE
        WHEN CHARINDEX('-', ISNULL(NULLIF(a.undefined8, ''), '')) > 0
            THEN LEFT(ISNULL(NULLIF(a.undefined8, ''), ''), CHARINDEX('-', ISNULL(NULLIF(a.undefined8, ''), '')) - 1)
        ELSE ISNULL(NULLIF(a.undefined8, ''), '未知渠道')
    END AS shop_channel,
    ISNULL(NULLIF(a.undefined66, ''), '') AS ship_warehouse_name,
    COALESCE(
        TRY_CONVERT(datetime2, NULLIF(a.undefined6, ''), 120),
        TRY_CONVERT(datetime2, REPLACE(NULLIF(a.undefined6, ''), '/', '-'), 120),
        TRY_CONVERT(datetime2, NULLIF(a.undefined6, ''), 111),
        TRY_CONVERT(datetime2, a.createdAt)
    ) AS ship_dt,
    COALESCE(
        TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined30, '')),
        TRY_CONVERT(decimal(18, 2), NULLIF(a.undefined20, '')),
        CAST(0 AS decimal(18, 2))
    ) AS shipped_qty
INTO #ship_base
FROM dbo.jst_goods_profit_kpi_items_afs AS a WITH (NOLOCK)
WHERE ISNULL(NULLIF(a.undefined10, ''), '') <> ''
  AND ISNULL(NULLIF(a.undefined62, ''), '') NOT LIKE '%配件%'
  AND ISNULL(NULLIF(a.undefined62, ''), '') NOT LIKE '%特殊单%';

IF OBJECT_ID('tempdb..#ship_stats') IS NOT NULL
    DROP TABLE #ship_stats;

SELECT
    sb.sku_id,
    COUNT(DISTINCT sb.order_id) AS order_count_30d,
    COUNT(DISTINCT CASE WHEN ISNULL(sb.product_id, '') <> '' THEN sb.product_id END) AS product_id_count_30d,
    COUNT(DISTINCT sb.shop_name) AS shop_count_30d,
    SUM(CASE WHEN sb.ship_dt >= DATEADD(day, -7, @end_time) THEN ISNULL(sb.shipped_qty, 0) ELSE 0 END) AS shipped_qty_7d,
    SUM(CASE WHEN sb.ship_dt >= DATEADD(day, -30, @end_time) THEN ISNULL(sb.shipped_qty, 0) ELSE 0 END) AS shipped_qty_30d,
    MAX(sb.ship_dt) AS last_ship_time
INTO #ship_stats
FROM #ship_base AS sb
WHERE sb.ship_dt >= @start_time
  AND sb.ship_dt < @end_time
GROUP BY sb.sku_id;

IF OBJECT_ID('tempdb..#sku_ref') IS NOT NULL
    DROP TABLE #sku_ref;

SELECT
    s.sku_id,
    s.name AS sku_name,
    s.other_1 AS spu,
    s.i_id AS style_code,
    s.category,
    s.item_type,
    s.brand,
    s.supplier_name,
    s.other_3 AS default_warehouse,
    s.other_5 AS sku_status,
    s.cost_price,
    s.other_10 AS board_material,
    s.modified
INTO #sku_ref
FROM dbo.jst_sku AS s WITH (NOLOCK)
WHERE NULLIF(s.sku_id, '') IN (SELECT sku_id FROM #ship_stats);

IF OBJECT_ID('tempdb..#inv_latest') IS NOT NULL
    DROP TABLE #inv_latest;

WITH inv_rank AS (
    SELECT
        NULLIF(i.sku_id, '') AS sku_id,
        TRY_CONVERT(decimal(18, 2), i.qty) AS stock_qty,
        TRY_CONVERT(decimal(18, 2), i.order_lock) AS order_lock_qty,
        TRY_CONVERT(decimal(18, 2), i.pick_lock) AS pick_lock_qty,
        TRY_CONVERT(decimal(18, 2), i.lock_qty) AS lock_qty,
        TRY_CONVERT(decimal(18, 2), i.virtual_qty) AS virtual_qty,
        TRY_CONVERT(decimal(18, 2), i.defective_qty) AS defective_qty,
        TRY_CONVERT(decimal(18, 2), i.purchase_qty) AS purchase_qty,
        TRY_CONVERT(decimal(18, 2), i.return_qty) AS return_qty,
        TRY_CONVERT(decimal(18, 2), i.in_qty) AS in_qty,
        TRY_CONVERT(decimal(18, 2), i.min_qty) AS min_qty,
        TRY_CONVERT(decimal(18, 2), i.max_qty) AS max_qty,
        i.wms_co_id,
        i.name AS inventory_note,
        COALESCE(i.updateTime, i.modified, i.createTime) AS inventory_snapshot_time,
        ROW_NUMBER() OVER (
            PARTITION BY NULLIF(i.sku_id, '')
            ORDER BY COALESCE(i.updateTime, i.modified, i.createTime) DESC, i.Id DESC
        ) AS rn
    FROM dbo.jst_inventoryInfo AS i WITH (NOLOCK)
    WHERE NULLIF(i.sku_id, '') IN (SELECT sku_id FROM #ship_stats)
)
SELECT
    sku_id,
    stock_qty,
    order_lock_qty,
    pick_lock_qty,
    lock_qty,
    virtual_qty,
    defective_qty,
    purchase_qty,
    return_qty,
    in_qty,
    min_qty,
    max_qty,
    wms_co_id,
    inventory_note,
    inventory_snapshot_time,
    CASE
        WHEN ISNULL(stock_qty, 0) - ISNULL(order_lock_qty, 0) - ISNULL(pick_lock_qty, 0) - ISNULL(lock_qty, 0) < 0
            THEN CAST(0 AS decimal(18, 2))
        ELSE ISNULL(stock_qty, 0) - ISNULL(order_lock_qty, 0) - ISNULL(pick_lock_qty, 0) - ISNULL(lock_qty, 0)
    END AS available_qty
INTO #inv_latest
FROM inv_rank
WHERE rn = 1;

IF OBJECT_ID('tempdb..#sku_owner') IS NOT NULL
    DROP TABLE #sku_owner;

SELECT
    sr.sku_id,
    COALESCE(
        NULLIF(pc_exact.purchaser, ''),
        NULLIF(pc_style.purchaser, ''),
        NULLIF(pc_base.purchaser, ''),
        '未分配'
    ) AS purchaser_name,
    COALESCE(
        NULLIF(po_exact.oper, ''),
        NULLIF(po_style.oper, ''),
        NULLIF(po_base.oper, ''),
        NULLIF(pc_exact.oper, ''),
        NULLIF(pc_style.oper, ''),
        NULLIF(pc_base.oper, ''),
        '未分配'
    ) AS spu_oper_name
INTO #sku_owner
FROM #sku_ref AS sr
OUTER APPLY (
    SELECT TOP 1 c.purchaser, c.oper
    FROM dbo.fys_SkuDataLibrary_SpuConfig AS c WITH (NOLOCK)
    WHERE NULLIF(c.spu, '') = NULLIF(sr.spu, '')
    ORDER BY c.Id DESC
) AS pc_exact
OUTER APPLY (
    SELECT TOP 1 c.purchaser, c.oper
    FROM dbo.fys_SkuDataLibrary_SpuConfig AS c WITH (NOLOCK)
    WHERE NULLIF(c.spu, '') = NULLIF(sr.style_code, '')
    ORDER BY c.Id DESC
) AS pc_style
OUTER APPLY (
    SELECT TOP 1 c.purchaser, c.oper
    FROM dbo.fys_SkuDataLibrary_SpuConfig AS c WITH (NOLOCK)
    WHERE NULLIF(c.spu, '') = CASE
        WHEN CHARINDEX('K', ISNULL(sr.spu, '')) > 0
            THEN LEFT(sr.spu, CHARINDEX('K', sr.spu) - 1)
        ELSE NULL
    END
    ORDER BY c.Id DESC
) AS pc_base
OUTER APPLY (
    SELECT TOP 1 o.oper
    FROM dbo.op_operManage AS o WITH (NOLOCK)
    WHERE NULLIF(o.spu, '') = NULLIF(sr.spu, '')
    ORDER BY o.Id DESC
) AS po_exact
OUTER APPLY (
    SELECT TOP 1 o.oper
    FROM dbo.op_operManage AS o WITH (NOLOCK)
    WHERE NULLIF(o.spu, '') = NULLIF(sr.style_code, '')
    ORDER BY o.Id DESC
) AS po_style
OUTER APPLY (
    SELECT TOP 1 o.oper
    FROM dbo.op_operManage AS o WITH (NOLOCK)
    WHERE NULLIF(o.spu, '') = CASE
        WHEN CHARINDEX('K', ISNULL(sr.spu, '')) > 0
            THEN LEFT(sr.spu, CHARINDEX('K', sr.spu) - 1)
        ELSE NULL
    END
    ORDER BY o.Id DESC
) AS po_base;

IF OBJECT_ID('tempdb..#operator_top') IS NOT NULL
    DROP TABLE #operator_top;

WITH oper_rank AS (
    SELECT
        sb.sku_id,
        NULLIF(t.oper, '') AS oper_name,
        SUM(ISNULL(sb.shipped_qty, 0)) AS shipped_qty_sum_30d,
        ROW_NUMBER() OVER (
            PARTITION BY sb.sku_id
            ORDER BY SUM(ISNULL(sb.shipped_qty, 0)) DESC, NULLIF(t.oper, '') ASC
        ) AS rn
    FROM #ship_base AS sb
    INNER JOIN dbo.op_TargetManagement AS t WITH (NOLOCK)
        ON NULLIF(t.shopproid, '') = NULLIF(sb.product_id, '')
       AND ISNULL(NULLIF(t.shopname, ''), '') = ISNULL(NULLIF(sb.shop_name, ''), '')
    WHERE sb.ship_dt >= @start_time
      AND sb.ship_dt < @end_time
      AND ISNULL(NULLIF(t.oper, ''), '') <> ''
    GROUP BY sb.sku_id, NULLIF(t.oper, '')
)
SELECT
    sku_id,
    oper_name AS operator_name
INTO #operator_top
FROM oper_rank
WHERE rn = 1;

IF OBJECT_ID('tempdb..#channel_top') IS NOT NULL
    DROP TABLE #channel_top;

WITH channel_rank AS (
    SELECT
        sb.sku_id,
        sb.shop_channel,
        SUM(ISNULL(sb.shipped_qty, 0)) AS shipped_qty_sum_30d,
        ROW_NUMBER() OVER (
            PARTITION BY sb.sku_id
            ORDER BY SUM(ISNULL(sb.shipped_qty, 0)) DESC, sb.shop_channel ASC
        ) AS rn
    FROM #ship_base AS sb
    WHERE sb.ship_dt >= @start_time
      AND sb.ship_dt < @end_time
    GROUP BY sb.sku_id, sb.shop_channel
)
SELECT
    sku_id,
    shop_channel AS top_shop_channel,
    shipped_qty_sum_30d AS top_channel_shipped_qty_30d
INTO #channel_top
FROM channel_rank
WHERE rn = 1;

IF OBJECT_ID('tempdb..#shop_top') IS NOT NULL
    DROP TABLE #shop_top;

WITH shop_rank AS (
    SELECT
        sb.sku_id,
        sb.shop_name,
        SUM(ISNULL(sb.shipped_qty, 0)) AS shipped_qty_sum_30d,
        ROW_NUMBER() OVER (
            PARTITION BY sb.sku_id
            ORDER BY SUM(ISNULL(sb.shipped_qty, 0)) DESC, sb.shop_name ASC
        ) AS rn
    FROM #ship_base AS sb
    WHERE sb.ship_dt >= @start_time
      AND sb.ship_dt < @end_time
    GROUP BY sb.sku_id, sb.shop_name
)
SELECT
    sku_id,
    shop_name AS top_shop_name,
    shipped_qty_sum_30d AS top_shop_shipped_qty_30d
INTO #shop_top
FROM shop_rank
WHERE rn = 1;

IF OBJECT_ID('tempdb..#warehouse_top') IS NOT NULL
    DROP TABLE #warehouse_top;

WITH wh_rank AS (
    SELECT
        sb.sku_id,
        sb.ship_warehouse_name,
        SUM(ISNULL(sb.shipped_qty, 0)) AS shipped_qty_sum_30d,
        ROW_NUMBER() OVER (
            PARTITION BY sb.sku_id
            ORDER BY SUM(ISNULL(sb.shipped_qty, 0)) DESC, sb.ship_warehouse_name ASC
        ) AS rn
    FROM #ship_base AS sb
    WHERE sb.ship_dt >= @start_time
      AND sb.ship_dt < @end_time
      AND ISNULL(NULLIF(sb.ship_warehouse_name, ''), '') <> ''
    GROUP BY sb.sku_id, sb.ship_warehouse_name
)
SELECT
    sku_id,
    ship_warehouse_name AS top_warehouse_name
INTO #warehouse_top
FROM wh_rank
WHERE rn = 1;

IF OBJECT_ID('tempdb..#risk_eval') IS NOT NULL
    DROP TABLE #risk_eval;

SELECT
    sr.sku_id,
    sr.sku_name,
    sr.spu,
    sr.category,
    sr.item_type,
    sr.brand,
    sr.cost_price,
    sr.board_material,
    sr.sku_status,
    COALESCE(NULLIF(sr.supplier_name, ''), '未标注') AS factory_name,
    COALESCE(NULLIF(ow.purchaser_name, ''), '未分配') AS purchaser_name,
    COALESCE(NULLIF(ot.operator_name, ''), NULLIF(ow.spu_oper_name, ''), '未分配') AS operator_name,
    COALESCE(NULLIF(wt.top_warehouse_name, ''), NULLIF(sr.default_warehouse, ''), NULLIF(inv.inventory_note, ''), '未标注') AS warehouse_name,
    ct.top_shop_channel,
    stp.top_shop_name,
    ISNULL(ss.product_id_count_30d, 0) AS product_id_count_30d,
    ISNULL(ss.shop_count_30d, 0) AS shop_count_30d,
    ISNULL(ss.order_count_30d, 0) AS order_count_30d,
    ISNULL(ss.shipped_qty_7d, 0) AS shipped_qty_7d,
    ISNULL(ss.shipped_qty_30d, 0) AS shipped_qty_30d,
    ss.last_ship_time,
    inv.stock_qty,
    inv.order_lock_qty,
    inv.pick_lock_qty,
    inv.lock_qty,
    inv.available_qty,
    inv.virtual_qty,
    inv.defective_qty,
    inv.purchase_qty,
    inv.return_qty,
    inv.in_qty,
    inv.min_qty,
    inv.max_qty,
    inv.inventory_snapshot_time,
    CAST(ISNULL(ss.shipped_qty_7d, 0) / 7.0 AS decimal(18, 2)) AS avg_daily_shipped_7d,
    CAST(ISNULL(ss.shipped_qty_30d, 0) / 30.0 AS decimal(18, 2)) AS avg_daily_shipped_30d,
    CAST(
        CASE
            WHEN ISNULL(ss.shipped_qty_30d, 0) <= 0 THEN NULL
            ELSE inv.available_qty / NULLIF(ISNULL(ss.shipped_qty_30d, 0) / 30.0, 0)
        END AS decimal(18, 2)
    ) AS estimated_stockout_days,
    @lead_days AS replenishment_lead_days,
    CAST(
        CASE
            WHEN ISNULL(ss.shipped_qty_30d, 0) <= 0 THEN 0
            WHEN inv.available_qty >= (ISNULL(ss.shipped_qty_30d, 0) / 30.0) * @lead_days THEN 0
            ELSE ((ISNULL(ss.shipped_qty_30d, 0) / 30.0) * @lead_days) - inv.available_qty
        END AS decimal(18, 2)
    ) AS suggest_replenish_qty_to_lead_days,
    CASE
        WHEN inv.sku_id IS NULL THEN '无库存快照'
        WHEN ISNULL(ss.shipped_qty_30d, 0) <= 0 THEN '近30天无发货'
        WHEN inv.available_qty <= 0 THEN '缺货/可用库存<=0'
        WHEN (inv.available_qty / NULLIF(ISNULL(ss.shipped_qty_30d, 0) / 30.0, 0)) <= @lead_days THEN '补货紧急（<=10天）'
        WHEN (inv.available_qty / NULLIF(ISNULL(ss.shipped_qty_30d, 0) / 30.0, 0)) <= @warn_days THEN '补货预警（10-15天）'
        ELSE '库存安全'
    END AS stock_risk_level,
    CASE
        WHEN inv.sku_id IS NULL THEN 1
        WHEN ISNULL(ss.shipped_qty_30d, 0) <= 0 THEN 0
        WHEN inv.available_qty <= 0 THEN 1
        WHEN (inv.available_qty / NULLIF(ISNULL(ss.shipped_qty_30d, 0) / 30.0, 0)) <= @warn_days THEN 1
        ELSE 0
    END AS has_stock_risk
INTO #risk_eval
FROM #ship_stats AS ss
LEFT JOIN #sku_ref AS sr
    ON sr.sku_id = ss.sku_id
LEFT JOIN #inv_latest AS inv
    ON inv.sku_id = ss.sku_id
LEFT JOIN #sku_owner AS ow
    ON ow.sku_id = ss.sku_id
LEFT JOIN #operator_top AS ot
    ON ot.sku_id = ss.sku_id
LEFT JOIN #channel_top AS ct
    ON ct.sku_id = ss.sku_id
LEFT JOIN #shop_top AS stp
    ON stp.sku_id = ss.sku_id
LEFT JOIN #warehouse_top AS wt
    ON wt.sku_id = ss.sku_id;

-- 1) 汇总：近30天在售SKU + 缺货风险SKU数量
SELECT
    COUNT(*) AS sold_sku_count_30d,
    SUM(CASE WHEN has_stock_risk = 1 THEN 1 ELSE 0 END) AS stock_risk_sku_count,
    SUM(CASE WHEN stock_risk_level LIKE '补货紧急%' OR stock_risk_level LIKE '缺货%' THEN 1 ELSE 0 END) AS urgent_sku_count,
    SUM(CASE WHEN stock_risk_level LIKE '补货预警%' THEN 1 ELSE 0 END) AS warning_sku_count,
    @lookback_days AS lookback_days,
    @lead_days AS replenishment_lead_days,
    @warn_days AS warning_days
FROM #risk_eval;

-- 2) 明细：全量SKU风险清单（默认只显示风险项）
SELECT TOP (@limit)
    sku_id,
    sku_name,
    spu,
    category,
    item_type,
    brand,
    sku_status,
    cost_price,
    board_material,
    factory_name,
    purchaser_name,
    operator_name,
    warehouse_name,
    top_shop_channel,
    top_shop_name,
    product_id_count_30d,
    shop_count_30d,
    order_count_30d,
    shipped_qty_7d,
    shipped_qty_30d,
    avg_daily_shipped_7d,
    avg_daily_shipped_30d,
    stock_qty,
    order_lock_qty,
    pick_lock_qty,
    lock_qty,
    available_qty,
    estimated_stockout_days,
    replenishment_lead_days,
    suggest_replenish_qty_to_lead_days,
    stock_risk_level,
    inventory_snapshot_time,
    last_ship_time
FROM #risk_eval
WHERE @include_safe = 1
   OR has_stock_risk = 1
ORDER BY
    CASE
        WHEN stock_risk_level LIKE '缺货%' THEN 1
        WHEN stock_risk_level LIKE '补货紧急%' THEN 2
        WHEN stock_risk_level LIKE '补货预警%' THEN 3
        WHEN stock_risk_level = '无库存快照' THEN 4
        ELSE 9
    END,
    estimated_stockout_days ASC,
    shipped_qty_30d DESC,
    sku_id ASC;
"""


def main() -> int:
    parser = argparse.ArgumentParser(description="All-SKU stock risk alert report")
    parser.add_argument("--days", type=int, default=30, help="lookback window days, default 30")
    parser.add_argument("--lead-days", type=int, default=10, help="replenishment lead time days")
    parser.add_argument("--warn-days", type=int, default=15, help="warning line days (must >= lead-days)")
    parser.add_argument("--limit", type=int, default=500, help="max rows for detail result set")
    parser.add_argument("--include-safe", action="store_true", help="include safe SKUs in detail output")
    args = parser.parse_args()

    days = max(7, min(args.days, 180))
    lead_days = max(1, min(args.lead_days, 60))
    warn_days = max(lead_days, min(args.warn_days, 90))
    limit = max(1, min(args.limit, 5000))

    sql = build_sql(days, lead_days, warn_days, limit, args.include_safe)
    cmd = ["company-db", "query", "--sql", sql]
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        err = (proc.stderr or proc.stdout or "company-db failed").strip()
        print(err, file=sys.stderr)
        return proc.returncode

    print(proc.stdout.rstrip())
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
