DROP PROCEDURE IF EXISTS sp_GetOperationsMonitor;

DELIMITER $$

CREATE PROCEDURE sp_GetOperationsMonitor()
BEGIN

    DROP TEMPORARY TABLE IF EXISTS tmp_scan_agg;
    DROP TEMPORARY TABLE IF EXISTS tmp_daily_scan;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_order;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_first_scan;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_stats;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_push_ranked;
    DROP TEMPORARY TABLE IF EXISTS tmp_order_full;
    DROP TEMPORARY TABLE IF EXISTS tmp_should_replace_dedup;
    DROP TEMPORARY TABLE IF EXISTS tmp_label_push;
    DROP TEMPORARY TABLE IF EXISTS tmp_daily_metrics;

    -- ================================================================
    -- 步骤1a：tmp_scan_agg - 每个运单的扫描摘要
    --         一次全量扫描 label_scan_history，消除后续所有重复扫描
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_scan_agg (
        NeutralWaybillNumber  VARCHAR(100) NOT NULL,
        MinScanTime           DATETIME     NULL,
        FirstSuccessTime_UTC5 DATETIME     NULL,
        FirstSuccessDate_UTC5 DATE         NULL,
        IsSuccess             TINYINT      NOT NULL DEFAULT 0,
        IsStopLabel           TINYINT      NOT NULL DEFAULT 0,
        PRIMARY KEY (NeutralWaybillNumber)
    );

    INSERT INTO tmp_scan_agg
    SELECT
        NeutralWaybillNumber,
        MIN(CreatedAt),
        MIN(CASE WHEN Result = 0 THEN CONVERT_TZ(CreatedAt, '+00:00', '-05:00') END),
        DATE(MIN(CASE WHEN Result = 0 THEN CONVERT_TZ(CreatedAt, '+00:00', '-05:00') END)),
        MAX(CASE WHEN Result = 0 THEN 1 ELSE 0 END),
        MAX(CASE WHEN Result = 0 AND Description LIKE '%成功返回STOP标签%' THEN 1 ELSE 0 END)
    FROM label_scan_history
    GROUP BY NeutralWaybillNumber;

    -- ================================================================
    -- 步骤1b：tmp_daily_scan - 每日扫描统计（扫描数/完成数/失败数/STOP数）
    --         与步骤1a共用同一次 label_scan_history 扫描
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_daily_scan (
        日期           DATE    NOT NULL,
        当日扫描数     INT     NOT NULL DEFAULT 0,
        当日换单完成数  INT    NOT NULL DEFAULT 0,
        当日换单失败数  INT    NOT NULL DEFAULT 0,
        当日STOP数     INT     NOT NULL DEFAULT 0,
        PRIMARY KEY (日期)
    );

    INSERT INTO tmp_daily_scan (日期, 当日扫描数, 当日换单完成数, 当日STOP数)
    SELECT
        DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00')),
        COUNT(*),
        COUNT(DISTINCT CASE WHEN Result = 0 THEN NeutralWaybillNumber END),
        COUNT(DISTINCT CASE WHEN Result = 0 AND Description LIKE '%成功返回STOP标签%' THEN NeutralWaybillNumber END)
    FROM label_scan_history
    GROUP BY DATE(CONVERT_TZ(CreatedAt, '+00:00', '-05:00'));

    UPDATE tmp_daily_scan ds
    INNER JOIN (
        SELECT
            DATE(CONVERT_TZ(h.CreatedAt, '+00:00', '-05:00')) AS 日期,
            COUNT(DISTINCT h.NeutralWaybillNumber)             AS 当日换单失败数
        FROM label_scan_history h
        INNER JOIN tmp_scan_agg sa ON h.NeutralWaybillNumber = sa.NeutralWaybillNumber
        WHERE h.Result != 0
          AND sa.IsSuccess = 0
        GROUP BY DATE(CONVERT_TZ(h.CreatedAt, '+00:00', '-05:00'))
    ) fail_data ON ds.日期 = fail_data.日期
    SET ds.当日换单失败数 = fail_data.当日换单失败数;

    -- ================================================================
    -- 步骤2：tmp_form_order - 关联交接单和换单请求
    --        先对 arrival_handover_forms 两次索引查找（HandoverNumber 有唯一索引）
    --        加上步骤1新建的 idx_master_package_number / idx_bill_of_lading_number 索引
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_form_order (
        FormId                  INT          NOT NULL,
        HandoverNumber          VARCHAR(100) NOT NULL,
        ReceiptTime             DATETIME     NULL,
        ReceiptDate             DATE         NULL,
        ReceiptHour             TINYINT      NULL,
        RequestId               INT          NOT NULL,
        NeutralWaybillNumber    VARCHAR(100) NOT NULL,
        LabelRetrievedAt_UTC5   DATETIME     NULL,
        LabelRetrievedDate_UTC5 DATE         NULL,
        RequestCreatedAt        DATETIME     NOT NULL,
        HasLabel                TINYINT      NOT NULL DEFAULT 0,
        PRIMARY KEY (RequestId),
        INDEX idx_form_id     (FormId),
        INDEX idx_waybill     (NeutralWaybillNumber),
        INDEX idx_receipt_date (ReceiptDate),
        INDEX idx_label_date  (LabelRetrievedDate_UTC5)
    );

    INSERT INTO tmp_form_order
    SELECT
        COALESCE(f_m.Id, f_b.Id)                                                 AS FormId,
        COALESCE(f_m.HandoverNumber, f_b.HandoverNumber)                         AS HandoverNumber,
        COALESCE(f_m.ReceiptTime, f_b.ReceiptTime)                               AS ReceiptTime,
        DATE(COALESCE(f_m.ReceiptTime, f_b.ReceiptTime))                         AS ReceiptDate,
        HOUR(COALESCE(f_m.ReceiptTime, f_b.ReceiptTime))                         AS ReceiptHour,
        r.Id                                                                     AS RequestId,
        r.NeutralWaybillNumber,
        CASE WHEN r.LabelRetrievedAt IS NOT NULL
             THEN CONVERT_TZ(r.LabelRetrievedAt, '+00:00', '-05:00') END         AS LabelRetrievedAt_UTC5,
        DATE(CONVERT_TZ(r.LabelRetrievedAt, '+00:00', '-05:00'))                 AS LabelRetrievedDate_UTC5,
        r.CreatedAt                                                              AS RequestCreatedAt,
        CASE WHEN r.Label IS NOT NULL AND r.Label != '' THEN 1 ELSE 0 END        AS HasLabel
    FROM label_replace_requests r
    LEFT JOIN arrival_handover_forms f_m ON r.MasterPackageNumber = f_m.HandoverNumber
    LEFT JOIN arrival_handover_forms f_b ON r.BillOfLadingNumber  = f_b.HandoverNumber
    WHERE COALESCE(f_m.Id, f_b.Id) IS NOT NULL;

    -- ================================================================
    -- 步骤3：tmp_form_first_scan - 每个交接单的首次扫描时间
    --        直接读取 tmp_scan_agg，无需再扫 label_scan_history
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_form_first_scan (
        FormId             INT      NOT NULL,
        FirstScanTime_UTC5 DATETIME NULL,
        PRIMARY KEY (FormId)
    );

    INSERT INTO tmp_form_first_scan
    SELECT
        fo.FormId,
        CONVERT_TZ(MIN(sa.MinScanTime), '+00:00', '-05:00')
    FROM tmp_form_order fo
    LEFT JOIN tmp_scan_agg sa ON fo.NeutralWaybillNumber = sa.NeutralWaybillNumber
    GROUP BY fo.FormId;

    -- ================================================================
    -- 步骤4：tmp_form_stats - 每个交接单的总数、达标阈值和首次达标时间
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_form_stats (
        FormId                  INT      NOT NULL,
        TotalOrderCount         INT      NOT NULL DEFAULT 0,
        QualifyNeedCount        INT      NOT NULL DEFAULT 0,
        FirstQualifiedTime_UTC5 DATETIME NULL,
        PRIMARY KEY (FormId)
    );

    INSERT INTO tmp_form_stats (FormId, TotalOrderCount, QualifyNeedCount)
    SELECT
        FormId,
        COUNT(DISTINCT RequestId),
        CEIL(COUNT(DISTINCT RequestId) * 0.8)
    FROM tmp_form_order
    GROUP BY FormId;

    CREATE TEMPORARY TABLE tmp_form_push_ranked (
        FormId                INT      NOT NULL,
        LabelRetrievedAt_UTC5 DATETIME NULL,
        PushOrder             INT      NOT NULL,
        QualifyNeedCount      INT      NOT NULL,
        INDEX idx_form_push (FormId, PushOrder)
    );

    INSERT INTO tmp_form_push_ranked
    SELECT
        fo.FormId,
        fo.LabelRetrievedAt_UTC5,
        ROW_NUMBER() OVER (PARTITION BY fo.FormId ORDER BY fo.LabelRetrievedAt_UTC5),
        fs.QualifyNeedCount
    FROM tmp_form_order fo
    INNER JOIN tmp_form_stats fs ON fo.FormId = fs.FormId
    WHERE fo.HasLabel = 1 AND fo.LabelRetrievedAt_UTC5 IS NOT NULL;

    UPDATE tmp_form_stats fs
    INNER JOIN (
        SELECT
            FormId,
            MIN(LabelRetrievedAt_UTC5) AS FirstQualifiedTime_UTC5
        FROM tmp_form_push_ranked
        WHERE PushOrder >= QualifyNeedCount
        GROUP BY FormId
    ) q ON fs.FormId = q.FormId
    SET fs.FirstQualifiedTime_UTC5 = q.FirstQualifiedTime_UTC5;

    DROP TEMPORARY TABLE IF EXISTS tmp_form_push_ranked;

    -- ================================================================
    -- 步骤5：tmp_order_full - 每个有标签订单的完整信息（含考核时间）
    --        预先计算 AssessmentBaseTime / AssessmentTime / AssessmentDate
    --        避免后续重复展开 GREATEST() CASE WHEN
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_order_full (
        RequestId               INT          NOT NULL,
        FormId                  INT          NOT NULL,
        NeutralWaybillNumber    VARCHAR(100) NOT NULL,
        HasLabel                TINYINT      NOT NULL DEFAULT 0,
        ReceiptDate             DATE         NULL,
        ReceiptTime             DATETIME     NULL,
        LabelRetrievedDate_UTC5 DATE         NULL,
        LabelRetrievedAt_UTC5   DATETIME     NULL,
        RequestCreatedDate_UTC5 DATE         NULL,
        AssessmentBaseTime      DATETIME     NULL,
        AssessmentBaseDate      DATE         NULL,
        AssessmentTime          DATETIME     NULL,
        AssessmentDate          DATE         NULL,
        FirstSuccessTime_UTC5   DATETIME     NULL,
        FirstSuccessDate_UTC5   DATE         NULL,
        IsSuccess               TINYINT      NOT NULL DEFAULT 0,
        IsStopLabel             TINYINT      NOT NULL DEFAULT 0,
        IsCompletedInAssessment TINYINT      NOT NULL DEFAULT 0,
        PRIMARY KEY (RequestId),
        INDEX idx_receipt_date       (ReceiptDate),
        INDEX idx_label_date         (LabelRetrievedDate_UTC5),
        INDEX idx_success_date       (FirstSuccessDate_UTC5),
        INDEX idx_assessment_base    (AssessmentBaseDate),
        INDEX idx_assessment_date    (AssessmentDate),
        INDEX idx_created_date       (RequestCreatedDate_UTC5),
        INDEX idx_success_label_date (IsSuccess, LabelRetrievedDate_UTC5, FirstSuccessDate_UTC5)
    );

    INSERT INTO tmp_order_full
    SELECT
        fo.RequestId,
        fo.FormId,
        fo.NeutralWaybillNumber,
        fo.HasLabel,
        fo.ReceiptDate,
        fo.ReceiptTime,
        fo.LabelRetrievedDate_UTC5,
        fo.LabelRetrievedAt_UTC5,
        DATE(CONVERT_TZ(fo.RequestCreatedAt, '+00:00', '-05:00'))                AS RequestCreatedDate_UTC5,
        GREATEST(
            CASE
                WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                     AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                THEN ffs.FirstScanTime_UTC5
                ELSE fo.ReceiptTime
            END,
            fs.FirstQualifiedTime_UTC5
        )                                                                        AS AssessmentBaseTime,
        DATE(GREATEST(
            CASE
                WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                     AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                THEN ffs.FirstScanTime_UTC5
                ELSE fo.ReceiptTime
            END,
            fs.FirstQualifiedTime_UTC5
        ))                                                                       AS AssessmentBaseDate,
        CASE
            WHEN fs.FirstQualifiedTime_UTC5 IS NOT NULL AND fo.HasLabel = 1
            THEN CASE
                WHEN HOUR(GREATEST(
                    CASE
                        WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                             AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                        THEN ffs.FirstScanTime_UTC5
                        ELSE fo.ReceiptTime
                    END,
                    fs.FirstQualifiedTime_UTC5
                )) < 16
                THEN DATE_ADD(
                         DATE_ADD(DATE(GREATEST(
                             CASE
                                 WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                                      AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                                 THEN ffs.FirstScanTime_UTC5
                                 ELSE fo.ReceiptTime
                             END,
                             fs.FirstQualifiedTime_UTC5
                         )), INTERVAL 1 DAY),
                         INTERVAL 16 HOUR
                     )
                ELSE DATE_ADD(
                         DATE_ADD(DATE(GREATEST(
                             CASE
                                 WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                                      AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                                 THEN ffs.FirstScanTime_UTC5
                                 ELSE fo.ReceiptTime
                             END,
                             fs.FirstQualifiedTime_UTC5
                         )), INTERVAL 1 DAY),
                         INTERVAL '23:59:59' HOUR_SECOND
                     )
            END
            ELSE NULL
        END                                                                      AS AssessmentTime,
        DATE(CASE
            WHEN fs.FirstQualifiedTime_UTC5 IS NOT NULL AND fo.HasLabel = 1
            THEN DATE_ADD(DATE(GREATEST(
                CASE
                    WHEN ffs.FirstScanTime_UTC5 IS NOT NULL
                         AND ffs.FirstScanTime_UTC5 < fo.ReceiptTime
                    THEN ffs.FirstScanTime_UTC5
                    ELSE fo.ReceiptTime
                END,
                fs.FirstQualifiedTime_UTC5
            )), INTERVAL 1 DAY)
            ELSE NULL
        END)                                                                     AS AssessmentDate,
        sa.FirstSuccessTime_UTC5,
        sa.FirstSuccessDate_UTC5,
        COALESCE(sa.IsSuccess, 0)                                                AS IsSuccess,
        COALESCE(sa.IsStopLabel, 0)                                              AS IsStopLabel,
        0                                                                        AS IsCompletedInAssessment
    FROM tmp_form_order fo
    INNER JOIN tmp_form_stats fs       ON fo.FormId             = fs.FormId
    LEFT JOIN  tmp_form_first_scan ffs ON fo.FormId             = ffs.FormId
    LEFT JOIN  tmp_scan_agg sa         ON fo.NeutralWaybillNumber = sa.NeutralWaybillNumber
    WHERE fo.HasLabel = 1;

    UPDATE tmp_order_full
    SET IsCompletedInAssessment = 1
    WHERE AssessmentTime IS NOT NULL
      AND IsSuccess = 1
      AND FirstSuccessTime_UTC5 <= AssessmentTime;

    DROP TEMPORARY TABLE IF EXISTS tmp_form_first_scan;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_stats;
    DROP TEMPORARY TABLE IF EXISTS tmp_form_order;
    DROP TEMPORARY TABLE IF EXISTS tmp_scan_agg;

    -- ================================================================
    -- 步骤6：构建 tmp_daily_metrics 的日期维度
    --        从 tmp_order_full 和 tmp_daily_scan 收集所有出现过的日期
    -- ================================================================
    CREATE TEMPORARY TABLE tmp_daily_metrics (
        日期             DATE NOT NULL,
        当天新增换单数   INT  NOT NULL DEFAULT 0,
        当天应该换单数   INT  NOT NULL DEFAULT 0,
        当日标签推送数   INT  NOT NULL DEFAULT 0,
        累计要换的总单数 INT  NOT NULL DEFAULT 0,
        `24H完成数`      INT  NOT NULL DEFAULT 0,
        PRIMARY KEY (日期)
    );

    INSERT IGNORE INTO tmp_daily_metrics (日期)
    SELECT DISTINCT ReceiptDate             FROM tmp_order_full WHERE ReceiptDate IS NOT NULL
    UNION
    SELECT DISTINCT LabelRetrievedDate_UTC5 FROM tmp_order_full WHERE LabelRetrievedDate_UTC5 IS NOT NULL
    UNION
    SELECT DISTINCT FirstSuccessDate_UTC5   FROM tmp_order_full WHERE FirstSuccessDate_UTC5 IS NOT NULL
    UNION
    SELECT DISTINCT AssessmentBaseDate      FROM tmp_order_full WHERE AssessmentBaseDate IS NOT NULL
    UNION
    SELECT DISTINCT AssessmentDate          FROM tmp_order_full WHERE AssessmentDate IS NOT NULL
    UNION
    SELECT DISTINCT 日期                    FROM tmp_daily_scan;

    -- ================================================================
    -- 步骤7：按日期聚合各项指标（无 CROSS JOIN，均为有索引的 GROUP BY）
    -- ================================================================

    -- 当天新增换单数
    UPDATE tmp_daily_metrics dm
    INNER JOIN (
        SELECT ReceiptDate AS 日期, COUNT(DISTINCT RequestId) AS cnt
        FROM tmp_order_full
        WHERE ReceiptDate IS NOT NULL
        GROUP BY ReceiptDate
    ) t ON dm.日期 = t.日期
    SET dm.当天新增换单数 = t.cnt;

    -- 当日标签推送数
    UPDATE tmp_daily_metrics dm
    INNER JOIN (
        SELECT LabelRetrievedDate_UTC5 AS 日期, COUNT(DISTINCT RequestId) AS cnt
        FROM tmp_order_full
        WHERE LabelRetrievedDate_UTC5 IS NOT NULL
        GROUP BY LabelRetrievedDate_UTC5
    ) t ON dm.日期 = t.日期
    SET dm.当日标签推送数 = t.cnt;

    -- 当天应该换单数 + 24H完成数
    -- 一个订单的 AssessmentBaseDate 和 AssessmentDate 可能不同（BaseDate=T，AssessmentDate=T+1）
    -- 该订单在 T 和 T+1 两天都应被计入"当天应该换单数"
    -- 使用 UNION 展开两个日期维度后去重统计
    CREATE TEMPORARY TABLE tmp_should_replace_dedup (
        日期             DATE NOT NULL,
        当天应该换单数   INT  NOT NULL DEFAULT 0,
        `24H完成数`      INT  NOT NULL DEFAULT 0,
        PRIMARY KEY (日期)
    );

    INSERT INTO tmp_should_replace_dedup
    SELECT
        d.日期,
        COUNT(DISTINCT ofu.RequestId)                                                   AS 当天应该换单数,
        COUNT(DISTINCT CASE
            WHEN ofu.IsCompletedInAssessment = 1
                 AND ofu.FirstSuccessDate_UTC5 = d.日期
            THEN ofu.RequestId END)                                                     AS `24H完成数`
    FROM (
        SELECT DISTINCT AssessmentBaseDate AS 日期 FROM tmp_order_full WHERE AssessmentBaseDate IS NOT NULL
        UNION
        SELECT DISTINCT AssessmentDate     AS 日期 FROM tmp_order_full WHERE AssessmentDate IS NOT NULL
    ) d
    INNER JOIN tmp_order_full ofu
        ON ofu.AssessmentTime IS NOT NULL
        AND (ofu.AssessmentBaseDate = d.日期 OR ofu.AssessmentDate = d.日期)
        AND (ofu.IsSuccess = 0 OR ofu.FirstSuccessDate_UTC5 <= d.日期)
    GROUP BY d.日期;

    UPDATE tmp_daily_metrics dm
    INNER JOIN tmp_should_replace_dedup srd ON dm.日期 = srd.日期
    SET
        dm.当天应该换单数 = srd.当天应该换单数,
        dm.`24H完成数`    = srd.`24H完成数`;

    DROP TEMPORARY TABLE IF EXISTS tmp_should_replace_dedup;

    -- 累计要换的总单数
    -- 每个订单的计入区间：[MAX(LabelRetrievedDate_UTC5, RequestCreatedDate_UTC5), FirstSuccessDate_UTC5-1 或无穷]
    -- 对每个日期 d：count 满足 start_date <= d AND (无成功 OR success_date > d) 的订单数
    -- 利用 tmp_daily_metrics × tmp_order_full JOIN，但 tmp_order_full 已建索引，
    -- 数据量：日期数 × 订单数远小于原来的 CROSS JOIN（OrderFullInfo 是完整订单，现在只取有标签的）
    UPDATE tmp_daily_metrics dm
    INNER JOIN (
        SELECT
            dm2.日期,
            COUNT(DISTINCT ofu.RequestId) AS cnt
        FROM tmp_daily_metrics dm2
        INNER JOIN tmp_order_full ofu
            ON ofu.HasLabel = 1
            AND ofu.LabelRetrievedDate_UTC5 <= dm2.日期
            AND ofu.RequestCreatedDate_UTC5  <= dm2.日期
            AND (ofu.IsSuccess = 0 OR ofu.FirstSuccessDate_UTC5 > dm2.日期)
        GROUP BY dm2.日期
    ) t ON dm.日期 = t.日期
    SET dm.累计要换的总单数 = t.cnt;

    -- ================================================================
    -- 最终输出
    -- ================================================================
    SELECT
        dm.日期,
        dm.当天新增换单数,
        dm.累计要换的总单数,
        dm.当天应该换单数,
        COALESCE(ds.当日换单完成数, 0)                                                  AS 当日换单完成数,
        COALESCE(ds.当日换单失败数, 0)                                                  AS 当日换单失败数,
        COALESCE(ds.当日STOP数, 0)                                                      AS 当日STOP数,
        dm.`24H完成数`                                                                  AS 24小时换单成功数,
        dm.当日标签推送数,
        COALESCE(ds.当日扫描数, 0)                                                      AS 当日扫描数,
        CASE
            WHEN dm.当天应该换单数 = 0 THEN '0.00%'
            ELSE CONCAT(ROUND(COALESCE(ds.当日换单完成数, 0) / dm.当天应该换单数 * 100, 2), '%')
        END                                                                             AS 当天换单完成率,
        CASE
            WHEN dm.当天应该换单数 = 0 THEN '0.00%'
            ELSE CONCAT(ROUND(dm.`24H完成数` / dm.当天应该换单数 * 100, 2), '%')
        END                                                                             AS 24小时换单率,
        (UTC_TIMESTAMP() - INTERVAL 5 HOUR)                                             AS 数据拉取时间（UTC_5）
    FROM tmp_daily_metrics dm
    LEFT JOIN tmp_daily_scan ds ON dm.日期 = ds.日期
    ORDER BY dm.日期 DESC;

    -- ================================================================
    -- 清理临时表
    -- ================================================================
    DROP TEMPORARY TABLE IF EXISTS tmp_daily_scan;
    DROP TEMPORARY TABLE IF EXISTS tmp_order_full;
    DROP TEMPORARY TABLE IF EXISTS tmp_daily_metrics;

END$$

DELIMITER ;
