import base64
import hashlib
import json
from urllib.request import Request, urlopen

from odoo.fields import Command


VERSION = "V1.0-20260828"
PDF_URL = "https://img.kxunpt.cn/public/ai-marketing-platform/work/2026-08-28-odoo-customer-portal-launch/customer-solution-v1.pdf"
PDF_SHA256 = "794ea8548797cc9ac48d5dbcbbc5937c3ea509039b2f253e86e593f3d0a91a3b"
LOGO_URL = "https://img.kxunpt.cn/public/ai-marketing-platform/work/2026-08-28-odoo-customer-portal-launch/cpt-logo-orange.png"
LOGO_SHA256 = "58c87931a17ddf738b8ad020c7cba11ea0aa2a4bde6ed5a8501cd02ade89f912"
QUOTE_DOC_NAME = f"智慧食堂客户方案说明｜{VERSION}.pdf"
PRODUCT_DOC_NAME = f"智慧食堂客户方案说明｜{VERSION}"
PILOT_REF = f"PORTAL-PILOT-{VERSION}"
PILOT_CUSTOMER = f"客户门户验收｜{VERSION}"
BASE_URL = "https://odoo.cpt-fit.com"
PREVIOUS_URL_KEY = "cpt.customer_portal_launch.previous_web_base_url"
PREVIOUS_FREEZE_KEY = "cpt.customer_portal_launch.previous_web_base_url_freeze"
PREVIOUS_LOGO_KEY = "cpt.customer_portal_launch.previous_company_logo"
MISSING = "__MISSING__"
SENSITIVE_MARKERS = (
    "成本", "毛利", "供应商", "厂商", "采购价", "发票号", "发货单号", "源表行", "内部审批",
)
PUBLIC_PRODUCT_NAMES = {
    "CPT-N410": "410型自助称重结算台（单秤）",
    "CPT-BP001": "智能绑盘终端",
}


def download_asset(url, expected_sha256, expected_prefix, label):
    request = Request(url, headers={"User-Agent": "CPT-Odoo-Portal-Launch/1.0"})
    with urlopen(request, timeout=30) as response:
        payload = response.read()
    if hashlib.sha256(payload).hexdigest() != expected_sha256:
        raise RuntimeError(f"{label}校验和不一致")
    if not payload.startswith(expected_prefix):
        raise RuntimeError(f"{label}文件格式不正确")
    return payload


def public_line_name(template_line):
    if template_line.display_type:
        return (template_line.name or "方案小节").splitlines()[0]
    product_template = template_line.product_id.product_tmpl_id
    model_spec = (product_template.cpt_model_spec or "").strip()
    name = PUBLIC_PRODUCT_NAMES.get(model_spec, product_template.name.strip())
    if product_template.cpt_model_spec:
        name += f"\n型号/规格：{model_spec}"
    return name


def assert_clean(text, label):
    matched = [marker for marker in SENSITIVE_MARKERS if marker in (text or "")]
    if matched:
        raise RuntimeError(f"{label} 命中禁止外发词：{','.join(matched)}")


def main():
    ICP = env["ir.config_parameter"].sudo()
    old_url = ICP.get_param("web.base.url")
    old_freeze = ICP.get_param("web.base.url.freeze")
    already_applied = bool(
        env["quotation.document"].sudo().search_count([("name", "=", QUOTE_DOC_NAME)])
        or env["sale.order"].sudo().search_count([("client_order_ref", "=", PILOT_REF)])
    )
    if not ICP.search([("key", "=", PREVIOUS_URL_KEY)]):
        previous_url = "http://127.0.0.1:8069" if already_applied else (old_url or MISSING)
        ICP.set_param(PREVIOUS_URL_KEY, previous_url)
    if not ICP.search([("key", "=", PREVIOUS_FREEZE_KEY)]):
        previous_freeze = MISSING if already_applied else (old_freeze or MISSING)
        ICP.set_param(PREVIOUS_FREEZE_KEY, previous_freeze)
    ICP.set_param("web.base.url", BASE_URL)
    ICP.set_param("web.base.url.freeze", "True")

    pdf_bytes = download_asset(PDF_URL, PDF_SHA256, b"%PDF-", "客户版方案 PDF")
    logo_bytes = download_asset(LOGO_URL, LOGO_SHA256, b"\x89PNG\r\n\x1a\n", "康比特 Logo")
    company = env.company.sudo()
    if not ICP.search([("key", "=", PREVIOUS_LOGO_KEY)]):
        previous_logo = MISSING if already_applied else (company.logo.decode() if company.logo else MISSING)
        ICP.set_param(PREVIOUS_LOGO_KEY, previous_logo)
    company.write({"logo": base64.b64encode(logo_bytes)})
    QuotationDocument = env["quotation.document"].sudo()
    quote_doc = QuotationDocument.search([("name", "=", QUOTE_DOC_NAME)], limit=1)
    quote_doc_values = {
        "name": QUOTE_DOC_NAME,
        "datas": base64.b64encode(pdf_bytes),
        "mimetype": "application/pdf",
        "document_type": "header",
        "active": True,
        "add_by_default": True,
        "sequence": 5,
    }
    if quote_doc:
        quote_doc.write(quote_doc_values)
    else:
        quote_doc = QuotationDocument.create(quote_doc_values)

    Template = env["sale.order.template"].sudo()
    template_names = [
        "标准组合｜410智能称重餐线（数量待项目确认）",
        "标准组合｜AI识别结算餐线（数量待项目确认）",
    ]
    templates = Template.search([("name", "in", template_names)])
    if len(templates) != len(template_names):
        found = set(templates.mapped("name"))
        missing = [name for name in template_names if name not in found]
        raise RuntimeError("缺少报价模板：" + "、".join(missing))
    quote_doc.write({"quotation_template_ids": [Command.set(templates.ids)]})

    ProductDocument = env["product.document"].sudo()
    ProductTemplate = env["product.template"].sudo()
    core_products = ProductTemplate.search([("cpt_model_spec", "in", ["CPT-N410", "CPT-S20A"])])
    if not core_products:
        raise RuntimeError("未找到 CPT-N410 或 CPT-S20A 标准产品")
    product_doc_ids = []
    for product_template in core_products:
        document = ProductDocument.search([
            ("name", "=", PRODUCT_DOC_NAME),
            ("res_model", "=", "product.template"),
            ("res_id", "=", product_template.id),
        ], limit=1)
        values = {
            "name": PRODUCT_DOC_NAME,
            "type": "url",
            "url": PDF_URL,
            "mimetype": "application/pdf",
            "res_model": "product.template",
            "res_id": product_template.id,
            "attached_on_sale": "quotation",
            "active": True,
            "sequence": 1,
        }
        if document:
            document.write(values)
        else:
            document = ProductDocument.create(values)
        product_doc_ids.append(document.id)

    partner = env["res.partner"].sudo().search([("name", "=", PILOT_CUSTOMER)], limit=1)
    if not partner:
        partner = env["res.partner"].sudo().create({
            "name": PILOT_CUSTOMER,
            "company_type": "company",
            "lang": "zh_CN",
            "customer_rank": 1,
        })

    pilot_template = templates.filtered(lambda item: "410智能称重餐线" in item.name)
    Order = env["sale.order"].sudo()
    order = Order.search([("client_order_ref", "=", PILOT_REF)], limit=1)
    safe_note = (
        "<p>客户门户验收报价。数量和价格仅用于功能验收，"
        "正式报价需完成销售配置和商务复核后发送。</p>"
    )
    if not order:
        order_lines = []
        for template_line in pilot_template.sale_order_template_line_ids.sorted("sequence"):
            values = template_line._prepare_order_line_values()
            values["name"] = public_line_name(template_line)
            order_lines.append(Command.create(values))
        order = Order.create({
            "partner_id": partner.id,
            "client_order_ref": PILOT_REF,
            "sale_order_template_id": pilot_template.id,
            "order_line": order_lines,
            "note": safe_note,
            "require_signature": False,
            "require_payment": False,
            "quotation_document_ids": [Command.set([quote_doc.id])],
        })
    else:
        order.write({
            "partner_id": partner.id,
            "note": safe_note,
            "require_signature": False,
            "require_payment": False,
            "quotation_document_ids": [Command.set([quote_doc.id])],
        })
    for order_line in order.order_line.filtered(lambda line: line.product_id):
        order_line.name = public_line_name(
            pilot_template.sale_order_template_line_ids.filtered(
                lambda template_line: template_line.product_id == order_line.product_id
            )[:1]
        )

    assert_clean(order.note or "", "报价备注")
    for line in order.order_line:
        assert_clean(line.name or "", "报价行")
    if order.state not in ("draft", "sent"):
        raise RuntimeError("门户验收报价状态不是草稿/已发送")
    order._portal_ensure_token()
    if not order.access_token:
        raise RuntimeError("未生成客户门户令牌")
    if order.require_signature or order.require_payment:
        raise RuntimeError("门户验收报价不允许签署或付款")
    if quote_doc not in order.quotation_document_ids:
        raise RuntimeError("客户版方案 PDF 未关联到门户验收报价")
    if ICP.get_param("web.base.url") != BASE_URL or ICP.get_param("web.base.url.freeze") != "True":
        raise RuntimeError("外网基础域名配置未生效")

    env.cr.commit()
    print("APPLY_RESULT", json.dumps({
        "version": VERSION,
        "base_url": ICP.get_param("web.base.url"),
        "base_url_freeze": ICP.get_param("web.base.url.freeze"),
        "company_logo_sha256": hashlib.sha256(base64.b64decode(company.logo)).hexdigest(),
        "quotation_document_id": quote_doc.id,
        "product_document_ids": product_doc_ids,
        "template_names": sorted(templates.mapped("name")),
        "pilot_order_id": order.id,
        "pilot_order_name": order.name,
        "pilot_access_url": order.access_url,
        "pilot_line_count": len(order.order_line.filtered(lambda line: not line.display_type)),
        "signature": order.require_signature,
        "payment": order.require_payment,
    }, ensure_ascii=False, sort_keys=True))


try:
    main()
except Exception:
    env.cr.rollback()
    raise
