import { EstimateStatus, 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 ResolvedProject = {
  id: string;
};

type PlanningNavigationDeps = {
  createEstimateCaller: (ctx: TRPCContext) => {
    list: (params: {
      query?: string;
      status?: EstimateStatus;
      projectId?: string;
    }) => Promise<unknown>;
  };
  createClientCaller: (ctx: TRPCContext) => {
    list: (params: {
      isActive: boolean;
      search?: string;
    }) => Promise<Array<{
      id: string;
      name: string;
      code: string | null;
      _count: { projects: number };
    }>>;
  };
  createOrgUnitCaller: (ctx: TRPCContext) => {
    list: (params: {
      isActive: boolean;
      level?: number;
    }) => Promise<Array<{ id: string }>>;
    getById: (params: { id: string }) => Promise<{
      id: string;
      name: string;
      shortName: string | null;
      level: number;
      parent: { name: string } | null;
      _count: { resources: number };
    }>;
  };
  createTimelineCaller: (ctx: TRPCContext) => {
    getMyEntriesView: (params: {
      startDate: Date;
      endDate: Date;
      resourceIds?: string[];
      projectIds?: string[];
      clientIds?: string[];
      chapters?: string[];
      eids?: string[];
      countryCodes?: string[];
    }) => Promise<unknown>;
    getMyHolidayOverlays: (params: {
      startDate: Date;
      endDate: Date;
      resourceIds?: string[];
      projectIds?: string[];
      clientIds?: string[];
      chapters?: string[];
      eids?: string[];
      countryCodes?: string[];
    }) => Promise<unknown>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  resolveProjectIdentifier: (
    ctx: ToolContext,
    identifier: string,
  ) => Promise<ResolvedProject | AssistantToolErrorResult>;
  parseIsoDate: (value: string, fieldName: string) => Date;
};

export const planningNavigationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "search_estimates",
      description: "Search for estimates (cost/effort estimates) by project or name. Returns estimate name, status, version count.",
      parameters: {
        type: "object",
        properties: {
          projectCode: { type: "string", description: "Project short code to filter by" },
          query: { type: "string", description: "Search term (matches estimate name)" },
          status: { type: "string", description: "Filter by status: DRAFT, IN_REVIEW, APPROVED, ARCHIVED" },
          limit: { type: "integer", description: "Max results. Default: 20" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_clients",
      description: "List clients/customers. Can search by name or code.",
      parameters: {
        type: "object",
        properties: {
          query: { type: "string", description: "Search term (matches name or code)" },
          limit: { type: "integer", description: "Max results. Default: 20" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_org_units",
      description: "List organizational units (departments, teams) with their hierarchy.",
      parameters: {
        type: "object",
        properties: {
          level: { type: "integer", description: "Filter by org level (5, 6, or 7)" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_my_timeline_entries_view",
      description: "Get the caller's own self-service timeline entries view for a date range using the real timeline self-service endpoint. Returns only data for the caller's linked resource.",
      parameters: {
        type: "object",
        properties: {
          startDate: { type: "string", description: "Start date in YYYY-MM-DD." },
          endDate: { type: "string", description: "End date in YYYY-MM-DD." },
          resourceIds: { type: "array", items: { type: "string" }, description: "Optional filters are accepted but will be scoped to the caller's own linked resource." },
          projectIds: { type: "array", items: { type: "string" }, description: "Optional project IDs to narrow the caller's own timeline view." },
          clientIds: { type: "array", items: { type: "string" }, description: "Optional client IDs to narrow the caller's own timeline view." },
          chapters: { type: "array", items: { type: "string" }, description: "Optional chapter filters. Self-service scoping still applies." },
          eids: { type: "array", items: { type: "string" }, description: "Optional employee IDs. Self-service scoping still applies." },
          countryCodes: { type: "array", items: { type: "string" }, description: "Optional country codes. Self-service scoping still applies." },
        },
        required: ["startDate", "endDate"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_my_timeline_holiday_overlays",
      description: "Get the caller's own self-service holiday overlays for a date range using the real timeline self-service endpoint. Returns only holiday overlays for the caller's linked resource.",
      parameters: {
        type: "object",
        properties: {
          startDate: { type: "string", description: "Start date in YYYY-MM-DD." },
          endDate: { type: "string", description: "End date in YYYY-MM-DD." },
          resourceIds: { type: "array", items: { type: "string" }, description: "Optional filters are accepted but will be scoped to the caller's own linked resource." },
          projectIds: { type: "array", items: { type: "string" }, description: "Optional project IDs to narrow the caller's own holiday overlay view." },
          clientIds: { type: "array", items: { type: "string" }, description: "Optional client IDs to narrow the caller's own holiday overlay view." },
          chapters: { type: "array", items: { type: "string" }, description: "Optional chapter filters. Self-service scoping still applies." },
          eids: { type: "array", items: { type: "string" }, description: "Optional employee IDs. Self-service scoping still applies." },
          countryCodes: { type: "array", items: { type: "string" }, description: "Optional country codes. Self-service scoping still applies." },
        },
        required: ["startDate", "endDate"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "navigate_to_page",
      description: "Navigate the user to a specific page in CapaKraken, optionally with filters. Use this when the user wants to see data on a specific page (e.g. 'show me on the timeline', 'open the resources page').",
      parameters: {
        type: "object",
        properties: {
          page: {
            type: "string",
            description: "Page name: timeline, dashboard, resources, projects, allocations, staffing, estimates, vacations, my-vacations, roles, skills-analytics, chargeability, computation-graph",
          },
          eids: { type: "string", description: "Comma-separated employee IDs to filter (for timeline)" },
          chapters: { type: "string", description: "Comma-separated chapters to filter (for timeline)" },
          projectIds: { type: "string", description: "Comma-separated project IDs to filter (for timeline)" },
          clientIds: { type: "string", description: "Comma-separated client IDs to filter (for timeline)" },
          countryCodes: { type: "string", description: "Comma-separated country codes to filter (e.g. 'ES,DE' for Spain and Germany, for timeline)" },
          startDate: { type: "string", description: "Start date YYYY-MM-DD (for timeline)" },
          days: { type: "integer", description: "Number of days to show (for timeline)" },
        },
        required: ["page"],
      },
    },
  },
], {
  search_estimates: {
    allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
  },
  list_clients: {
    requiresPlanningRead: true,
  },
  list_org_units: {
    requiresResourceOverview: true,
  },
});

export function createPlanningNavigationExecutors(
  deps: PlanningNavigationDeps,
): Record<string, ToolExecutor> {
  return {
    async search_estimates(
      params: {
        projectCode?: string;
        query?: string;
        status?: string;
        limit?: number;
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createEstimateCaller(deps.createScopedCallerContext(ctx));
      let projectId: string | undefined;
      if (params.projectCode) {
        const project = await deps.resolveProjectIdentifier(ctx, params.projectCode);
        if ("error" in project) {
          return project;
        }
        projectId = project.id;
      }

      return caller.list({
        ...(params.query ? { query: params.query } : {}),
        ...(params.status ? { status: params.status as EstimateStatus } : {}),
        ...(projectId ? { projectId } : {}),
      });
    },

    async list_clients(
      params: { query?: string; limit?: number },
      ctx: ToolContext,
    ) {
      const limit = Math.min(params.limit ?? 20, 50);
      const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));
      const clients = await caller.list({
        isActive: true,
        ...(params.query ? { search: params.query } : {}),
      });

      return clients.slice(0, limit).map((client) => ({
        id: client.id,
        name: client.name,
        code: client.code,
        projectCount: client._count.projects,
      }));
    },

    async list_org_units(
      params: { level?: number },
      ctx: ToolContext,
    ) {
      const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx));
      const units = await caller.list({
        isActive: true,
        ...(params.level !== undefined ? { level: params.level } : {}),
      });
      const details = await Promise.all(units.map((unit) => caller.getById({ id: unit.id })));

      return details.map((unit) => ({
        id: unit.id,
        name: unit.name,
        shortName: unit.shortName,
        level: unit.level,
        parent: unit.parent?.name ?? null,
        resourceCount: unit._count.resources,
      }));
    },

    async get_my_timeline_entries_view(
      params: {
        startDate: string;
        endDate: string;
        resourceIds?: string[];
        projectIds?: string[];
        clientIds?: string[];
        chapters?: string[];
        eids?: string[];
        countryCodes?: string[];
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createTimelineCaller(deps.createScopedCallerContext(ctx));
      return caller.getMyEntriesView({
        startDate: deps.parseIsoDate(params.startDate, "startDate"),
        endDate: deps.parseIsoDate(params.endDate, "endDate"),
        ...(params.resourceIds ? { resourceIds: params.resourceIds } : {}),
        ...(params.projectIds ? { projectIds: params.projectIds } : {}),
        ...(params.clientIds ? { clientIds: params.clientIds } : {}),
        ...(params.chapters ? { chapters: params.chapters } : {}),
        ...(params.eids ? { eids: params.eids } : {}),
        ...(params.countryCodes ? { countryCodes: params.countryCodes } : {}),
      });
    },

    async get_my_timeline_holiday_overlays(
      params: {
        startDate: string;
        endDate: string;
        resourceIds?: string[];
        projectIds?: string[];
        clientIds?: string[];
        chapters?: string[];
        eids?: string[];
        countryCodes?: string[];
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createTimelineCaller(deps.createScopedCallerContext(ctx));
      return caller.getMyHolidayOverlays({
        startDate: deps.parseIsoDate(params.startDate, "startDate"),
        endDate: deps.parseIsoDate(params.endDate, "endDate"),
        ...(params.resourceIds ? { resourceIds: params.resourceIds } : {}),
        ...(params.projectIds ? { projectIds: params.projectIds } : {}),
        ...(params.clientIds ? { clientIds: params.clientIds } : {}),
        ...(params.chapters ? { chapters: params.chapters } : {}),
        ...(params.eids ? { eids: params.eids } : {}),
        ...(params.countryCodes ? { countryCodes: params.countryCodes } : {}),
      });
    },

    async navigate_to_page(
      params: {
        page: string;
        eids?: string;
        chapters?: string;
        projectIds?: string;
        clientIds?: string;
        countryCodes?: string;
        startDate?: string;
        days?: number;
      },
      _ctx: ToolContext,
    ) {
      const pageMap: Record<string, string> = {
        timeline: "/timeline",
        dashboard: "/dashboard",
        resources: "/resources",
        projects: "/projects",
        allocations: "/allocations",
        staffing: "/staffing",
        estimates: "/estimates",
        vacations: "/vacations",
        "my-vacations": "/vacations/my",
        roles: "/roles",
        "skills-analytics": "/analytics/skills",
        chargeability: "/reports/chargeability",
        "computation-graph": "/analytics/computation-graph",
      };
      const path = pageMap[params.page];
      if (!path) {
        return { error: `Unknown page: ${params.page}. Available: ${Object.keys(pageMap).join(", ")}` };
      }

      const queryParts: string[] = [];
      if (params.eids) queryParts.push(`eids=${encodeURIComponent(params.eids)}`);
      if (params.chapters) queryParts.push(`chapters=${encodeURIComponent(params.chapters)}`);
      if (params.projectIds) queryParts.push(`projectIds=${encodeURIComponent(params.projectIds)}`);
      if (params.clientIds) queryParts.push(`clientIds=${encodeURIComponent(params.clientIds)}`);
      if (params.countryCodes) queryParts.push(`countryCodes=${encodeURIComponent(params.countryCodes)}`);
      if (params.startDate) queryParts.push(`startDate=${encodeURIComponent(params.startDate)}`);
      if (params.days) queryParts.push(`days=${params.days}`);

      const url = queryParts.length > 0 ? `${path}?${queryParts.join("&")}` : path;

      return {
        __action: "navigate" as const,
        url,
        description: `Navigiere zu ${path}`,
      };
    },
  };
}
