"""Install order_line hosts on record.* contracts.

Child rows are related data.entity records. Parent id is bound presentation-side
via listRecords ?filter=. Header amount stays an explicit parent field.
checkout_app_block stays a coverage gap; these hosts are not checkout.
"""
from __future__ import annotations

import json
from pathlib import Path
import sys

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))

from src.domain.app_blocks.hosts import merge_host_map
from tests.test_phase11_pack_migration_materialization import _load_migration

MIGRATION = ROOT / "alembic/versions/159_order_line_child_records.py"


def write(path: Path, text: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(text, encoding="utf-8")


def _entity(composition: dict, key: str) -> dict:
    return next(item for item in composition["entities"] if item["entity_key"] == key)


def _schema_json(entity: dict) -> str:
    return json.dumps({"fields": entity["schema"]["fields"]}, indent=2) + "\n"


def nuxt_table(entity_key: str, parent_field: str, columns: str, empty: str) -> str:
    return f'''<script setup>
import AdminRecordTable from "./AdminRecordTable.vue";
const props = defineProps({{
  emptyTitle: {{ type: String, default: "{empty}" }},
  parentRecordId: {{ type: String, default: "" }},
  recordId: {{ type: String, default: "" }},
}});
const emit = defineEmits(["select"]);
const parentId = computed(() => props.parentRecordId || props.recordId);
const search = computed(() => {{
  const id = parentId.value;
  if (!id) return "?limit=50";
  return `?limit=50&filter=${{encodeURIComponent(JSON.stringify({{ {parent_field}: id }}))}}`;
}});
</script>
<template><AdminRecordTable entity-key="{entity_key}" :search="search" :empty-title="emptyTitle"
  :columns="{columns}"
  @select="emit('select', $event)" /></template>
'''


def nuxt_create(entity_key: str, parent_field: str, schema_import: str, heading: str) -> str:
    return f'''<script setup>
import schema from "~/lib/entity-schemas/{schema_import}";
import RecordCreate from "../RecordCreate.vue";
const props = defineProps({{
  parentRecordId: {{ type: String, default: "" }},
  recordId: {{ type: String, default: "" }},
}});
const emit = defineEmits(["created"]);
const defaults = computed(() => {{
  const id = props.parentRecordId || props.recordId;
  return id ? {{ {parent_field}: id }} : {{}};
}});
</script>
<template><RecordCreate entity-key="{entity_key}" :schema="schema" :defaults="defaults"
 heading="{heading}" @created="emit('created', $event)" /></template>
'''


def nuxt_detail(entity_key: str, title: str, fields_json: str) -> str:
    return f'''<script setup>
import AdminRecordDetail from "./AdminRecordDetail.vue";
defineProps({{ recordId: {{ type: String, required: true }} }});
const fields = {fields_json};
</script>
<template><AdminRecordDetail entity-key="{entity_key}" :record-id="recordId" title="{title}" :fields="fields" /></template>
'''


def labels(entity: dict) -> list[dict]:
    return [{"key": field["key"], "label": field["name"]} for field in entity["schema"]["fields"]]


def main() -> None:
    module = _load_migration(str(MIGRATION))
    composition = module.order_lifecycle_v1_1()
    order_line = _entity(composition, "order_line")

    for framework, lib in (("nuxt_app", "lib"), ("react_app", "src/lib"), ("html", "js")):
        write(
            ROOT / "boilerplates" / framework / lib / "entity-schemas" / "order_line.json",
            _schema_json(order_line),
        )

    nuxt = ROOT / "boilerplates/nuxt_app/components/starter/blocks"
    write(
        nuxt / "OrderLineTable.vue",
        nuxt_table(
            "order_line",
            "order_record_id",
            "[{key:'description',label:'Description'},{key:'quantity',label:'Qty'},{key:'unit_price',label:'Unit price'}]",
            "No order lines yet",
        ),
    )
    write(
        nuxt / "OrderLineCreate.vue",
        nuxt_create("order_line", "order_record_id", "order_line.json", "Create order line"),
    )
    write(
        nuxt / "OrderLineDetail.vue",
        nuxt_detail("order_line", "Order line", json.dumps(labels(order_line))),
    )

    react_path = ROOT / "boilerplates/react_app/src/components/starter/order-blocks.jsx"
    react_src = react_path.read_text(encoding="utf-8")
    if "OrderLineTable" not in react_src:
        if "orderLineSchema" not in react_src:
            react_src = react_src.replace(
                'import orderSchema from "../../lib/entity-schemas/order_record.json";',
                'import orderSchema from "../../lib/entity-schemas/order_record.json";\nimport orderLineSchema from "../../lib/entity-schemas/order_line.json";',
            )
        react_src = react_src.rstrip() + '''
export function OrderLineTable(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${encodeURIComponent(JSON.stringify({ order_record_id: parentId }))}` : "?limit=50";
 return <AdminRecordTable {...props} entityKey="order_line" search={search} columns={[{key:"description",label:"Description"},{key:"quantity",label:"Qty"},{key:"unit_price",label:"Unit price"}]} emptyTitle={props.emptyTitle || "No order lines yet"} />;
}
export function OrderLineDetail(props) {
 return <AdminRecordDetail {...props} entityKey="order_line" fields={orderLineSchema.fields.map(f=>({key:f.key,label:f.name}))} />;
}
export function OrderLineCreate(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? { order_record_id: parentId } : {};
 return <RecordCreate {...props} entityKey="order_line" schema={orderLineSchema} defaults={defaults} heading="Create order line" />;
}
'''
        write(react_path, react_src + "\n")

    html_path = ROOT / "boilerplates/html/js/order-blocks.js"
    html_src = html_path.read_text(encoding="utf-8")
    if "mountOrderLineTable" not in html_src:
        html_src += f'''
const orderLineSchema = {json.dumps({"fields": order_line["schema"]["fields"]})};
export function mountOrderLineTable(root, props={{}}) {{
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${{encodeURIComponent(JSON.stringify({{ order_record_id: parentId }}))}}` : "?limit=50";
 return mountAdminRecordTable(root,{{...props,entityKey:"order_line",search,columns:[{{key:"description",label:"Description"}},{{key:"quantity",label:"Qty"}},{{key:"unit_price",label:"Unit price"}}],emptyTitle:props.emptyTitle||"No order lines yet"}});
}}
export function mountOrderLineDetail(root, props={{}}) {{
 return mountAdminRecordDetail(root,{{...props,entityKey:"order_line",fields:orderLineSchema.fields.map(f=>({{key:f.key,label:f.name}}))}});
}}
export function mountOrderLineCreate(root, props={{}}) {{
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? {{ order_record_id: parentId }} : {{}};
 return mountRecordCreate(root,{{...props,entityKey:"order_line",schema:orderLineSchema,defaults,heading:"Create order line"}});
}}
'''
        write(html_path, html_src)

    impls = {
        "orders.order": {
            "lines": {
                "nuxt_app": "components/starter/blocks/OrderLineTable.vue",
                "react_app": "src/components/starter/order-blocks.jsx#OrderLineTable",
                "html": "js/order-blocks.js#mountOrderLineTable",
            },
            "line_create": {
                "nuxt_app": "components/starter/blocks/OrderLineCreate.vue",
                "react_app": "src/components/starter/order-blocks.jsx#OrderLineCreate",
                "html": "js/order-blocks.js#mountOrderLineCreate",
            },
            "line_detail": {
                "nuxt_app": "components/starter/blocks/OrderLineDetail.vue",
                "react_app": "src/components/starter/order-blocks.jsx#OrderLineDetail",
                "html": "js/order-blocks.js#mountOrderLineDetail",
            },
        },
    }

    for framework in ("nuxt_app", "react_app", "html"):
        path = ROOT / "boilerplates" / framework / "p2hs/app-block-hosts.json"
        existing = json.loads(path.read_text(encoding="utf-8"))
        hosts = merge_host_map(existing)
        for host in hosts["hosts"]:
            block_impls = impls.get(host["block"])
            if not block_impls or host["slot"] not in block_impls:
                continue
            host["implementation"] = block_impls[host["slot"]][framework]
        hosts["hosts"] = [host for host in hosts["hosts"] if host.get("implementation")]
        write(path, json.dumps(hosts, indent=2) + "\n")
    print("wave3 order_line hosts materialized")


if __name__ == "__main__":
    main()
