"""Reproducibly install CRM wrappers into the existing three Starter runtimes.

Boilerplate source trees are locally maintained/ignored by repository policy.
This script retains the new wrapper source in version control. No new adapter,
route authority, or persistence path: every wrapper mounts existing composites.
"""
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_crm_authorization_11e2 import lead_composition
from tests.test_phase11_pack_migration_materialization import _load_migration


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


def main():
    customer = _load_migration(str(ROOT / "alembic/versions/144_customer_records_pack.py")).customer_composition()["entities"][0]["schema"]
    lead = lead_composition()["entities"][0]["schema"]
    # These are presentation projections. Workflow legality stays in Cloud.
    schemas = {"customer": customer, "lead": lead}
    for framework, lib in (("nuxt_app", "lib"), ("react_app", "src/lib"), ("html", "js")):
        for key, schema in schemas.items():
            write(ROOT / "boilerplates" / framework / lib / "entity-schemas" / f"{key}.json", json.dumps({"fields": schema["fields"]}, indent=2) + "\n")

    nuxt = ROOT / "boilerplates/nuxt_app/components/starter/blocks"
    fields = [{"key": f["key"], "label": f["name"]} for f in customer["fields"]]
    write(nuxt / "CustomerTable.vue", '''<script setup>
import AdminRecordTable from "./AdminRecordTable.vue";
defineProps({ emptyTitle: { type: String, default: "No customers yet" } });
const emit = defineEmits(["select"]);
</script>
<template><AdminRecordTable entity-key="customer" :empty-title="emptyTitle"
  :columns="[{key:'name',label:'Name'},{key:'email',label:'Email'},{key:'status',label:'Status'}]"
  @select="emit('select', $event)" /></template>
''')
    write(nuxt / "CustomerDetail.vue", '''<script setup>
import AdminRecordDetail from "./AdminRecordDetail.vue";
defineProps({ recordId: { type: String, required: true } });
const fields = ''' + json.dumps(fields) + ''';
</script>
<template><AdminRecordDetail entity-key="customer" :record-id="recordId" title="Customer" :fields="fields" /></template>
''')
    for action in ("Create", "Edit"):
        edit = action == "Edit"
        write(nuxt / f"Customer{action}.vue", f'''<script setup>
import schema from "~/lib/entity-schemas/customer.json";
import Record{action} from "../Record{action}.vue";
defineProps({{ recordId: {{ type: String, default: "" }} }});
const emit = defineEmits(["{'updated' if edit else 'created'}"]);
</script>
<template><Record{action} entity-key="customer" :schema="schema" {' :record-id="recordId"' if edit else ''}
 heading="{action} Customer" @{'updated' if edit else 'created'}="emit('{'updated' if edit else 'created'}', $event)" /></template>
''')
    write(nuxt / "PipelineDetail.vue", '''<script setup>
import schema from "~/lib/entity-schemas/lead.json";
import LeadDetail from "./LeadDetail.vue";
import RecordEdit from "../RecordEdit.vue";
defineProps({ recordId: { type: String, required: true } });
</script>
<template><section><LeadDetail :record-id="recordId" />
 <RecordEdit entity-key="lead" :record-id="recordId" :schema="schema"
 :visible-fields="['status']" heading="Change pipeline stage" submit-label="Change stage" />
</section></template>
''')
    react = '''import React from "react";
import { AdminRecordTable, AdminRecordDetail, LeadDetail } from "./blocks";
import { RecordCreate } from "./RecordCreate";
import { RecordEdit } from "./RecordEdit";
import customerSchema from "../../lib/entity-schemas/customer.json";
import leadSchema from "../../lib/entity-schemas/lead.json";
export function CustomerTable(props) {
 return <AdminRecordTable {...props} entityKey="customer" columns={[{key:"name",label:"Name"},{key:"email",label:"Email"},{key:"status",label:"Status"}]} />;
}
export function CustomerDetail(props) {
 return <AdminRecordDetail {...props} entityKey="customer" fields={customerSchema.fields.map(f=>({key:f.key,label:f.name}))} />;
}
export function CustomerCreate(props) {
 return <RecordCreate {...props} entityKey="customer" schema={customerSchema} heading="Create Customer" />;
}
export function CustomerEdit(props) {
 return <RecordEdit {...props} entityKey="customer" schema={customerSchema} heading="Edit Customer" />;
}
export function PipelineDetail({recordId}) {
 return <section><LeadDetail recordId={recordId}/><RecordEdit entityKey="lead" recordId={recordId}
 schema={leadSchema} visibleFields={["status"]} heading="Change pipeline stage" submitLabel="Change stage" /></section>;
}
'''
    write(ROOT / "boilerplates/react_app/src/components/starter/crm-blocks.jsx", react)
    html = '''import { mountAdminRecordTable, mountAdminRecordDetail, mountRecordCreate, mountRecordEdit, mountLeadDetail } from "./starter-blocks.js";
'''
    # JSON module import support differs among the maintained HTML hosts; embed
    # exact schema projection like the existing Documents host, never a graph.
    html += "const customerSchema = " + json.dumps({"fields": customer["fields"]}) + ";\n"
    html += "const leadSchema = " + json.dumps({"fields": lead["fields"]}) + ";\n"
    html += '''export function mountCustomerTable(root, props={}) {
 return mountAdminRecordTable(root,{...props,entityKey:"customer",columns:[{key:"name",label:"Name"},{key:"email",label:"Email"},{key:"status",label:"Status"}]});
}
export function mountCustomerDetail(root, props={}) {
 return mountAdminRecordDetail(root,{...props,entityKey:"customer",fields:customerSchema.fields.map(f=>({key:f.key,label:f.name}))});
}
export function mountCustomerCreate(root, props={}) {
 return mountRecordCreate(root,{...props,entityKey:"customer",schema:customerSchema,heading:"Create Customer"});
}
export function mountCustomerEdit(root, props={}) {
 return mountRecordEdit(root,{...props,entityKey:"customer",schema:customerSchema,heading:"Edit Customer"});
}
export function mountPipelineDetail(root, props={}) {
 const detail=document.createElement("section"); const edit=document.createElement("section");
 root.replaceChildren(detail,edit);
 mountLeadDetail(detail,props);
 return mountRecordEdit(edit,{...props,entityKey:"lead",schema:leadSchema,visibleFields:["status"],heading:"Change pipeline stage",submitLabel:"Change stage"});
}
'''
    write(ROOT / "boilerplates/html/js/crm-blocks.js", html)
    for framework in ("nuxt_app", "react_app", "html"):
        path = ROOT / "boilerplates" / framework / "p2hs/app-block-hosts.json"
        existing = json.loads(path.read_text())
        hosts = merge_host_map(existing)
        existing_by_key = {(h["block"], h["slot"]): h for h in existing["hosts"]}
        for host in hosts["hosts"]:
            block, slot = host["block"], host["slot"]
            if block not in ("crm.customer", "crm.pipeline"):
                continue
            if slot == "empty" or (block == "crm.pipeline" and slot == "pipeline"):
                host["implementation"] = existing_by_key[("crm.lead", slot)]["implementation"]
                continue
            component = "PipelineDetail" if block == "crm.pipeline" else "Customer" + {"list":"Table","detail":"Detail","create":"Create","edit":"Edit"}[slot]
            if framework == "nuxt_app":
                impl = f"components/starter/blocks/{component}.vue"
            elif framework == "react_app":
                impl = f"src/components/starter/crm-blocks.jsx#{component}"
            else:
                impl = f"js/crm-blocks.js#mount{component}"
            host["implementation"] = impl
        hosts["hosts"] = [host for host in hosts["hosts"] if host.get("implementation")]
        write(path, json.dumps(hosts, indent=2) + "\n")
    print("CRM hosts materialized in the three existing Starter runtimes")


if __name__ == "__main__":
    main()
