"""Install quote_line, invoice_line, and catalog_variant hosts on record.* contracts.

Child rows are related data.entity records. Parent id is bound presentation-side
via listRecords ?filter=. Header amount/price stay explicit parent fields.
"""
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/158_quote_invoice_catalog_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))
    quote = module.quote_lifecycle_v1_1()
    invoice = module.billing_management_v1_1()
    catalog = module.product_catalog_v1_1()
    quote_line = _entity(quote, "quote_line")
    invoice_line = _entity(invoice, "invoice_line")
    catalog_item = _entity(catalog, "catalog_item")
    catalog_variant = _entity(catalog, "catalog_variant")

    schemas = {
        "quote_line": quote_line,
        "invoice_line": invoice_line,
        "catalog_item": catalog_item,
        "catalog_variant": catalog_variant,
    }
    for framework, lib in (("nuxt_app", "lib"), ("react_app", "src/lib"), ("html", "js")):
        for key, entity in schemas.items():
            write(ROOT / "boilerplates" / framework / lib / "entity-schemas" / f"{key}.json", _schema_json(entity))

    nuxt = ROOT / "boilerplates/nuxt_app/components/starter/blocks"
    write(
        nuxt / "QuoteLineTable.vue",
        nuxt_table(
            "quote_line",
            "quote_record_id",
            "[{key:'description',label:'Description'},{key:'quantity',label:'Qty'},{key:'unit_price',label:'Unit price'}]",
            "No quote lines yet",
        ),
    )
    write(nuxt / "QuoteLineCreate.vue", nuxt_create("quote_line", "quote_record_id", "quote_line.json", "Create quote line"))
    write(nuxt / "QuoteLineDetail.vue", nuxt_detail("quote_line", "Quote line", json.dumps(labels(quote_line))))
    write(
        nuxt / "InvoiceLineTable.vue",
        nuxt_table(
            "invoice_line",
            "invoice_record_id",
            "[{key:'description',label:'Description'},{key:'quantity',label:'Qty'},{key:'unit_price',label:'Unit price'}]",
            "No invoice lines yet",
        ),
    )
    write(nuxt / "InvoiceLineCreate.vue", nuxt_create("invoice_line", "invoice_record_id", "invoice_line.json", "Create invoice line"))
    write(nuxt / "InvoiceLineDetail.vue", nuxt_detail("invoice_line", "Invoice line", json.dumps(labels(invoice_line))))
    write(
        nuxt / "CatalogVariantTable.vue",
        nuxt_table(
            "catalog_variant",
            "catalog_item_record_id",
            "[{key:'sku',label:'SKU'},{key:'name',label:'Name'},{key:'status',label:'Status'}]",
            "No catalog variants yet",
        ),
    )
    write(
        nuxt / "CatalogVariantCreate.vue",
        nuxt_create("catalog_variant", "catalog_item_record_id", "catalog_variant.json", "Create catalog variant"),
    )
    write(
        nuxt / "CatalogVariantDetail.vue",
        nuxt_detail("catalog_variant", "Catalog variant", json.dumps(labels(catalog_variant))),
    )
    write(
        nuxt / "CatalogTable.vue",
        '''<script setup>
import AdminRecordTable from "./AdminRecordTable.vue";
defineProps({ emptyTitle: { type: String, default: "No catalog items yet" } });
const emit = defineEmits(["select"]);
</script>
<template><AdminRecordTable entity-key="catalog_item" :empty-title="emptyTitle"
  :columns="[{key:'name',label:'Name'},{key:'sku',label:'SKU'},{key:'category',label:'Category'},{key:'status',label:'Status'}]"
  @select="emit('select', $event)" /></template>
''',
    )
    write(
        nuxt / "CatalogDetail.vue",
        nuxt_detail("catalog_item", "Catalog item", json.dumps(labels(catalog_item))),
    )

    react_child = '''
export function QuoteLineTable(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${encodeURIComponent(JSON.stringify({ quote_record_id: parentId }))}` : "?limit=50";
 return <AdminRecordTable {...props} entityKey="quote_line" search={search} columns={[{key:"description",label:"Description"},{key:"quantity",label:"Qty"},{key:"unit_price",label:"Unit price"}]} emptyTitle={props.emptyTitle || "No quote lines yet"} />;
}
export function QuoteLineDetail(props) {
 return <AdminRecordDetail {...props} entityKey="quote_line" fields={quoteLineSchema.fields.map(f=>({key:f.key,label:f.name}))} />;
}
export function QuoteLineCreate(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? { quote_record_id: parentId } : {};
 return <RecordCreate {...props} entityKey="quote_line" schema={quoteLineSchema} defaults={defaults} heading="Create quote line" />;
}
'''
    react_invoice = '''
export function InvoiceLineTable(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${encodeURIComponent(JSON.stringify({ invoice_record_id: parentId }))}` : "?limit=50";
 return <AdminRecordTable {...props} entityKey="invoice_line" search={search} columns={[{key:"description",label:"Description"},{key:"quantity",label:"Qty"},{key:"unit_price",label:"Unit price"}]} emptyTitle={props.emptyTitle || "No invoice lines yet"} />;
}
export function InvoiceLineDetail(props) {
 return <AdminRecordDetail {...props} entityKey="invoice_line" fields={invoiceLineSchema.fields.map(f=>({key:f.key,label:f.name}))} />;
}
export function InvoiceLineCreate(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? { invoice_record_id: parentId } : {};
 return <RecordCreate {...props} entityKey="invoice_line" schema={invoiceLineSchema} defaults={defaults} heading="Create invoice line" />;
}
'''
    react_variant = '''
export function CatalogVariantTable(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${encodeURIComponent(JSON.stringify({ catalog_item_record_id: parentId }))}` : "?limit=50";
 return <AdminRecordTable {...props} entityKey="catalog_variant" search={search} columns={[{key:"sku",label:"SKU"},{key:"name",label:"Name"},{key:"status",label:"Status"}]} emptyTitle={props.emptyTitle || "No catalog variants yet"} />;
}
export function CatalogVariantDetail(props) {
 return <AdminRecordDetail {...props} entityKey="catalog_variant" fields={catalogVariantSchema.fields.map(f=>({key:f.key,label:f.name}))} />;
}
export function CatalogVariantCreate(props) {
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? { catalog_item_record_id: parentId } : {};
 return <RecordCreate {...props} entityKey="catalog_variant" schema={catalogVariantSchema} defaults={defaults} heading="Create catalog variant" />;
}
'''

    react_quote = ROOT / "boilerplates/react_app/src/components/starter/capability-phase3-blocks.jsx"
    quote_src = react_quote.read_text(encoding="utf-8")
    if "QuoteLineTable" not in quote_src:
        quote_src = quote_src.replace(
            'import quoteSchema from "../../lib/entity-schemas/quote.json";',
            'import quoteSchema from "../../lib/entity-schemas/quote.json";\nimport quoteLineSchema from "../../lib/entity-schemas/quote_line.json";',
        )
        quote_src = quote_src.rstrip() + react_child
        write(react_quote, quote_src + "\n")

    react_invoice_path = ROOT / "boilerplates/react_app/src/components/starter/invoice-blocks.jsx"
    invoice_src = react_invoice_path.read_text(encoding="utf-8")
    if "InvoiceLineTable" not in invoice_src:
        if "invoiceLineSchema" not in invoice_src:
            invoice_src = invoice_src.replace(
                'import invoiceSchema from "../../lib/entity-schemas/invoice_record.json";',
                'import invoiceSchema from "../../lib/entity-schemas/invoice_record.json";\nimport invoiceLineSchema from "../../lib/entity-schemas/invoice_line.json";',
            )
        write(react_invoice_path, invoice_src.rstrip() + react_invoice + "\n")

    react_catalog = ROOT / "boilerplates/react_app/src/components/starter/catalog-blocks.jsx"
    catalog_src = react_catalog.read_text(encoding="utf-8")
    catalog_src = catalog_src.replace(
        'columns={[{key:"name",label:"Name"},{key:"sku",label:"SKU"},{key:"status",label:"Status"}]}',
        'columns={[{key:"name",label:"Name"},{key:"sku",label:"SKU"},{key:"category",label:"Category"},{key:"status",label:"Status"}]}',
    )
    if "CatalogVariantTable" not in catalog_src:
        if "catalogVariantSchema" not in catalog_src:
            catalog_src = catalog_src.replace(
                'import catalogSchema from "../../lib/entity-schemas/catalog_item.json";',
                'import catalogSchema from "../../lib/entity-schemas/catalog_item.json";\nimport catalogVariantSchema from "../../lib/entity-schemas/catalog_variant.json";',
            )
        write(react_catalog, catalog_src.rstrip() + react_variant + "\n")
    else:
        write(react_catalog, catalog_src)

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

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

    html_catalog = ROOT / "boilerplates/html/js/catalog-blocks.js"
    catalog_fields = json.dumps({"fields": catalog_item["schema"]["fields"]})
    variant_fields = json.dumps({"fields": catalog_variant["schema"]["fields"]})
    write(
        html_catalog,
        f'''import {{ mountAdminRecordTable, mountAdminRecordDetail, mountRecordCreate, mountRecordEdit }} from "./starter-blocks.js";
const catalogSchema = {catalog_fields};
export function mountCatalogTable(root, props={{}}) {{
 return mountAdminRecordTable(root,{{...props,entityKey:"catalog_item",columns:[{{key:"name",label:"Name"}},{{key:"sku",label:"SKU"}},{{key:"category",label:"Category"}},{{key:"status",label:"Status"}}]}});
}}
export function mountCatalogDetail(root, props={{}}) {{
 return mountAdminRecordDetail(root,{{...props,entityKey:"catalog_item",fields:catalogSchema.fields.map(f=>({{key:f.key,label:f.name}}))}});
}}
export function mountCatalogCreate(root, props={{}}) {{
 return mountRecordCreate(root,{{...props,entityKey:"catalog_item",schema:catalogSchema,heading:"Create catalog item"}});
}}
export function mountCatalogEdit(root, props={{}}) {{
 return mountRecordEdit(root,{{...props,entityKey:"catalog_item",schema:catalogSchema,heading:"Edit catalog item"}});
}}
const catalogVariantSchema = {variant_fields};
export function mountCatalogVariantTable(root, props={{}}) {{
 const parentId = props.parentRecordId || props.recordId || "";
 const search = parentId ? `?limit=50&filter=${{encodeURIComponent(JSON.stringify({{ catalog_item_record_id: parentId }}))}}` : "?limit=50";
 return mountAdminRecordTable(root,{{...props,entityKey:"catalog_variant",search,columns:[{{key:"sku",label:"SKU"}},{{key:"name",label:"Name"}},{{key:"status",label:"Status"}}],emptyTitle:props.emptyTitle||"No catalog variants yet"}});
}}
export function mountCatalogVariantDetail(root, props={{}}) {{
 return mountAdminRecordDetail(root,{{...props,entityKey:"catalog_variant",fields:catalogVariantSchema.fields.map(f=>({{key:f.key,label:f.name}}))}});
}}
export function mountCatalogVariantCreate(root, props={{}}) {{
 const parentId = props.parentRecordId || props.recordId || "";
 const defaults = parentId ? {{ catalog_item_record_id: parentId }} : {{}};
 return mountRecordCreate(root,{{...props,entityKey:"catalog_variant",schema:catalogVariantSchema,defaults,heading:"Create catalog variant"}});
}}
''',
    )

    impls = {
        "crm.quote": {
            "lines": {
                "nuxt_app": "components/starter/blocks/QuoteLineTable.vue",
                "react_app": "src/components/starter/capability-phase3-blocks.jsx#QuoteLineTable",
                "html": "js/capability-phase3-blocks.js#mountQuoteLineTable",
            },
            "line_create": {
                "nuxt_app": "components/starter/blocks/QuoteLineCreate.vue",
                "react_app": "src/components/starter/capability-phase3-blocks.jsx#QuoteLineCreate",
                "html": "js/capability-phase3-blocks.js#mountQuoteLineCreate",
            },
            "line_detail": {
                "nuxt_app": "components/starter/blocks/QuoteLineDetail.vue",
                "react_app": "src/components/starter/capability-phase3-blocks.jsx#QuoteLineDetail",
                "html": "js/capability-phase3-blocks.js#mountQuoteLineDetail",
            },
        },
        "billing.invoice": {
            "lines": {
                "nuxt_app": "components/starter/blocks/InvoiceLineTable.vue",
                "react_app": "src/components/starter/invoice-blocks.jsx#InvoiceLineTable",
                "html": "js/invoice-blocks.js#mountInvoiceLineTable",
            },
            "line_create": {
                "nuxt_app": "components/starter/blocks/InvoiceLineCreate.vue",
                "react_app": "src/components/starter/invoice-blocks.jsx#InvoiceLineCreate",
                "html": "js/invoice-blocks.js#mountInvoiceLineCreate",
            },
            "line_detail": {
                "nuxt_app": "components/starter/blocks/InvoiceLineDetail.vue",
                "react_app": "src/components/starter/invoice-blocks.jsx#InvoiceLineDetail",
                "html": "js/invoice-blocks.js#mountInvoiceLineDetail",
            },
        },
        "catalog.product": {
            "variants": {
                "nuxt_app": "components/starter/blocks/CatalogVariantTable.vue",
                "react_app": "src/components/starter/catalog-blocks.jsx#CatalogVariantTable",
                "html": "js/catalog-blocks.js#mountCatalogVariantTable",
            },
            "variant_create": {
                "nuxt_app": "components/starter/blocks/CatalogVariantCreate.vue",
                "react_app": "src/components/starter/catalog-blocks.jsx#CatalogVariantCreate",
                "html": "js/catalog-blocks.js#mountCatalogVariantCreate",
            },
            "variant_detail": {
                "nuxt_app": "components/starter/blocks/CatalogVariantDetail.vue",
                "react_app": "src/components/starter/catalog-blocks.jsx#CatalogVariantDetail",
                "html": "js/catalog-blocks.js#mountCatalogVariantDetail",
            },
        },
    }

    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("wave2 child-record hosts materialized")


if __name__ == "__main__":
    main()
