// React provider exposing the canonical SysMLModel + applyOps.
// Used by both canvases (and the rail) so they stay in sync.

"use client";

import { createContext, useCallback, useContext, useMemo, useState } from "react";
import { applyOps, type ApplyResult } from "./applyOps";
import { validate, type ValidationIssue } from "../sysml/validate";
import type { SysMLModel } from "../sysml/model";
import type { ModelOp } from "./ops";

export interface ModelStoreValue {
  model: SysMLModel;
  apply: (ops: ModelOp[]) => ApplyResult;
  /** Validation issues, recomputed on each successful apply. */
  issues: ValidationIssue[];
  issuesByElement: Map<string, ValidationIssue[]>;
}

const ModelStoreContext = createContext<ModelStoreValue | null>(null);

interface ModelStoreProviderProps {
  initialModel: SysMLModel;
  children: React.ReactNode;
}

export function ModelStoreProvider({ initialModel, children }: ModelStoreProviderProps) {
  const [model, setModel] = useState<SysMLModel>(initialModel);

  const apply = useCallback((ops: ModelOp[]): ApplyResult => {
    let result: ApplyResult = { model, idMapping: {}, errors: [], applied: false };
    setModel(current => {
      result = applyOps(current, ops);
      return result.applied ? result.model : current;
    });
    return result;
  }, [model]);

  const { issues, issuesByElement } = useMemo(() => {
    const issues = validate(model);
    const issuesByElement = new Map<string, ValidationIssue[]>();
    for (const i of issues) {
      const k = anchorKey(i.anchor);
      if (!k) continue;
      const prev = issuesByElement.get(k) ?? [];
      prev.push(i);
      issuesByElement.set(k, prev);
    }
    return { issues, issuesByElement };
  }, [model]);

  const value: ModelStoreValue = useMemo(
    () => ({ model, apply, issues, issuesByElement }),
    [model, apply, issues, issuesByElement]
  );

  return <ModelStoreContext.Provider value={value}>{children}</ModelStoreContext.Provider>;
}

export function useModelStore(): ModelStoreValue {
  const ctx = useContext(ModelStoreContext);
  if (!ctx) throw new Error("useModelStore must be used inside ModelStoreProvider");
  return ctx;
}

export function useModel(): SysMLModel {
  return useModelStore().model;
}

export function useApply(): (ops: ModelOp[]) => ApplyResult {
  return useModelStore().apply;
}

function anchorKey(anchor: ValidationIssue["anchor"]): string | null {
  switch (anchor.kind) {
    case "block": return anchor.id;
    case "association": return `assoc:${anchor.id}`;
    case "constraint": return `constraint:${anchor.id}`;
    case "requirement": return `req:${anchor.id}`;
    case "property": return anchor.blockId;
    case "model": return null;
  }
}
