import { 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 BlueprintRecord = {
  id: string;
  name: string;
  fieldDefs: unknown;
  rolePresets: unknown;
};

type ResolvedResource = {
  id: string;
};

type BlueprintsRateCardsDeps = {
  createBlueprintCaller: (ctx: TRPCContext) => {
    listSummaries: () => Promise<Array<{
      id: string;
      name: string;
      _count: { projects: number };
    }>>;
    getByIdentifier: (params: { identifier: string }) => Promise<BlueprintRecord>;
  };
  createRateCardCaller: (ctx: TRPCContext) => {
    list: (params: {
      isActive: boolean;
      search?: string;
    }) => Promise<Array<{
      id: string;
      name: string;
      effectiveFrom: Date | null;
      effectiveTo: Date | null;
      _count: { lines: number };
    }>>;
    resolveBestRate: (params: {
      resourceId?: string;
      roleName?: string;
      date?: Date;
    }) => Promise<unknown>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  resolveResourceIdentifier: (
    ctx: ToolContext,
    identifier: string,
  ) => Promise<ResolvedResource | AssistantToolErrorResult>;
  resolveEntityOrAssistantError: <T>(
    resolve: () => Promise<T>,
    notFoundMessage: string,
  ) => Promise<T | AssistantToolErrorResult>;
  parseOptionalIsoDate: (
    value: string | undefined,
    fieldName: string,
  ) => Date | undefined;
  fmtDate: (value: Date | null | undefined) => string | null;
};

export const blueprintsRateCardsToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "list_blueprints",
      description: "List available project blueprints with their field definitions.",
      parameters: { type: "object", properties: {} },
    },
  },
  {
    type: "function",
    function: {
      name: "get_blueprint",
      description: "Get detailed blueprint with all field definitions and role presets.",
      parameters: {
        type: "object",
        properties: {
          identifier: { type: "string", description: "Blueprint ID or name (partial match)" },
        },
        required: ["identifier"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_rate_cards",
      description: "List rate cards with their effective dates and line items.",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Search by name" },
          limit: { type: "integer", description: "Max results. Default: 20" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "resolve_rate",
      description: "Look up the applicable rate for a resource, role, or management level from rate cards.",
      parameters: {
        type: "object",
        properties: {
          resourceId: { type: "string", description: "Resource ID or name" },
          roleName: { type: "string", description: "Role name" },
          date: { type: "string", description: "Date to check rate for (YYYY-MM-DD). Default: today" },
        },
      },
    },
  },
], {
  list_blueprints: {
    requiresPlanningRead: true,
  },
  get_blueprint: {
    requiresPlanningRead: true,
  },
  list_rate_cards: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
    requiresCostView: true,
  },
  resolve_rate: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
    requiresCostView: true,
  },
});

export function createBlueprintsRateCardsExecutors(
  deps: BlueprintsRateCardsDeps,
): Record<string, ToolExecutor> {
  return {
    async list_blueprints(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createBlueprintCaller(deps.createScopedCallerContext(ctx));
      const blueprints = await caller.listSummaries();
      return blueprints.map((blueprint) => ({
        id: blueprint.id,
        name: blueprint.name,
        projectCount: blueprint._count.projects,
      }));
    },

    async get_blueprint(params: { identifier: string }, ctx: ToolContext) {
      const caller = deps.createBlueprintCaller(deps.createScopedCallerContext(ctx));
      const blueprint = await deps.resolveEntityOrAssistantError(
        () => caller.getByIdentifier({ identifier: params.identifier }),
        `Blueprint not found: ${params.identifier}`,
      );
      if ("error" in blueprint) {
        return blueprint;
      }

      return {
        id: blueprint.id,
        name: blueprint.name,
        fieldDefs: blueprint.fieldDefs,
        rolePresets: blueprint.rolePresets,
      };
    },

    async list_rate_cards(
      params: { query?: string; limit?: number },
      ctx: ToolContext,
    ) {
      const caller = deps.createRateCardCaller(deps.createScopedCallerContext(ctx));
      const cards = await caller.list({
        isActive: true,
        ...(params.query ? { search: params.query } : {}),
      });

      return cards
        .map((card) => ({
          id: card.id,
          name: card.name,
          effectiveFrom: deps.fmtDate(card.effectiveFrom),
          effectiveTo: deps.fmtDate(card.effectiveTo),
          lineCount: card._count.lines,
        }))
        .slice(0, Math.min(params.limit ?? 20, 50));
    },

    async resolve_rate(
      params: { resourceId?: string; roleName?: string; date?: string },
      ctx: ToolContext,
    ) {
      const caller = deps.createRateCardCaller(deps.createScopedCallerContext(ctx));
      const date = deps.parseOptionalIsoDate(params.date, "date");

      if (params.resourceId) {
        const resource = await deps.resolveResourceIdentifier(ctx, params.resourceId);
        if ("error" in resource) {
          return resource;
        }

        return caller.resolveBestRate({
          resourceId: resource.id,
          ...(date ? { date } : {}),
        });
      }

      return caller.resolveBestRate({
        ...(params.roleName ? { roleName: params.roleName } : {}),
        ...(date ? { date } : {}),
      });
    },
  };
}
