import { CreateResourceSchema, PermissionKey, UpdateResourceSchema } 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 ResolvedResource = {
  id: string;
  eid: string;
  displayName: string;
};

type ResolvedReference = {
  id: string;
};

type ResourceRecord = {
  id: string;
  eid: string;
  displayName: string;
};

type ParsedCreateResourceInput = ReturnType<typeof CreateResourceSchema.parse>;
type ParsedUpdateResourceInput = ReturnType<typeof UpdateResourceSchema.parse>;

type ResourceToolsDeps = {
  assertPermission: (ctx: ToolContext, perm: PermissionKey) => void;
  createResourceCaller: (ctx: TRPCContext) => {
    listSummariesDetail: (params: {
      search?: string;
      country?: string;
      metroCity?: string;
      orgUnit?: string;
      roleName?: string;
      isActive?: boolean;
      limit: number;
    }) => Promise<unknown>;
    getByIdentifierDetail: (params: { identifier: string }) => Promise<unknown>;
    create: (params: ParsedCreateResourceInput) => Promise<ResourceRecord>;
    update: (params: {
      id: string;
      data: ParsedUpdateResourceInput;
    }) => Promise<ResourceRecord>;
    deactivate: (params: { id: string }) => Promise<unknown>;
  };
  createRoleCaller: (ctx: TRPCContext) => {
    resolveByIdentifier: (params: { identifier: string }) => Promise<ResolvedReference>;
  };
  createCountryCaller: (ctx: TRPCContext) => {
    resolveByIdentifier: (params: { identifier: string }) => Promise<ResolvedReference>;
  };
  createOrgUnitCaller: (ctx: TRPCContext) => {
    resolveByIdentifier: (params: { identifier: string }) => Promise<ResolvedReference>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  resolveResourceIdentifier: (
    ctx: ToolContext,
    identifier: string,
  ) => Promise<ResolvedResource | AssistantToolErrorResult>;
  resolveEntityOrAssistantError: <T>(
    resolve: () => Promise<T>,
    notFoundMessage: string,
  ) => Promise<T | AssistantToolErrorResult>;
  toAssistantResourceMutationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantResourceCreationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const resourceReadToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "search_resources",
      description: "Search for resources (employees) by name, employee ID, chapter, country, metro city, org unit, or role. Resource overview access required. Returns a list of matching resources with key details.",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Search term (matches displayName, eid, chapter)" },
          country: { type: "string", description: "Filter by country name or code (e.g. 'Spain', 'ES', 'Deutschland', 'DE')" },
          metroCity: { type: "string", description: "Filter by metro city name (e.g. 'Madrid', 'München')" },
          orgUnit: { type: "string", description: "Filter by org unit name (partial match)" },
          roleName: { type: "string", description: "Filter by role name (partial match)" },
          isActive: { type: "boolean", description: "Filter by active status. Default: true" },
          limit: { type: "integer", description: "Max results. Default: 50" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_resource",
      description: "Get detailed information about a single resource by ID, employee ID (eid), or name.",
      parameters: {
        type: "object",
        properties: {
          identifier: { type: "string", description: "Resource ID, employee ID (eid like EMP-001), or display name" },
        },
        required: ["identifier"],
      },
    },
  },
], {
  search_resources: {
    requiresResourceOverview: true,
  },
});

export const resourceMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "update_resource",
      description: "Update a resource's details. Requires manageResources permission. Always confirm with the user before calling this.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Resource ID, EID, or display name" },
          displayName: { type: "string", description: "New display name" },
          fte: { type: "number", description: "New FTE (0.0-1.0)" },
          lcrCents: { type: "integer", description: "New LCR in cents (e.g. 8500 = 85.00 EUR/h)" },
          chapter: { type: "string", description: "New chapter" },
          chargeabilityTarget: { type: "number", description: "New chargeability target (0-100)" },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_resource",
      description: "Create a new resource/employee. Requires manageResources permission. Always confirm with the user before calling this.",
      parameters: {
        type: "object",
        properties: {
          eid: { type: "string", description: "Employee ID (e.g. EMP-001)" },
          displayName: { type: "string", description: "Full name" },
          email: { type: "string", description: "Email address" },
          fte: { type: "number", description: "Full-time equivalent 0.0-1.0. Default: 1.0" },
          lcrCents: { type: "integer", description: "Labor cost rate in cents/hour (e.g. 8500 = 85.00 EUR/h)" },
          ucrCents: { type: "integer", description: "Utilization cost rate in cents/hour (optional; defaults to 70% of LCR)" },
          chapter: { type: "string", description: "Chapter/department" },
          chargeabilityTarget: { type: "number", description: "Target utilization percentage 0-100. Default: 80" },
          roleName: { type: "string", description: "Role name to assign (e.g. 'Designer')" },
          countryCode: { type: "string", description: "Country code or name (e.g. 'DE', 'Germany')" },
          orgUnitName: { type: "string", description: "Organizational unit name" },
          postalCode: { type: "string", description: "Postal code. If provided without federalState, state may be inferred." },
        },
        required: ["eid", "displayName", "lcrCents"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "deactivate_resource",
      description: "Deactivate a resource (soft delete). Requires manageResources permission. Always confirm with the user before calling this.",
      parameters: {
        type: "object",
        properties: {
          identifier: { type: "string", description: "Resource ID, EID, or display name" },
        },
        required: ["identifier"],
      },
    },
  },
], {
  update_resource: {
    requiredPermissions: [PermissionKey.MANAGE_RESOURCES],
  },
  create_resource: {
    requiredPermissions: [PermissionKey.MANAGE_RESOURCES],
  },
  deactivate_resource: {
    requiredPermissions: [PermissionKey.MANAGE_RESOURCES],
  },
});

export function createResourceExecutors(
  deps: ResourceToolsDeps,
): Record<string, ToolExecutor> {
  return {
    async search_resources(
      params: {
        query?: string;
        country?: string;
        metroCity?: string;
        orgUnit?: string;
        roleName?: string;
        isActive?: boolean;
        limit?: number;
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createResourceCaller(deps.createScopedCallerContext(ctx));
      return caller.listSummariesDetail({
        ...(params.query !== undefined ? { search: params.query } : {}),
        ...(params.country !== undefined ? { country: params.country } : {}),
        ...(params.metroCity !== undefined ? { metroCity: params.metroCity } : {}),
        ...(params.orgUnit !== undefined ? { orgUnit: params.orgUnit } : {}),
        ...(params.roleName !== undefined ? { roleName: params.roleName } : {}),
        isActive: params.isActive ?? true,
        limit: Math.min(params.limit ?? 50, 100),
      });
    },

    async get_resource(params: { identifier: string }, ctx: ToolContext) {
      const caller = deps.createResourceCaller(deps.createScopedCallerContext(ctx));
      return caller.getByIdentifierDetail({ identifier: params.identifier });
    },

    async update_resource(
      params: {
        id: string;
        displayName?: string;
        fte?: number;
        lcrCents?: number;
        chapter?: string;
        chargeabilityTarget?: number;
      },
      ctx: ToolContext,
    ) {
      deps.assertPermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const resource = await deps.resolveResourceIdentifier(ctx, params.id);
      if ("error" in resource) {
        return resource;
      }

      const caller = deps.createResourceCaller(deps.createScopedCallerContext(ctx));
      const data = UpdateResourceSchema.parse({
        ...(params.displayName !== undefined ? { displayName: params.displayName } : {}),
        ...(params.fte !== undefined ? { fte: params.fte } : {}),
        ...(params.lcrCents !== undefined ? { lcrCents: params.lcrCents } : {}),
        ...(params.chapter !== undefined ? { chapter: params.chapter } : {}),
        ...(params.chargeabilityTarget !== undefined
          ? { chargeabilityTarget: params.chargeabilityTarget }
          : {}),
      });

      const updatedFields = Object.keys(data);
      if (updatedFields.length === 0) {
        return { error: "No fields to update" };
      }

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

      return {
        __action: "invalidate",
        scope: ["resource"],
        success: true,
        message: `Updated resource ${updated.displayName} (${updated.eid})`,
        updatedFields,
      };
    },

    async create_resource(
      params: {
        eid: string;
        displayName: string;
        email?: string;
        fte?: number;
        lcrCents: number;
        ucrCents?: number;
        chapter?: string;
        chargeabilityTarget?: number;
        roleName?: string;
        countryCode?: string;
        orgUnitName?: string;
        postalCode?: string;
      },
      ctx: ToolContext,
    ) {
      deps.assertPermission(ctx, PermissionKey.MANAGE_RESOURCES);
      if (!params.email?.trim()) {
        return { error: "email is required to create a resource." };
      }

      const scopedContext = deps.createScopedCallerContext(ctx);
      const roleCaller = deps.createRoleCaller(scopedContext);
      const countryCaller = deps.createCountryCaller(scopedContext);
      const orgUnitCaller = deps.createOrgUnitCaller(scopedContext);

      const data: Record<string, unknown> = {
        eid: params.eid,
        displayName: params.displayName,
        email: params.email,
        lcrCents: params.lcrCents,
        ucrCents: params.ucrCents ?? Math.round(params.lcrCents * 0.7),
        chargeabilityTarget: params.chargeabilityTarget ?? 80,
        currency: "EUR",
        availability: {
          monday: 8,
          tuesday: 8,
          wednesday: 8,
          thursday: 8,
          friday: 8,
        },
        skills: [],
        dynamicFields: {},
        ...(params.fte !== undefined ? { fte: params.fte } : {}),
        ...(params.chapter ? { chapter: params.chapter } : {}),
        ...(params.postalCode ? { postalCode: params.postalCode } : {}),
      };

      const roleIdentifier = params.roleName?.trim();
      if (roleIdentifier) {
        const role = await deps.resolveEntityOrAssistantError(
          () => roleCaller.resolveByIdentifier({ identifier: roleIdentifier }),
          `Role not found: "${roleIdentifier}"`,
        );
        if ("error" in role) {
          return role;
        }
        data.roleId = role.id;
      }

      const countryIdentifier = params.countryCode?.trim();
      if (countryIdentifier) {
        const country = await deps.resolveEntityOrAssistantError(
          () => countryCaller.resolveByIdentifier({ identifier: countryIdentifier }),
          `Country not found: "${countryIdentifier}"`,
        );
        if ("error" in country) {
          return country;
        }
        data.countryId = country.id;
      }

      const orgUnitIdentifier = params.orgUnitName?.trim();
      if (orgUnitIdentifier) {
        const orgUnit = await deps.resolveEntityOrAssistantError(
          () => orgUnitCaller.resolveByIdentifier({ identifier: orgUnitIdentifier }),
          `Org unit not found: "${orgUnitIdentifier}"`,
        );
        if ("error" in orgUnit) {
          return orgUnit;
        }
        data.orgUnitId = orgUnit.id;
      }

      const input = CreateResourceSchema.parse(data);
      const caller = deps.createResourceCaller(scopedContext);
      let resource;
      try {
        resource = await caller.create(input);
      } catch (error) {
        const mapped = deps.toAssistantResourceCreationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["resource"],
        success: true,
        message: `Created resource: ${resource.displayName} (${resource.eid})`,
        resourceId: resource.id,
      };
    },

    async deactivate_resource(
      params: { identifier: string },
      ctx: ToolContext,
    ) {
      deps.assertPermission(ctx, PermissionKey.MANAGE_RESOURCES);
      const resource = await deps.resolveResourceIdentifier(ctx, params.identifier);
      if ("error" in resource) {
        return resource;
      }

      const caller = deps.createResourceCaller(deps.createScopedCallerContext(ctx));
      try {
        await caller.deactivate({ id: resource.id });
      } catch (error) {
        const mapped = deps.toAssistantResourceMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate",
        scope: ["resource"],
        success: true,
        message: `Deactivated resource: ${resource.displayName} (${resource.eid})`,
      };
    },
  };
}
