from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from xml.sax.saxutils import escape
from zipfile import ZIP_DEFLATED, ZipFile


ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "runtime" / "e2e_accounts.json"
OUTPUT = ROOT / "runtime" / "e2e_accounts.xlsx"


def main() -> None:
    payload = json.loads(SOURCE.read_text(encoding="utf-8"))
    accounts = payload.get("accounts", [])
    orders = payload.get("dispatched_orders", [])

    sheets = [
        ("账号总览", account_rows(accounts)),
        ("子成员", member_rows(accounts)),
        ("丸掌柜绑定", binding_rows(accounts)),
        ("工作室授权", workshop_rows(accounts)),
        ("分单策略", strategy_rows(accounts)),
        ("派单订单", order_rows(orders)),
    ]
    write_xlsx(OUTPUT, sheets)
    print(f"已生成 Excel 台账：{OUTPUT}")


def account_rows(accounts: list[dict]) -> list[list[object]]:
    rows = [[
        "手机号",
        "密码",
        "账号类型",
        "企业名称",
        "联系人/成员名",
        "父账号",
        "成员角色",
        "成员状态",
        "企业认证",
        "丸友集状态",
        "丸掌柜状态",
        "丸掌柜绑定状态",
        "绑定丸掌柜账号",
        "最新授权密钥",
        "更新时间",
    ]]
    for item in accounts:
        rows.append([
            item.get("phone", ""),
            item.get("password", ""),
            item.get("account_type", ""),
            item.get("enterprise_name", ""),
            item.get("contact_name", ""),
            item.get("parent_phone", ""),
            item.get("member_role", ""),
            item.get("member_status", ""),
            item.get("certification_status", ""),
            item.get("wanyouji_status", ""),
            item.get("wanzhanggui_status", ""),
            item.get("wanzhanggui_binding_status", ""),
            item.get("bound_wanzhanggui_enterprise", ""),
            item.get("latest_auth_key", ""),
            item.get("updated_at", ""),
        ])
    return rows


def member_rows(accounts: list[dict]) -> list[list[object]]:
    rows = [[
        "子账号手机号",
        "密码",
        "角色",
        "状态",
        "成员名",
        "主账号",
        "主企业",
        "邀请链接",
        "更新时间",
    ]]
    for item in accounts:
        if item.get("account_type") != "sub":
            continue
        rows.append([
            item.get("phone", ""),
            item.get("password", ""),
            item.get("member_role", ""),
            item.get("member_status", ""),
            item.get("contact_name", ""),
            item.get("parent_phone", ""),
            item.get("parent_enterprise_name", ""),
            item.get("member_invite_url", ""),
            item.get("updated_at", ""),
        ])
    return rows


def binding_rows(accounts: list[dict]) -> list[list[object]]:
    rows = [["丸友集账号", "企业名称", "丸掌柜账号", "授权密钥", "状态"]]
    for item in accounts:
        for binding in item.get("wanzhanggui_bindings", []) or []:
            rows.append([
                item.get("phone", ""),
                item.get("enterprise_name", ""),
                binding.get("phone", ""),
                binding.get("auth_key", ""),
                binding.get("status", ""),
            ])
    return rows


def workshop_rows(accounts: list[dict]) -> list[list[object]]:
    rows = [["丸友集账号", "企业名称", "丸掌柜账号", "授权密钥", "授权状态"]]
    for item in accounts:
        for authorization in item.get("workshop_authorizations", []) or []:
            rows.append([
                item.get("phone", ""),
                item.get("enterprise_name", ""),
                authorization.get("wanzhanggui_phone", ""),
                authorization.get("auth_key", ""),
                authorization.get("status", ""),
            ])
    return rows


def strategy_rows(accounts: list[dict]) -> list[list[object]]:
    rows = [["丸友集账号", "企业名称", "丸掌柜账号", "策略名称", "策略类型", "状态", "创建时间"]]
    for item in accounts:
        for strategy in item.get("dispatch_strategies", []) or []:
            rows.append([
                item.get("phone", ""),
                item.get("enterprise_name", ""),
                strategy.get("wanzhanggui_phone", ""),
                strategy.get("strategy_name", ""),
                strategy.get("strategy_type", ""),
                strategy.get("status", ""),
                strategy.get("created_at", ""),
            ])
    return rows


def order_rows(orders: list[dict]) -> list[list[object]]:
    rows = [["丸掌柜账号", "来源单号", "订单标题", "策略名称", "策略类型", "丸友集账号", "状态", "创建时间", "更新时间"]]
    for item in orders:
        rows.append([
            item.get("wanzhanggui_phone", ""),
            item.get("source_order_no", ""),
            item.get("order_title", ""),
            item.get("strategy_name", ""),
            item.get("strategy_type", ""),
            ", ".join(item.get("wanyouji_phones", []) or []),
            item.get("status", ""),
            item.get("created_at", ""),
            item.get("updated_at", ""),
        ])
    return rows


def write_xlsx(path: Path, sheets: list[tuple[str, list[list[object]]]]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with ZipFile(path, "w", ZIP_DEFLATED) as archive:
        archive.writestr("[Content_Types].xml", content_types_xml(len(sheets)))
        archive.writestr("_rels/.rels", root_rels_xml())
        archive.writestr("xl/workbook.xml", workbook_xml(sheets))
        archive.writestr("xl/_rels/workbook.xml.rels", workbook_rels_xml(len(sheets)))
        archive.writestr("xl/styles.xml", styles_xml())
        archive.writestr("docProps/core.xml", core_xml())
        archive.writestr("docProps/app.xml", app_xml())
        for index, (_, rows) in enumerate(sheets, start=1):
            archive.writestr(f"xl/worksheets/sheet{index}.xml", worksheet_xml(rows))


def worksheet_xml(rows: list[list[object]]) -> str:
    body = []
    for row_index, row in enumerate(rows, start=1):
        cells = []
        for col_index, value in enumerate(row, start=1):
            ref = f"{column_name(col_index)}{row_index}"
            text = escape(str(value if value is not None else ""))
            style = ' s="1"' if row_index == 1 else ""
            cells.append(f'<c r="{ref}" t="inlineStr"{style}><is><t>{text}</t></is></c>')
        body.append(f'<row r="{row_index}">{"".join(cells)}</row>')
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" '
        'xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
        '<sheetViews><sheetView workbookViewId="0"/></sheetViews>'
        '<sheetFormatPr defaultRowHeight="18"/>'
        '<cols><col min="1" max="30" width="22" customWidth="1"/></cols>'
        f'<sheetData>{"".join(body)}</sheetData>'
        '<autoFilter ref="A1:Z1"/>'
        '</worksheet>'
    )


def column_name(index: int) -> str:
    name = ""
    while index:
        index, remainder = divmod(index - 1, 26)
        name = chr(65 + remainder) + name
    return name


def workbook_xml(sheets: list[tuple[str, list[list[object]]]]) -> str:
    sheet_xml = "".join(
        f'<sheet name="{escape(name)}" sheetId="{index}" r:id="rId{index}"/>'
        for index, (name, _) in enumerate(sheets, start=1)
    )
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" '
        'xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'
        f"<sheets>{sheet_xml}</sheets></workbook>"
    )


def workbook_rels_xml(count: int) -> str:
    rels = "".join(
        f'<Relationship Id="rId{index}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet{index}.xml"/>'
        for index in range(1, count + 1)
    )
    rels += f'<Relationship Id="rId{count + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
        f"{rels}</Relationships>"
    )


def content_types_xml(count: int) -> str:
    overrides = "".join(
        f'<Override PartName="/xl/worksheets/sheet{index}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>'
        for index in range(1, count + 1)
    )
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
        '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>'
        '<Default Extension="xml" ContentType="application/xml"/>'
        '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>'
        '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>'
        '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>'
        '<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>'
        f"{overrides}</Types>"
    )


def root_rels_xml() -> str:
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
        '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'
        '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>'
        '<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>'
        '</Relationships>'
    )


def styles_xml() -> str:
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
        '<fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font></fonts>'
        '<fills count="1"><fill><patternFill patternType="none"/></fill></fills>'
        '<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>'
        '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>'
        '<cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>'
        '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/></cellXfs>'
        '</styleSheet>'
    )


def core_xml() -> str:
    now = datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" '
        'xmlns:dc="http://purl.org/dc/elements/1.1/" '
        'xmlns:dcterms="http://purl.org/dc/terms/" '
        'xmlns:dcmitype="http://purl.org/dc/dcmitype/" '
        'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
        '<dc:creator>Codex</dc:creator>'
        f'<dcterms:created xsi:type="dcterms:W3CDTF">{now}</dcterms:created>'
        f'<dcterms:modified xsi:type="dcterms:W3CDTF">{now}</dcterms:modified>'
        '</cp:coreProperties>'
    )


def app_xml() -> str:
    return (
        '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
        '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" '
        'xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">'
        '<Application>Codex</Application></Properties>'
    )


if __name__ == "__main__":
    main()
