import type { TRPCContext } from "../../trpc.js";
import {
  CreateHolidayCalendarEntrySchema,
  CreateHolidayCalendarSchema,
  PreviewResolvedHolidaysSchema,
  SystemRole,
  UpdateHolidayCalendarEntrySchema,
  UpdateHolidayCalendarSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";

type AssistantToolErrorResult = { error: string };

type ResolvedResource = {
  id: string;
};

type HolidayCalendarEntryRecord = {
  id: string;
  date: Date;
  name: string;
  isRecurringAnnual?: boolean | null;
  source?: string | null;
};

type HolidayCalendarRecord = {
  id: string;
  name: string;
  scopeType: string;
  stateCode?: string | null;
  isActive?: boolean | null;
  priority?: number | null;
  country?: { id: string; code: string; name: string } | null;
  metroCity?: { id: string; name: string } | null;
  _count?: { entries?: number | null } | null;
  entries?: HolidayCalendarEntryRecord[] | null;
};

type VacationHolidayDeps = {
  createEntitlementCaller: (ctx: TRPCContext) => {
    getBalanceDetail: (params: { resourceId: string; year: number }) => Promise<unknown>;
  };
  createVacationCaller: (ctx: TRPCContext) => {
    list: (params: {
      status: "APPROVED";
      startDate: Date;
      endDate: Date;
      limit: number;
    }) => Promise<Array<{
      type: string;
      startDate: Date;
      endDate: Date;
      isHalfDay?: boolean | null;
      halfDayPart?: string | null;
      resource: {
        displayName: string;
        eid: string;
        chapter?: string | null;
      };
    }>>;
  };
  createHolidayCalendarCaller: (ctx: TRPCContext) => {
    resolveHolidaysDetail: (params: {
      periodStart: Date;
      periodEnd: Date;
      countryCode: string;
      stateCode?: string;
      metroCityName?: string;
    }) => Promise<{
      locationContext: unknown;
      periodStart: string;
      periodEnd: string;
      count: number;
      summary: unknown;
      holidays: unknown[];
    }>;
    resolveResourceHolidaysDetail: (params: {
      resourceId: string;
      periodStart: Date;
      periodEnd: Date;
    }) => Promise<{
      resource: unknown;
      periodStart: string;
      periodEnd: string;
      count: number;
      summary: unknown;
      holidays: unknown[];
    }>;
    listCalendarsDetail: (params: {
      includeInactive?: boolean;
      countryCode?: string;
      scopeType?: "COUNTRY" | "STATE" | "CITY";
      stateCode?: string;
      metroCity?: string;
    }) => Promise<unknown>;
    getCalendarByIdentifierDetail: (params: { identifier: string }) => Promise<unknown>;
    previewResolvedHolidaysDetail: (
      params: z.input<typeof PreviewResolvedHolidaysSchema>,
    ) => Promise<unknown>;
    createCalendar: (
      params: z.input<typeof CreateHolidayCalendarSchema>,
    ) => Promise<HolidayCalendarRecord>;
    updateCalendar: (params: {
      id: string;
      data: z.input<typeof UpdateHolidayCalendarSchema>;
    }) => Promise<HolidayCalendarRecord>;
    deleteCalendar: (params: { id: string }) => Promise<{ name: string }>;
    createEntry: (
      params: z.input<typeof CreateHolidayCalendarEntrySchema>,
    ) => Promise<HolidayCalendarEntryRecord>;
    updateEntry: (params: {
      id: string;
      data: z.input<typeof UpdateHolidayCalendarEntrySchema>;
    }) => Promise<HolidayCalendarEntryRecord>;
    deleteEntry: (params: { id: string }) => Promise<{ name: string }>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  resolveResourceIdentifier: (
    ctx: ToolContext,
    identifier: string,
  ) => Promise<ResolvedResource | AssistantToolErrorResult>;
  resolveHolidayPeriod: (input: {
    year?: number;
    periodStart?: string;
    periodEnd?: string;
  }) => { year: number | null; periodStart: Date; periodEnd: Date };
  resolveEntityOrAssistantError: <T>(
    resolve: () => Promise<T>,
    notFoundMessage: string,
  ) => Promise<T | AssistantToolErrorResult>;
  assertAdminRole: (ctx: ToolContext) => void;
  fmtDate: (value: Date | null | undefined) => string | null;
  formatHolidayCalendar: (calendar: HolidayCalendarRecord) => unknown;
  formatHolidayCalendarEntry: (entry: HolidayCalendarEntryRecord) => unknown;
  toAssistantHolidayCalendarMutationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantHolidayCalendarNotFoundError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantHolidayEntryMutationError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
  toAssistantHolidayEntryNotFoundError: (
    error: unknown,
  ) => AssistantToolErrorResult | null;
};

export const vacationHolidayReadToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "get_vacation_balance",
      description: "Get the holiday-aware vacation balance for a resource via the real entitlement workflow. Authenticated users can read their own balance; manager/admin/controller can read broader balances.",
      parameters: {
        type: "object",
        properties: {
          resourceId: { type: "string", description: "Resource ID, EID, or display name" },
          year: { type: "integer", description: "Year. Default: current year" },
        },
        required: ["resourceId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_vacations_upcoming",
      description: "List upcoming vacations across all resources, or for a specific resource/team. Shows who is off when.",
      parameters: {
        type: "object",
        properties: {
          resourceName: { type: "string", description: "Filter by resource name (partial match)" },
          chapter: { type: "string", description: "Filter by chapter/team" },
          daysAhead: { type: "integer", description: "How many days ahead to look. Default: 30" },
          limit: { type: "integer", description: "Max results. Default: 30" },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_holidays_by_region",
      description: "List resolved public holidays for a country, federal state, and optionally a city in a given year or date range. Use this to compare regions such as Bayern vs Hamburg.",
      parameters: {
        type: "object",
        properties: {
          countryCode: { type: "string", description: "Country code such as DE, ES, US, IN." },
          federalState: { type: "string", description: "Federal state / region code, e.g. BY, HH, NRW." },
          metroCity: { type: "string", description: "Optional city name for local city-specific holidays, e.g. Augsburg." },
          year: { type: "integer", description: "Full year, e.g. 2026. Default: current year." },
          periodStart: { type: "string", description: "Optional start date in YYYY-MM-DD. Requires periodEnd." },
          periodEnd: { type: "string", description: "Optional end date in YYYY-MM-DD. Requires periodStart." },
        },
        required: ["countryCode"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_resource_holidays",
      description: "List resolved public holidays for a specific resource based on that person's country, federal state, and city context.",
      parameters: {
        type: "object",
        properties: {
          identifier: { type: "string", description: "Resource ID, EID, or display name." },
          year: { type: "integer", description: "Full year, e.g. 2026. Default: current year." },
          periodStart: { type: "string", description: "Optional start date in YYYY-MM-DD. Requires periodEnd." },
          periodEnd: { type: "string", description: "Optional end date in YYYY-MM-DD. Requires periodStart." },
        },
        required: ["identifier"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_holiday_calendars",
      description: "List holiday calendars including scope, assignment, active flag, priority, and entry count. Useful to inspect the calendar-editor configuration context.",
      parameters: {
        type: "object",
        properties: {
          includeInactive: { type: "boolean", description: "Include inactive calendars. Default: false." },
          countryCode: { type: "string", description: "Optional country code filter such as DE or ES." },
          scopeType: { type: "string", description: "Optional scope filter: COUNTRY, STATE, CITY." },
          stateCode: { type: "string", description: "Optional state/region code filter such as BY or NRW." },
          metroCity: { type: "string", description: "Optional city-name filter." },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_holiday_calendar",
      description: "Get a single holiday calendar including all entries. Accepts either the calendar ID or its name.",
      parameters: {
        type: "object",
        properties: {
          identifier: { type: "string", description: "Holiday calendar ID or name." },
        },
        required: ["identifier"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "preview_resolved_holiday_calendar",
      description: "Preview the resolved holiday result for a country/state/city scope and year, including which calendar each holiday comes from.",
      parameters: {
        type: "object",
        properties: {
          countryId: { type: "string", description: "Country ID." },
          stateCode: { type: "string", description: "Optional state/region code." },
          metroCityId: { type: "string", description: "Optional metro city ID for city-specific preview." },
          year: { type: "integer", description: "Full year, e.g. 2026." },
        },
        required: ["countryId", "year"],
      },
    },
  },
], {
  list_holiday_calendars: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  get_holiday_calendar: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export const vacationHolidayMutationToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "create_holiday_calendar",
      description: "Create a holiday calendar for a country, state, or city scope. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          name: { type: "string", description: "Calendar name." },
          scopeType: { type: "string", description: "COUNTRY, STATE, or CITY." },
          countryId: { type: "string", description: "Country ID." },
          stateCode: { type: "string", description: "Required for STATE calendars." },
          metroCityId: { type: "string", description: "Required for CITY calendars." },
          isActive: { type: "boolean", description: "Whether the calendar is active. Default: true." },
          priority: { type: "integer", description: "Priority used during calendar resolution. Default: 0." },
        },
        required: ["name", "scopeType", "countryId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_holiday_calendar",
      description: "Update an existing holiday calendar. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Holiday calendar ID." },
          data: {
            type: "object",
            properties: {
              name: { type: "string" },
              stateCode: { type: "string" },
              metroCityId: { type: "string" },
              isActive: { type: "boolean" },
              priority: { type: "integer" },
            },
          },
        },
        required: ["id", "data"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "delete_holiday_calendar",
      description: "Delete a holiday calendar and all of its entries. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Holiday calendar ID." },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "create_holiday_calendar_entry",
      description: "Create a holiday entry in an existing calendar. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          holidayCalendarId: { type: "string", description: "Holiday calendar ID." },
          date: { type: "string", description: "Date in YYYY-MM-DD format." },
          name: { type: "string", description: "Holiday name." },
          isRecurringAnnual: { type: "boolean", description: "Whether the holiday repeats every year." },
          source: { type: "string", description: "Optional source or legal basis." },
        },
        required: ["holidayCalendarId", "date", "name"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "update_holiday_calendar_entry",
      description: "Update an existing holiday calendar entry. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Holiday calendar entry ID." },
          data: {
            type: "object",
            properties: {
              date: { type: "string", description: "Date in YYYY-MM-DD format." },
              name: { type: "string" },
              isRecurringAnnual: { type: "boolean" },
              source: { type: "string" },
            },
          },
        },
        required: ["id", "data"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "delete_holiday_calendar_entry",
      description: "Delete a holiday calendar entry. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Holiday calendar entry ID." },
        },
        required: ["id"],
      },
    },
  },
], {
  create_holiday_calendar: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  update_holiday_calendar: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  delete_holiday_calendar: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  create_holiday_calendar_entry: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  update_holiday_calendar_entry: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
  delete_holiday_calendar_entry: {
    allowedSystemRoles: [SystemRole.ADMIN],
  },
});

export function createVacationHolidayExecutors(
  deps: VacationHolidayDeps,
): Record<string, ToolExecutor> {
  return {
    async get_vacation_balance(params: { resourceId: string; year?: number }, ctx: ToolContext) {
      const year = params.year ?? new Date().getFullYear();
      const resource = await deps.resolveResourceIdentifier(ctx, params.resourceId);
      if ("error" in resource) {
        return resource;
      }

      const caller = deps.createEntitlementCaller(deps.createScopedCallerContext(ctx));
      return caller.getBalanceDetail({ resourceId: resource.id, year });
    },

    async list_vacations_upcoming(params: {
      resourceName?: string;
      chapter?: string;
      daysAhead?: number;
      limit?: number;
    }, ctx: ToolContext) {
      const daysAhead = params.daysAhead ?? 30;
      const limit = Math.min(params.limit ?? 30, 50);
      const caller = deps.createVacationCaller(deps.createScopedCallerContext(ctx));
      const now = new Date();
      const until = new Date(now.getTime() + daysAhead * 24 * 60 * 60 * 1000);

      const vacations = await caller.list({
        status: "APPROVED",
        startDate: now,
        endDate: until,
        limit,
      });

      return vacations
        .filter((vacation) => {
          if (params.resourceName) {
            const resourceName = vacation.resource.displayName.toLowerCase();
            if (!resourceName.includes(params.resourceName.toLowerCase())) {
              return false;
            }
          }
          if (params.chapter) {
            const chapter = vacation.resource.chapter?.toLowerCase() ?? "";
            if (!chapter.includes(params.chapter.toLowerCase())) {
              return false;
            }
          }
          return true;
        })
        .slice(0, limit)
        .map((vacation) => ({
          resource: vacation.resource.displayName,
          eid: vacation.resource.eid,
          chapter: vacation.resource.chapter ?? null,
          type: vacation.type,
          start: deps.fmtDate(vacation.startDate),
          end: deps.fmtDate(vacation.endDate),
          isHalfDay: vacation.isHalfDay,
          halfDayPart: vacation.halfDayPart,
        }));
    },

    async list_holidays_by_region(params: {
      countryCode: string;
      federalState?: string;
      metroCity?: string;
      year?: number;
      periodStart?: string;
      periodEnd?: string;
    }, ctx: ToolContext) {
      const { year, periodStart, periodEnd } = deps.resolveHolidayPeriod(params);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      const resolved = await caller.resolveHolidaysDetail({
        periodStart,
        periodEnd,
        countryCode: params.countryCode.trim().toUpperCase(),
        ...(params.federalState ? { stateCode: params.federalState } : {}),
        ...(params.metroCity ? { metroCityName: params.metroCity } : {}),
      });

      return {
        locationContext: resolved.locationContext,
        year,
        periodStart: resolved.periodStart,
        periodEnd: resolved.periodEnd,
        count: resolved.count,
        summary: resolved.summary,
        holidays: resolved.holidays,
      };
    },

    async get_resource_holidays(params: {
      identifier: string;
      year?: number;
      periodStart?: string;
      periodEnd?: string;
    }, ctx: ToolContext) {
      const resource = await deps.resolveResourceIdentifier(ctx, params.identifier);
      if ("error" in resource) {
        return resource;
      }

      const { year, periodStart, periodEnd } = deps.resolveHolidayPeriod(params);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      const resolved = await caller.resolveResourceHolidaysDetail({
        resourceId: resource.id,
        periodStart,
        periodEnd,
      });

      return {
        resource: resolved.resource,
        year,
        periodStart: resolved.periodStart,
        periodEnd: resolved.periodEnd,
        count: resolved.count,
        summary: resolved.summary,
        holidays: resolved.holidays,
      };
    },

    async list_holiday_calendars(params: {
      includeInactive?: boolean;
      countryCode?: string;
      scopeType?: "COUNTRY" | "STATE" | "CITY";
      stateCode?: string;
      metroCity?: string;
    }, ctx: ToolContext) {
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      return caller.listCalendarsDetail(params);
    },

    async get_holiday_calendar(params: { identifier: string }, ctx: ToolContext) {
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      const identifier = params.identifier.trim();
      return deps.resolveEntityOrAssistantError(
        () => caller.getCalendarByIdentifierDetail({ identifier }),
        `Holiday calendar not found: ${identifier}`,
      );
    },

    async preview_resolved_holiday_calendar(params: {
      countryId: string;
      stateCode?: string;
      metroCityId?: string;
      year: number;
    }, ctx: ToolContext) {
      const input = PreviewResolvedHolidaysSchema.parse(params);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      return deps.resolveEntityOrAssistantError(
        () => caller.previewResolvedHolidaysDetail(input),
        "Country not found with the given criteria.",
      );
    },

    async create_holiday_calendar(params: {
      name: string;
      scopeType: "COUNTRY" | "STATE" | "CITY";
      countryId: string;
      stateCode?: string;
      metroCityId?: string;
      isActive?: boolean;
      priority?: number;
    }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));

      let created;
      try {
        created = await caller.createCalendar(CreateHolidayCalendarSchema.parse(params));
      } catch (error) {
        const mapped = deps.toAssistantHolidayCalendarMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        calendar: deps.formatHolidayCalendar(created),
        message: `Created holiday calendar: ${created.name}`,
      };
    },

    async update_holiday_calendar(params: {
      id: string;
      data: {
        name?: string;
        stateCode?: string | null;
        metroCityId?: string | null;
        isActive?: boolean;
        priority?: number;
      };
    }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      const input = {
        id: params.id,
        data: UpdateHolidayCalendarSchema.parse(params.data),
      };

      let updated;
      try {
        updated = await caller.updateCalendar(input);
      } catch (error) {
        const mapped = deps.toAssistantHolidayCalendarMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        calendar: deps.formatHolidayCalendar(updated),
        message: `Updated holiday calendar: ${updated.name}`,
      };
    },

    async delete_holiday_calendar(params: { id: string }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));

      let deleted;
      try {
        deleted = await caller.deleteCalendar({ id: params.id });
      } catch (error) {
        const mapped = deps.toAssistantHolidayCalendarNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        message: `Deleted holiday calendar: ${deleted.name}`,
      };
    },

    async create_holiday_calendar_entry(params: {
      holidayCalendarId: string;
      date: string;
      name: string;
      isRecurringAnnual?: boolean;
      source?: string;
    }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));

      let created;
      try {
        created = await caller.createEntry(CreateHolidayCalendarEntrySchema.parse(params));
      } catch (error) {
        const mapped = deps.toAssistantHolidayEntryMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        entry: deps.formatHolidayCalendarEntry(created),
        message: `Created holiday entry: ${created.name}`,
      };
    },

    async update_holiday_calendar_entry(params: {
      id: string;
      data: {
        date?: string;
        name?: string;
        isRecurringAnnual?: boolean;
        source?: string | null;
      };
    }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));
      const input = {
        id: params.id,
        data: UpdateHolidayCalendarEntrySchema.parse(params.data),
      };

      let updated;
      try {
        updated = await caller.updateEntry(input);
      } catch (error) {
        const mapped = deps.toAssistantHolidayEntryMutationError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        entry: deps.formatHolidayCalendarEntry(updated),
        message: `Updated holiday entry: ${updated.name}`,
      };
    },

    async delete_holiday_calendar_entry(params: { id: string }, ctx: ToolContext) {
      deps.assertAdminRole(ctx);
      const caller = deps.createHolidayCalendarCaller(deps.createScopedCallerContext(ctx));

      let deleted;
      try {
        deleted = await caller.deleteEntry({ id: params.id });
      } catch (error) {
        const mapped = deps.toAssistantHolidayEntryNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }

      return {
        __action: "invalidate" as const,
        scope: ["holidayCalendar", "vacation"],
        success: true,
        message: `Deleted holiday entry: ${deleted.name}`,
      };
    },
  };
}
