import type { TRPCContext } from "../../trpc.js";
import {
  CreateClientSchema,
  CreateOrgUnitSchema,
  SystemRole,
  UpdateClientSchema,
  UpdateOrgUnitSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";

type AssistantToolErrorResult = { error: string };

type ClientsOrgUnitsDeps = {
  createClientCaller: (ctx: TRPCContext) => {
    create: (params: z.input<typeof CreateClientSchema>) => Promise<{
      id: string;
      name: string;
    }>;
    update: (params: {
      id: string;
      data: z.input<typeof UpdateClientSchema>;
    }) => Promise<{
      id: string;
      name: string;
    }>;
    getById: (params: { id: string }) => Promise<{ name: string }>;
    delete: (params: { id: string }) => Promise<unknown>;
  };
  createOrgUnitCaller: (ctx: TRPCContext) => {
    create: (params: z.input<typeof CreateOrgUnitSchema>) => Promise<{
      id: string;
      name: string;
    }>;
    update: (params: {
      id: string;
      data: z.input<typeof UpdateOrgUnitSchema>;
    }) => Promise<{
      id: string;
      name: string;
    }>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  toAssistantClientMutationError: (
    error: unknown,
    action?: "create" | "update" | "delete",
  ) => AssistantToolErrorResult | null;
  toAssistantOrgUnitMutationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const clientMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "create_client",
      description: "Create a new client. Requires manager or admin role. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          name: { type: "string", description: "Client name" },
          code: { type: "string", description: "Client code" },
          parentId: { type: "string", description: "Optional parent client ID" },
          sortOrder: { type: "integer", description: "Sort order. Default: 0" },
          tags: { type: "array", items: { type: "string" }, description: "Optional client tags" },
        },
        required: ["name"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_client",
      description: "Update a client. Requires manager or admin role. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Client ID" },
          name: { type: "string", description: "New name" },
          code: { type: "string", description: "New code" },
          sortOrder: { type: "integer", description: "New sort order" },
          isActive: { type: "boolean", description: "Set active state" },
          parentId: { type: "string", description: "Parent client ID; use null to clear" },
          tags: { type: "array", items: { type: "string" }, description: "Replacement client tags" },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "delete_client",
      description: "Delete a client. Requires admin role. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Client ID" },
        },
        required: ["id"],
      },
    },
  },
], {
  create_client: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
  },
  update_client: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
  },
  delete_client: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export const orgUnitMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "create_org_unit",
      description: "Create a new organizational unit. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          name: { type: "string", description: "Org unit name" },
          shortName: { type: "string", description: "Short name/code" },
          level: { type: "integer", description: "Level (5, 6, or 7)" },
          parentId: { type: "string", description: "Parent org unit ID (optional)" },
          sortOrder: { type: "integer", description: "Sort order. Default: 0" },
        },
        required: ["name", "level"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_org_unit",
      description: "Update an organizational unit. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Org unit ID" },
          name: { type: "string", description: "New name" },
          shortName: { type: "string", description: "New short name" },
          sortOrder: { type: "integer", description: "New sort order" },
          isActive: { type: "boolean", description: "Set active state" },
          parentId: { type: "string", description: "Parent org unit ID; use null to clear" },
        },
        required: ["id"],
      },
    },
  },
], {
  create_org_unit: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  update_org_unit: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export function createClientsOrgUnitsExecutors(
  deps: ClientsOrgUnitsDeps,
): Record<string, ToolExecutor> {
  return {
    async create_client(params: {
      name: string;
      code?: string;
      parentId?: string;
      sortOrder?: number;
      tags?: string[];
    }, ctx: ToolContext) {
      const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));

      let client;
      try {
        client = await caller.create(CreateClientSchema.parse(params));
      } catch (error) {
        const mapped = deps.toAssistantClientMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["client"],
        success: true,
        message: `Created client: ${client.name}`,
        clientId: client.id,
        client,
      };
    },

    async update_client(params: {
      id: string;
      name?: string;
      code?: string | null;
      sortOrder?: number;
      isActive?: boolean;
      parentId?: string | null;
      tags?: string[];
    }, ctx: ToolContext) {
      const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));
      const data = UpdateClientSchema.parse({
        ...(params.name !== undefined ? { name: params.name } : {}),
        ...(params.code !== undefined ? { code: params.code } : {}),
        ...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}),
        ...(params.isActive !== undefined ? { isActive: params.isActive } : {}),
        ...(params.parentId !== undefined ? { parentId: params.parentId } : {}),
        ...(params.tags !== undefined ? { tags: params.tags } : {}),
      });
      if (Object.keys(data).length === 0) {
        return { error: "No fields to update" };
      }

      let client;
      try {
        client = await caller.update({ id: params.id, data });
      } catch (error) {
        const mapped = deps.toAssistantClientMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["client"],
        success: true,
        message: `Updated client: ${client.name}`,
        clientId: client.id,
        client,
      };
    },

    async delete_client(params: { id: string }, ctx: ToolContext) {
      const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));

      let client;
      try {
        client = await caller.getById({ id: params.id });
        await caller.delete({ id: params.id });
      } catch (error) {
        const mapped = deps.toAssistantClientMutationError(error, "delete");
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["client"],
        success: true,
        message: `Deleted client: ${client.name}`,
      };
    },

    async create_org_unit(params: {
      name: string;
      shortName?: string;
      level: number;
      parentId?: string;
      sortOrder?: number;
    }, ctx: ToolContext) {
      const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx));

      let orgUnit;
      try {
        orgUnit = await caller.create(CreateOrgUnitSchema.parse(params));
      } catch (error) {
        const mapped = deps.toAssistantOrgUnitMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["orgUnit"],
        success: true,
        message: `Created org unit: ${orgUnit.name}`,
        orgUnitId: orgUnit.id,
        orgUnit,
      };
    },

    async update_org_unit(params: {
      id: string;
      name?: string;
      shortName?: string | null;
      sortOrder?: number;
      isActive?: boolean;
      parentId?: string | null;
    }, ctx: ToolContext) {
      const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx));
      const data = UpdateOrgUnitSchema.parse({
        ...(params.name !== undefined ? { name: params.name } : {}),
        ...(params.shortName !== undefined ? { shortName: params.shortName } : {}),
        ...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}),
        ...(params.isActive !== undefined ? { isActive: params.isActive } : {}),
        ...(params.parentId !== undefined ? { parentId: params.parentId } : {}),
      });
      if (Object.keys(data).length === 0) {
        return { error: "No fields to update" };
      }

      let orgUnit;
      try {
        orgUnit = await caller.update({ id: params.id, data });
      } catch (error) {
        const mapped = deps.toAssistantOrgUnitMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["orgUnit"],
        success: true,
        message: `Updated org unit: ${orgUnit.name}`,
        orgUnitId: orgUnit.id,
        orgUnit,
      };
    },
  };
}
