import type {
  CreateEstimateInput,
  EstimateExportFormat,
  EstimateStatus,
  UpdateEstimateDraftInput,
} from "@capakraken/shared";
import { PermissionKey, SystemRole } from "@capakraken/shared";
import type { TRPCContext } from "../../trpc.js";
import {
  withToolAccess,
  type ToolContext,
  type ToolDef,
  type ToolExecutor,
} from "./shared.js";

type AssistantToolErrorResult = { error: string };

type EstimateMutationAction =
  | "clone"
  | "updateDraft"
  | "submitVersion"
  | "approveVersion"
  | "createRevision"
  | "createExport"
  | "createPlanningHandoff"
  | "generateWeeklyPhasing"
  | "updateCommercialTerms";

type WeeklyPhasingPattern = "even" | "front_loaded" | "back_loaded" | "custom";

type ResolvedProject = {
  id: string;
};

type EstimateRecord = {
  id: string;
  name: string;
};

type EstimateToolsDeps = {
  assertPermission: (ctx: ToolContext, perm: PermissionKey) => void;
  createEstimateCaller: (ctx: TRPCContext) => {
    getById: (params: { id: string }) => Promise<unknown>;
    listVersions: (params: { estimateId: string }) => Promise<unknown>;
    getVersionSnapshot: (params: {
      estimateId: string;
      versionId?: string;
    }) => Promise<unknown>;
    create: (params: {
      name: string;
      projectId?: string;
      opportunityId?: string;
      baseCurrency?: string;
      status?: EstimateStatus;
      versionLabel?: string;
      versionNotes?: string;
      assumptions?: CreateEstimateInput["assumptions"];
      scopeItems?: CreateEstimateInput["scopeItems"];
      demandLines?: CreateEstimateInput["demandLines"];
      resourceSnapshots?: CreateEstimateInput["resourceSnapshots"];
      metrics?: CreateEstimateInput["metrics"];
    }) => Promise<EstimateRecord>;
    clone: (params: {
      sourceEstimateId: string;
      name?: string;
      projectId?: string;
    }) => Promise<EstimateRecord>;
    updateDraft: (params: {
      id: string;
      projectId?: string;
      name?: string;
      opportunityId?: string;
      baseCurrency?: string;
      status?: EstimateStatus;
      versionLabel?: string;
      versionNotes?: string;
      assumptions?: UpdateEstimateDraftInput["assumptions"];
      scopeItems?: UpdateEstimateDraftInput["scopeItems"];
      demandLines?: UpdateEstimateDraftInput["demandLines"];
      resourceSnapshots?: UpdateEstimateDraftInput["resourceSnapshots"];
      metrics?: UpdateEstimateDraftInput["metrics"];
    }) => Promise<EstimateRecord>;
    submitVersion: (params: {
      estimateId: string;
      versionId?: string;
    }) => Promise<EstimateRecord>;
    approveVersion: (params: {
      estimateId: string;
      versionId?: string;
    }) => Promise<EstimateRecord>;
    createRevision: (params: {
      estimateId: string;
      sourceVersionId?: string;
      label?: string;
      notes?: string;
    }) => Promise<EstimateRecord>;
    createExport: (params: {
      estimateId: string;
      versionId?: string;
      format: EstimateExportFormat;
    }) => Promise<EstimateRecord>;
    createPlanningHandoff: (params: {
      estimateId: string;
      versionId?: string;
    }) => Promise<object>;
    generateWeeklyPhasing: (params: {
      estimateId: string;
      startDate: string;
      endDate: string;
      pattern?: WeeklyPhasingPattern;
    }) => Promise<object>;
    getWeeklyPhasing: (params: { estimateId: string }) => Promise<unknown>;
    getCommercialTerms: (params: {
      estimateId: string;
      versionId?: string;
    }) => Promise<unknown>;
    updateCommercialTerms: (params: {
      estimateId: string;
      versionId?: string;
      terms: Record<string, unknown>;
    }) => Promise<object>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  resolveProjectIdentifier: (
    ctx: ToolContext,
    identifier: string,
  ) => Promise<ResolvedProject | AssistantToolErrorResult>;
  toAssistantEstimateNotFoundError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantEstimateReadError: (
    error: unknown,
    context: "weeklyPhasing" | "commercialTerms",
  ) => AssistantToolErrorResult | null;
  toAssistantEstimateCreationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantEstimateMutationError: (
    error: unknown,
    action: EstimateMutationAction,
  ) => AssistantToolErrorResult | null;
};

async function resolveEstimateProjectId(
  ctx: ToolContext,
  deps: EstimateToolsDeps,
  params: { projectId?: string; projectCode?: string },
): Promise<string | AssistantToolErrorResult | undefined> {
  if (params.projectId) {
    return params.projectId;
  }
  if (!params.projectCode) {
    return undefined;
  }

  const project = await deps.resolveProjectIdentifier(ctx, params.projectCode);
  if ("error" in project) {
    return project;
  }
  return project.id;
}

export const estimateReadToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "get_estimate_detail",
      description: "Get one estimate via the real estimate router, including versions, demand lines, metrics, assumptions, and linked project data. Controller/manager/admin access and viewCosts required.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID" },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_estimate_versions",
      description: "List estimate versions via the real estimate router, including status, timestamps, and artifact counts. Controller/manager/admin access required.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID" },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_estimate_version_snapshot",
      description: "Get an estimate version snapshot via the real estimate router, including totals, breakdowns, exports, and resource snapshots. Controller/manager/admin access and viewCosts required.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID" },
          versionId: { type: "string", description: "Optional explicit version ID. Defaults to the latest version." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_estimate_weekly_phasing",
      description: "Get generated weekly phasing for an estimate via the real estimate router. Controller/manager/admin access required.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_estimate_commercial_terms",
      description: "Get estimate commercial terms via the real estimate router. Controller/manager/admin access required.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit version ID." },
        },
        required: ["estimateId"],
      },
    },
  },
], {
  get_estimate_detail: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
    requiresCostView: true,
  },
  list_estimate_versions: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
  },
  get_estimate_version_snapshot: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
    requiresCostView: true,
  },
  get_estimate_weekly_phasing: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
  },
  get_estimate_commercial_terms: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
  },
});

export const estimateMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "create_estimate",
      description: "Create a new estimate via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          projectId: { type: "string", description: "Optional project ID." },
          projectCode: { type: "string", description: "Optional project short code convenience alias." },
          name: { type: "string", description: "Estimate name." },
          opportunityId: { type: "string", description: "Optional opportunity/reference ID." },
          baseCurrency: { type: "string", description: "Base currency, e.g. EUR." },
          status: { type: "string", enum: ["DRAFT", "IN_REVIEW", "APPROVED", "ARCHIVED"] },
          versionLabel: { type: "string", description: "Optional working version label." },
          versionNotes: { type: "string", description: "Optional working version notes." },
          assumptions: { type: "array", items: { type: "object" }, description: "Estimate assumptions." },
          scopeItems: { type: "array", items: { type: "object" }, description: "Estimate scope items." },
          demandLines: { type: "array", items: { type: "object" }, description: "Estimate demand lines." },
          resourceSnapshots: { type: "array", items: { type: "object" }, description: "Resource cost snapshots." },
          metrics: { type: "array", items: { type: "object" }, description: "Optional metric overrides." },
        },
        required: ["name"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "clone_estimate",
      description: "Clone an existing estimate via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          sourceEstimateId: { type: "string", description: "Source estimate ID." },
          name: { type: "string", description: "Optional cloned estimate name." },
          projectId: { type: "string", description: "Optional target project ID." },
          projectCode: { type: "string", description: "Optional target project short code convenience alias." },
        },
        required: ["sourceEstimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_estimate_draft",
      description: "Update the working draft of an estimate via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Estimate ID." },
          projectId: { type: "string", description: "Optional linked project ID." },
          projectCode: { type: "string", description: "Optional linked project short code convenience alias." },
          name: { type: "string" },
          opportunityId: { type: "string" },
          baseCurrency: { type: "string" },
          status: { type: "string", enum: ["DRAFT", "IN_REVIEW", "APPROVED", "ARCHIVED"] },
          versionLabel: { type: "string" },
          versionNotes: { type: "string" },
          assumptions: { type: "array", items: { type: "object" } },
          scopeItems: { type: "array", items: { type: "object" } },
          demandLines: { type: "array", items: { type: "object" } },
          resourceSnapshots: { type: "array", items: { type: "object" } },
          metrics: { type: "array", items: { type: "object" } },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "submit_estimate_version",
      description: "Submit an estimate working version for review via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit version ID." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "approve_estimate_version",
      description: "Approve a submitted estimate version via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit version ID." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_estimate_revision",
      description: "Create a new working revision from the latest locked estimate version via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          sourceVersionId: { type: "string", description: "Optional source version ID." },
          label: { type: "string", description: "Optional revision label." },
          notes: { type: "string", description: "Optional revision notes." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_estimate_export",
      description: "Create an estimate export artifact via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit version ID." },
          format: { type: "string", enum: ["XLSX", "CSV", "JSON", "SAP", "MMP"], description: "Export format." },
        },
        required: ["estimateId", "format"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_estimate_planning_handoff",
      description: "Create planning allocations from an approved estimate version via the real estimate router. Manager/admin role and manageAllocations permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit approved version ID." },
        },
        required: ["estimateId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "generate_estimate_weekly_phasing",
      description: "Generate weekly phasing for the working estimate version via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          startDate: { type: "string", description: "Start date in YYYY-MM-DD." },
          endDate: { type: "string", description: "End date in YYYY-MM-DD." },
          pattern: { type: "string", enum: ["even", "front_loaded", "back_loaded", "custom"], description: "Distribution pattern." },
        },
        required: ["estimateId", "startDate", "endDate"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_estimate_commercial_terms",
      description: "Update estimate commercial terms on a working version via the real estimate router. Manager/admin role and manageProjects permission required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          estimateId: { type: "string", description: "Estimate ID." },
          versionId: { type: "string", description: "Optional explicit version ID." },
          terms: { type: "object", description: "Commercial terms payload." },
        },
        required: ["estimateId", "terms"],
      },
    },
  },
], {
  create_estimate: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  clone_estimate: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  update_estimate_draft: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  submit_estimate_version: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  approve_estimate_version: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  create_estimate_revision: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  create_estimate_export: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  create_estimate_planning_handoff: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_ALLOCATIONS],
  },
  generate_estimate_weekly_phasing: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
  update_estimate_commercial_terms: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
    requiredPermissions: [PermissionKey.MANAGE_PROJECTS],
  },
});

export function createEstimateExecutors(
  deps: EstimateToolsDeps,
): Record<string, ToolExecutor> {
  return {
    async get_estimate_detail(params: { estimateId: string }, ctx: ToolContext) {
      deps.assertPermission(ctx, "viewCosts");
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getById({ id: params.estimateId });
      } catch (error) {
        const mapped = deps.toAssistantEstimateNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async list_estimate_versions(params: { estimateId: string }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.listVersions({ estimateId: params.estimateId });
      } catch (error) {
        const mapped = deps.toAssistantEstimateNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async get_estimate_version_snapshot(params: {
      estimateId: string;
      versionId?: string;
    }, ctx: ToolContext) {
      deps.assertPermission(ctx, "viewCosts");
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getVersionSnapshot({
          estimateId: params.estimateId,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async create_estimate(params: {
      projectId?: string;
      projectCode?: string;
      name: string;
      opportunityId?: string;
      baseCurrency?: string;
      status?: EstimateStatus;
      versionLabel?: string;
      versionNotes?: string;
      assumptions?: CreateEstimateInput["assumptions"];
      scopeItems?: CreateEstimateInput["scopeItems"];
      demandLines?: CreateEstimateInput["demandLines"];
      resourceSnapshots?: CreateEstimateInput["resourceSnapshots"];
      metrics?: CreateEstimateInput["metrics"];
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      const projectId = await resolveEstimateProjectId(ctx, deps, params);
      if (projectId && typeof projectId === "object" && "error" in projectId) {
        return projectId;
      }

      let estimate;
      try {
        estimate = await caller.create({
          name: params.name,
          ...(typeof projectId === "string" ? { projectId } : {}),
          ...(params.opportunityId !== undefined ? { opportunityId: params.opportunityId } : {}),
          ...(params.baseCurrency !== undefined ? { baseCurrency: params.baseCurrency } : {}),
          ...(params.status !== undefined ? { status: params.status } : {}),
          ...(params.versionLabel !== undefined ? { versionLabel: params.versionLabel } : {}),
          ...(params.versionNotes !== undefined ? { versionNotes: params.versionNotes } : {}),
          ...(params.assumptions !== undefined ? { assumptions: params.assumptions } : {}),
          ...(params.scopeItems !== undefined ? { scopeItems: params.scopeItems } : {}),
          ...(params.demandLines !== undefined ? { demandLines: params.demandLines } : {}),
          ...(params.resourceSnapshots !== undefined ? { resourceSnapshots: params.resourceSnapshots } : {}),
          ...(params.metrics !== undefined ? { metrics: params.metrics } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateCreationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Created estimate "${estimate.name}".`,
      };
    },

    async clone_estimate(params: {
      sourceEstimateId: string;
      name?: string;
      projectId?: string;
      projectCode?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      const projectId = await resolveEstimateProjectId(ctx, deps, params);
      if (projectId && typeof projectId === "object" && "error" in projectId) {
        return projectId;
      }

      let estimate;
      try {
        estimate = await caller.clone({
          sourceEstimateId: params.sourceEstimateId,
          ...(params.name !== undefined ? { name: params.name } : {}),
          ...(typeof projectId === "string" ? { projectId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "clone");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Cloned estimate "${estimate.name}".`,
      };
    },

    async update_estimate_draft(params: {
      id: string;
      projectId?: string;
      projectCode?: string;
      name?: string;
      opportunityId?: string;
      baseCurrency?: string;
      status?: EstimateStatus;
      versionLabel?: string;
      versionNotes?: string;
      assumptions?: UpdateEstimateDraftInput["assumptions"];
      scopeItems?: UpdateEstimateDraftInput["scopeItems"];
      demandLines?: UpdateEstimateDraftInput["demandLines"];
      resourceSnapshots?: UpdateEstimateDraftInput["resourceSnapshots"];
      metrics?: UpdateEstimateDraftInput["metrics"];
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      const projectId = await resolveEstimateProjectId(ctx, deps, params);
      if (projectId && typeof projectId === "object" && "error" in projectId) {
        return projectId;
      }

      let estimate;
      try {
        estimate = await caller.updateDraft({
          id: params.id,
          ...(typeof projectId === "string" ? { projectId } : {}),
          ...(params.name !== undefined ? { name: params.name } : {}),
          ...(params.opportunityId !== undefined ? { opportunityId: params.opportunityId } : {}),
          ...(params.baseCurrency !== undefined ? { baseCurrency: params.baseCurrency } : {}),
          ...(params.status !== undefined ? { status: params.status } : {}),
          ...(params.versionLabel !== undefined ? { versionLabel: params.versionLabel } : {}),
          ...(params.versionNotes !== undefined ? { versionNotes: params.versionNotes } : {}),
          ...(params.assumptions !== undefined ? { assumptions: params.assumptions } : {}),
          ...(params.scopeItems !== undefined ? { scopeItems: params.scopeItems } : {}),
          ...(params.demandLines !== undefined ? { demandLines: params.demandLines } : {}),
          ...(params.resourceSnapshots !== undefined ? { resourceSnapshots: params.resourceSnapshots } : {}),
          ...(params.metrics !== undefined ? { metrics: params.metrics } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "updateDraft");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Updated estimate draft "${estimate.name}".`,
      };
    },

    async submit_estimate_version(params: {
      estimateId: string;
      versionId?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let estimate;
      try {
        estimate = await caller.submitVersion({
          estimateId: params.estimateId,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "submitVersion");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Submitted estimate version for "${estimate.name}".`,
      };
    },

    async approve_estimate_version(params: {
      estimateId: string;
      versionId?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let estimate;
      try {
        estimate = await caller.approveVersion({
          estimateId: params.estimateId,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "approveVersion");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Approved estimate version for "${estimate.name}".`,
      };
    },

    async create_estimate_revision(params: {
      estimateId: string;
      sourceVersionId?: string;
      label?: string;
      notes?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let estimate;
      try {
        estimate = await caller.createRevision({
          estimateId: params.estimateId,
          ...(params.sourceVersionId !== undefined ? { sourceVersionId: params.sourceVersionId } : {}),
          ...(params.label !== undefined ? { label: params.label } : {}),
          ...(params.notes !== undefined ? { notes: params.notes } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "createRevision");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Created a new estimate revision for "${estimate.name}".`,
      };
    },

    async create_estimate_export(params: {
      estimateId: string;
      versionId?: string;
      format: EstimateExportFormat;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let estimate;
      try {
        estimate = await caller.createExport({
          estimateId: params.estimateId,
          format: params.format,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "createExport");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        estimate,
        estimateId: estimate.id,
        message: `Created ${params.format} export for estimate "${estimate.name}".`,
      };
    },

    async create_estimate_planning_handoff(params: {
      estimateId: string;
      versionId?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.createPlanningHandoff({
          estimateId: params.estimateId,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "createPlanningHandoff");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate", "allocation", "timeline"],
        success: true,
        ...result,
        message: `Created planning handoff for estimate ${params.estimateId}.`,
      };
    },

    async generate_estimate_weekly_phasing(params: {
      estimateId: string;
      startDate: string;
      endDate: string;
      pattern?: WeeklyPhasingPattern;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.generateWeeklyPhasing({
          estimateId: params.estimateId,
          startDate: params.startDate,
          endDate: params.endDate,
          ...(params.pattern !== undefined ? { pattern: params.pattern } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "generateWeeklyPhasing");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        ...result,
        message: `Generated weekly phasing for estimate ${params.estimateId}.`,
      };
    },

    async get_estimate_weekly_phasing(params: {
      estimateId: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getWeeklyPhasing({ estimateId: params.estimateId });
      } catch (error) {
        const mapped = deps.toAssistantEstimateReadError(error, "weeklyPhasing");
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async get_estimate_commercial_terms(params: {
      estimateId: string;
      versionId?: string;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getCommercialTerms({
          estimateId: params.estimateId,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateReadError(error, "commercialTerms");
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async update_estimate_commercial_terms(params: {
      estimateId: string;
      versionId?: string;
      terms: Record<string, unknown>;
    }, ctx: ToolContext) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let result;
      try {
        result = await caller.updateCommercialTerms({
          estimateId: params.estimateId,
          terms: params.terms,
          ...(params.versionId !== undefined ? { versionId: params.versionId } : {}),
        });
      } catch (error) {
        const mapped = deps.toAssistantEstimateMutationError(error, "updateCommercialTerms");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["estimate"],
        success: true,
        ...result,
        message: `Updated commercial terms for estimate ${params.estimateId}.`,
      };
    },
  };
}
