# -*- coding: utf-8 -*-
"""中台：销售看板（首页图标可见范围 + 上传 xls 重生成 kb.html + 同步金额变化触发未读）。"""
import os
import re
import sys
import json
import datetime
import subprocess

from flask import render_template, request, redirect, url_for, flash, current_app
from flask_login import login_required

from app.models import HomeFeature, HomeFeatureParticipant, Chain, Staff, Store
from app.extensions import db
from app.admin import bp
from app.admin.views import _admin_only
from app.services import log_action, get_setting, set_setting
from app.region_data import CHINA_REGION

_FEATURE_KEY = "salesboard"
_XLS_NAME = "2026新营销发货数据明细表.xls"   # 脚本按 mtime 找最新 *新营销发货数据明细表*.xls*


def _parse_kb_total():
    """从 xyx 目录 kb.html 解析 D_CHAN_PIE 求和（万元）。目录未配/文件缺失/格式不符→None。"""
    xyx_dir = current_app.config.get("XYX_DIR")
    if not xyx_dir:
        return None
    kb_path = os.path.join(xyx_dir, "kb.html")
    if not os.path.isfile(kb_path):
        return None
    try:
        with open(kb_path, "r", encoding="utf-8") as f:
            content = f.read()
        m = re.search(r"const D_CHAN_PIE=\[([\d.,\s]+)\]", content)
        if not m:
            return None
        return round(sum(float(x) for x in m.group(1).split(",") if x.strip()), 1)
    except Exception:
        return None


def _refresh_salesboard_total():
    """解析 kb.html 求和，写入 Setting；金额变化（或首次）则更新 changed_at（触发店员未读）。"""
    total = _parse_kb_total()
    if total is None:
        return False
    old = get_setting("salesboard.total")
    try:
        old_f = float(old) if old not in (None, "") else None
    except (ValueError, TypeError):
        old_f = None
    set_setting("salesboard.total", total)
    if old_f is None or abs(old_f - total) > 0.05:
        set_setting("salesboard.changed_at", datetime.datetime.now().isoformat(timespec="seconds"))
    return True


@bp.route("/salesboard", methods=["GET", "POST"])
@login_required
def salesboard():
    """销售看板：可见范围配置（抄考试范围模式）+ 当前金额 + 上传/同步入口。"""
    _admin_only()
    f = HomeFeature.query.filter_by(feature_key=_FEATURE_KEY).first()
    if not f:
        f = HomeFeature(feature_key=_FEATURE_KEY, name="销售看板", scope_mode="all")
        db.session.add(f)
        db.session.commit()
    if request.method == "POST":
        f.scope_mode = request.form.get("scope_mode", "all")
        f.allowed_chains = (json.dumps([int(c) for c in request.form.getlist("chains")])
                            if f.scope_mode == "chain" else None)
        HomeFeatureParticipant.query.filter_by(feature_id=f.id).delete()
        if f.scope_mode == "staff":
            for sid in request.form.getlist("staff_ids"):
                db.session.add(HomeFeatureParticipant(feature_id=f.id, staff_id=int(sid)))
        f.updated_at = datetime.datetime.now()
        db.session.commit()
        log_action("salesboard_scope", target_type="home_feature", target_id=f.id,
                   summary="销售看板可见范围：%s" % f.scope_mode)
        flash("已保存可见范围", "success")
        return redirect(url_for("admin.salesboard"))
    chains = Chain.query.order_by(Chain.name).all()
    selected_chains = set(json.loads(f.allowed_chains) if f.allowed_chains else [])
    selected_staff = set(p.staff_id for p in
                         HomeFeatureParticipant.query.filter_by(feature_id=f.id).all())
    staff_list = Staff.query.filter(
        Staff.role == "staff", Staff.deleted.is_(False), Staff.status == "active"
    ).order_by(Staff.id.desc()).limit(500).all()
    chain_counts = {}
    for c in chains:
        chain_counts[c.id] = Staff.query.join(Staff.store).filter(
            Staff.role == "staff", Staff.deleted.is_(False), Staff.status == "active",
            Store.chain_id == c.id).count()
    return render_template("admin/salesboard.html", f=f, chains=chains,
                           selected_chains=selected_chains, staff_list=staff_list,
                           selected_staff=selected_staff, chain_counts=chain_counts,
                           china_region=CHINA_REGION,
                           total=get_setting("salesboard.total"),
                           changed_at=get_setting("salesboard.changed_at"),
                           xyx_configured=bool(current_app.config.get("XYX_DIR")),
                           active="salesboard")


@bp.route("/salesboard/upload-xls", methods=["POST"])
@login_required
def salesboard_upload_xls():
    """上传新发货明细表 xls → 覆盖到 xyx 目录 → 跑 xyx_script.py 重生成 kb.html → 同步金额。"""
    _admin_only()
    xyx_dir = current_app.config.get("XYX_DIR")
    if not xyx_dir or not os.path.isdir(xyx_dir):
        flash("未配置 xyx 看板目录（XYX_DIR），无法上传", "danger")
        return redirect(url_for("admin.salesboard"))
    f = request.files.get("xls")
    if not f or not f.filename:
        flash("请选择 xls 文件", "danger")
        return redirect(url_for("admin.salesboard"))
    ext = f.filename.rsplit(".", 1)[-1].lower() if "." in f.filename else ""
    if ext not in ("xls", "xlsx"):
        flash("仅支持 .xls / .xlsx", "danger")
        return redirect(url_for("admin.salesboard"))
    f.save(os.path.join(xyx_dir, _XLS_NAME))   # 覆盖固定名
    script = os.path.join(xyx_dir, "xyx_script.py")
    try:
        res = subprocess.run([sys.executable, script], cwd=xyx_dir, check=False,
                             capture_output=True, timeout=180)
    except subprocess.TimeoutExpired:
        flash("脚本执行超时", "danger")
        return redirect(url_for("admin.salesboard"))
    except Exception as e:
        flash("脚本执行失败：%s" % e, "danger")
        return redirect(url_for("admin.salesboard"))
    if res.returncode != 0:
        flash("脚本执行出错：%s" % res.stderr.decode("utf-8", "ignore")[-400:], "danger")
        return redirect(url_for("admin.salesboard"))
    _refresh_salesboard_total()
    log_action("salesboard_upload_xls", target_type="home_feature",
               summary="上传发货明细表，重生成销售看板")
    flash("已上传并重生成看板", "success")
    return redirect(url_for("admin.salesboard"))

