import { readFileSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import { walkPatternSection } from "@specverse/runtime/views/core";
import { extractBelongsToTargets, extractHasManyTargets, pluralize as pluralizeShared } from "./belongs-to.js";
import { emitJsxSource } from "./emit-jsx-source.js";
function emitView(context) {
  const skeleton = loadSkeleton(context.view.type);
  const bodyJsx = context.renderBody(context);
  const substitutions = buildSubstitutions(context, bodyJsx);
  return applySubstitutions(skeleton, substitutions);
}
const SKELETON_BY_VIEW_TYPE = {
  list: "list.tsx.template",
  detail: "detail.tsx.template",
  form: "form.tsx.template",
  dashboard: "dashboard.tsx.template"
  // Specialist types (board, timeline, calendar, analytics, workflow,
  // wizard, comparison, settings, map, feed, profile) come online in
  // Phase 2e.
};
function loadSkeleton(viewType) {
  const filename = SKELETON_BY_VIEW_TYPE[viewType.toLowerCase()];
  if (!filename) {
    throw new Error(
      `No skeleton registered for view type "${viewType}". Known types: ${Object.keys(SKELETON_BY_VIEW_TYPE).join(", ")}.`
    );
  }
  const here = dirname(fileURLToPath(import.meta.url));
  const path = join(here, "skeletons", filename);
  return readFileSync(path, "utf8");
}
function buildSubstitutions(context, body) {
  const modelName = context.model.name;
  const pluralModel = pluralize(modelName);
  const { imports, hooks } = buildBelongsToWiring(context);
  const lifecycle = buildLifecycleSubstitutions(context);
  const { fieldsBody, existingListBody } = buildFormSplitBodies(context);
  return {
    MODEL_NAME: modelName,
    PLURAL_MODEL: pluralModel,
    PLURAL_LOWER: pluralModel.toLowerCase(),
    SINGULAR_LOWER: modelName.toLowerCase(),
    BODY: body,
    RELATED_IMPORTS: imports,
    RELATED_HOOKS: hooks,
    RELATED_TAB_STATE: buildRelatedTabState(context),
    RELATED_TAB_IMPORT: buildRelatedTabImport(context),
    FIELDS_BODY: fieldsBody,
    EXISTING_LIST_BODY: existingListBody,
    ...lifecycle
  };
}
function buildFormSplitBodies(context) {
  if (context.view.type.toLowerCase() !== "form") {
    return { fieldsBody: "", existingListBody: "" };
  }
  const walkCtx = {
    model: context.model,
    modelSchemas: context.modelSchemas,
    view: context.view
  };
  const imports = /* @__PURE__ */ new Set();
  const fieldsNodes = walkPatternSection("form-view", "fields", walkCtx);
  const listNodes = walkPatternSection("form-view", "existing-entities-list", walkCtx);
  return {
    fieldsBody: emitJsxSource(fieldsNodes, { imports, indent: 10 }),
    existingListBody: emitJsxSource(listNodes, { imports, indent: 6 })
  };
}
function buildLifecycleSubstitutions(context) {
  const isForm = context.view.type.toLowerCase() === "form";
  const lifecycles = extractLifecycles(context.model);
  if (!isForm || lifecycles.length === 0) {
    return {
      LIFECYCLE_IMPORTS: "",
      LIFECYCLE_HOOK: "",
      LIFECYCLE_STATE: "",
      LIFECYCLE_HANDLER: "",
      LIFECYCLE_PANEL: "",
      HAS_LIFECYCLES: "false"
    };
  }
  const modelName = context.model.name;
  const primary = lifecycles[0];
  const lifecyclesConst = `${modelName.toUpperCase()}_LIFECYCLES`;
  return {
    LIFECYCLE_IMPORTS: `  useEvolve${modelName}Mutation,
  ${lifecyclesConst},`,
    LIFECYCLE_HOOK: `  const evolveItem = useEvolve${modelName}Mutation();`,
    LIFECYCLE_STATE: `  const [targetState, setTargetState] = useState<string>('');`,
    LIFECYCLE_HANDLER: `  const handleEvolveSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    if (!targetState) return;
    try {
      const result = await evolveItem.mutateAsync({
        id: entityId as any,
        toState: targetState,
        lifecycleName: '${primary.name}',
      });
      onSuccess?.(result as ${modelName});
    } catch {
      // Evolve errors are surfaced via evolveItem.isError.
    }
  };`,
    LIFECYCLE_PANEL: buildLifecyclePanel(modelName, primary, lifecyclesConst),
    HAS_LIFECYCLES: "true"
  };
}
function buildLifecyclePanel(modelName, primary, lifecyclesConst) {
  return `      {showTabs && activeTab === 'evolve' && (
        <form onSubmit={handleEvolveSubmit} className="space-y-6">
          <div className="rounded-lg border border-gray-200 bg-white p-6 dark:border-gray-700 dark:bg-gray-900">
            <dl className="grid grid-cols-1 gap-4 sm:grid-cols-2">
              <div>
                <dt className="text-sm font-medium text-gray-500 dark:text-gray-400">Current ${humanize(primary.name)}</dt>
                <dd className="mt-1">
                  <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
                    {String((existing as any)?.${primary.name} ?? '')}
                  </span>
                </dd>
              </div>
              <div>
                <label htmlFor="evolve-target" className="block text-sm font-medium text-gray-500 dark:text-gray-400">Next ${humanize(primary.name)}</label>
                <select
                  id="evolve-target"
                  value={targetState}
                  onChange={e => setTargetState(e.target.value)}
                  className="mt-1 w-full rounded border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100"
                >
                  <option value="">\u2014 choose \u2014</option>
                  {${lifecyclesConst}.${primary.name}.states.map(s => (
                    <option key={s} value={s}>{s}</option>
                  ))}
                </select>
              </div>
            </dl>
          </div>

          <div>
            <button
              type="submit"
              disabled={evolveItem.isPending || !targetState}
              className="rounded bg-purple-600 px-4 py-2 text-sm font-medium text-white hover:bg-purple-700 disabled:opacity-60"
            >
              {evolveItem.isPending ? 'Evolving\u2026' : \`Evolve to \${targetState || '\u2026'}\`}
            </button>
          </div>

          {evolveItem.isError && (
            <div className="rounded border border-red-200 bg-red-50 p-3 text-sm text-red-700 dark:border-red-800 dark:bg-red-950 dark:text-red-300">
              Evolve failed: {String(evolveItem.error)}
            </div>
          )}
        </form>
      )}`;
}
function extractLifecycles(model) {
  const out = [];
  const lifecycles = model?.lifecycles ?? {};
  for (const [name, def] of Object.entries(lifecycles)) {
    if (!def || typeof def !== "object") continue;
    let states = [];
    if (Array.isArray(def.states)) {
      states = def.states.map((s) => typeof s === "string" ? s : s?.name ?? s?.id ?? "").filter(Boolean);
    } else if (typeof def.flow === "string") {
      states = def.flow.split(/\s*(?:->|,)\s*/).map((s) => s.trim()).filter(Boolean);
    }
    if (states.length > 0) out.push({ name, states });
  }
  return out;
}
function humanize(name) {
  return name.replace(/([A-Z])/g, " $1").replace(/^./, (c) => c.toUpperCase()).trim();
}
function buildRelatedTabImport(context) {
  if (context.view.type.toLowerCase() !== "detail") return "";
  const hasMany = extractHasManyTargets(context.model, context.modelSchemas);
  if (hasMany.length < 2) return "";
  return `import { useState } from 'react';`;
}
function buildRelatedTabState(context) {
  if (context.view.type.toLowerCase() !== "detail") return "";
  const hasMany = extractHasManyTargets(context.model, context.modelSchemas);
  if (hasMany.length < 2) return "";
  const firstName = hasMany[0].name;
  return `  const [relatedTab, setRelatedTab] = useState<string>('${firstName}');`;
}
function buildBelongsToWiring(context) {
  const belongsTo = extractBelongsToTargets(context.model);
  const isDetail = context.view.type.toLowerCase() === "detail";
  const hasMany = isDetail ? extractHasManyTargets(context.model, context.modelSchemas) : [];
  const crossBelongsTo = [];
  if (isDetail) {
    for (const rel of hasMany) {
      const targetModel = context.modelSchemas[rel.target];
      if (!targetModel) continue;
      const targetFK = rel.foreignKey;
      for (const btm of extractBelongsToTargets(targetModel)) {
        if (`${btm.name}Id` === targetFK) continue;
        crossBelongsTo.push(btm);
      }
    }
  }
  if (belongsTo.length === 0 && hasMany.length === 0 && crossBelongsTo.length === 0) {
    const vt = context.view.type.toLowerCase();
    if (vt === "detail") {
      return {
        imports: `import { getEntityDisplayName, formatDisplayValue } from '../lib/entity-display';`,
        hooks: ""
      };
    }
    if (vt === "list" || vt === "dashboard") {
      return {
        imports: `import { formatDisplayValue } from '../lib/entity-display';`,
        hooks: ""
      };
    }
    return { imports: "", hooks: "" };
  }
  const hookImportNames = /* @__PURE__ */ new Set();
  for (const rel of belongsTo) {
    hookImportNames.add(`use${pluralizeShared(rel.target)}Query`);
  }
  for (const rel of hasMany) {
    hookImportNames.add(`use${pluralizeShared(rel.target)}Query`);
  }
  for (const rel of crossBelongsTo) {
    hookImportNames.add(`use${pluralizeShared(rel.target)}Query`);
  }
  const viewType = context.view.type.toLowerCase();
  const helperImports = /* @__PURE__ */ new Set();
  if (viewType === "form") {
    helperImports.add("getEntityDisplayName");
  } else if (viewType === "detail") {
    helperImports.add("getEntityDisplayName");
    helperImports.add("formatDisplayValue");
    if (belongsTo.length > 0 || crossBelongsTo.length > 0) {
      helperImports.add("resolveEntityDisplayName");
    }
  } else if (viewType === "list" || viewType === "dashboard") {
    helperImports.add("resolveEntityDisplayName");
    helperImports.add("formatDisplayValue");
  } else {
    helperImports.add("resolveEntityDisplayName");
  }
  const importLines = [];
  importLines.push(
    `import { ${[...hookImportNames].join(", ")} } from '../hooks/useApi';`
  );
  if (helperImports.size > 0) {
    importLines.push(`import { ${[...helperImports].join(", ")} } from '../lib/entity-display';`);
  }
  const hookLines = [];
  for (const rel of belongsTo) {
    hookLines.push(
      `  const { data: ${rel.name}Options = [] } = use${pluralizeShared(rel.target)}Query();`
    );
  }
  for (const rel of hasMany) {
    hookLines.push(
      `  const { data: ${rel.name}All = [] } = use${pluralizeShared(rel.target)}Query();`
    );
  }
  const emittedOptionsVars = new Set(belongsTo.map((r) => `${r.name}Options`));
  for (const rel of crossBelongsTo) {
    const varName = `${rel.name}Options`;
    if (emittedOptionsVars.has(varName)) continue;
    emittedOptionsVars.add(varName);
    hookLines.push(
      `  const { data: ${varName} = [] } = use${pluralizeShared(rel.target)}Query();`
    );
  }
  return {
    imports: importLines.join("\n"),
    hooks: hookLines.join("\n")
  };
}
function applySubstitutions(template, subs) {
  let out = template;
  for (const [key, value] of Object.entries(subs)) {
    const pattern = new RegExp(`\\{\\{${key}\\}\\}`, "g");
    out = out.replace(pattern, value);
  }
  return out;
}
function pluralize(s) {
  if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + "ies";
  if (/(s|x|z|ch|sh)$/i.test(s)) return s + "es";
  return s + "s";
}
export {
  emitView
};
