import { DispoStagedRecordType, ImportBatchStatus, StagedRecordStatus } from "@capakraken/db";
import { PermissionKey, SystemRole } from "@capakraken/shared";
import type { TRPCContext } from "../../trpc.js";
import { withToolAccess, type ToolContext, type ToolDef, type ToolExecutor } from "./shared.js";

type ImportExportDispoDeps = {
  assertPermission: (ctx: ToolContext, perm: PermissionKey) => void;
  createImportExportCaller: (ctx: TRPCContext) => {
    exportResourcesCSV: () => Promise<string>;
    exportProjectsCSV: () => Promise<string>;
    importCSV: (params: {
      entityType: "resources" | "projects" | "allocations";
      rows: Array<Record<string, string>>;
      dryRun: boolean;
    }) => Promise<unknown>;
  };
  createDispoCaller: (ctx: TRPCContext) => {
    listImportBatches: (params: {
      status?: ImportBatchStatus;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    getImportBatch: (params: { id: string }) => Promise<unknown>;
    stageImportBatch: (params: {
      chargeabilityWorkbookPath: string;
      costWorkbookPath?: string;
      notes?: string | null;
      planningWorkbookPath: string;
      referenceWorkbookPath: string;
      rosterWorkbookPath?: string;
    }) => Promise<unknown>;
    validateImportBatch: (params: {
      chargeabilityWorkbookPath: string;
      costWorkbookPath?: string;
      importBatchId?: string;
      notes?: string | null;
      planningWorkbookPath: string;
      referenceWorkbookPath: string;
      rosterWorkbookPath?: string;
    }) => Promise<unknown>;
    cancelImportBatch: (params: { id: string }) => Promise<unknown>;
    listStagedResources: (params: {
      importBatchId: string;
      status?: StagedRecordStatus;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    listStagedProjects: (params: {
      importBatchId: string;
      status?: StagedRecordStatus;
      isTbd?: boolean;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    listStagedAssignments: (params: {
      importBatchId: string;
      status?: StagedRecordStatus;
      resourceExternalId?: string;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    listStagedVacations: (params: {
      importBatchId: string;
      resourceExternalId?: string;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    listStagedUnresolvedRecords: (params: {
      importBatchId: string;
      recordType?: DispoStagedRecordType;
      limit?: number;
      cursor?: string;
    }) => Promise<unknown>;
    resolveStagedRecord: (params: {
      action: "APPROVE" | "REJECT" | "SKIP";
      id: string;
      recordType: DispoStagedRecordType;
    }) => Promise<unknown>;
    commitImportBatch: (params: {
      importBatchId: string;
      allowTbdUnresolved?: boolean;
      importTbdProjects?: boolean;
    }) => Promise<unknown>;
  };
  createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
  toAssistantDispoImportBatchNotFoundError: (error: unknown) => { error: string } | null;
};

const CONTROLLER_ASSISTANT_ROLES = [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER] as const;
const MANAGER_ASSISTANT_ROLES = [SystemRole.ADMIN, SystemRole.MANAGER] as const;
const ADMIN_ASSISTANT_ROLES = [SystemRole.ADMIN] as const;

export const importExportDispoToolDefinitions: ToolDef[] = withToolAccess([
  {
    type: "function",
    function: {
      name: "export_resources_csv",
      description: "Export the current active resource list as CSV via the real import/export router. Controller/manager/admin roles only.",
      parameters: {
        type: "object",
        properties: {},
      },
    },
  },
  {
    type: "function",
    function: {
      name: "export_projects_csv",
      description: "Export the current project list as CSV via the real import/export router. Controller/manager/admin roles only.",
      parameters: {
        type: "object",
        properties: {},
      },
    },
  },
  {
    type: "function",
    function: {
      name: "import_csv_data",
      description: "Import CSV-style row data for resources, projects, or allocations via the real import/export router. Requires manager/admin, importData permission, and defaults to dry-run.",
      parameters: {
        type: "object",
        properties: {
          entityType: { type: "string", enum: ["resources", "projects", "allocations"], description: "Import target entity type." },
          rows: {
            type: "array",
            description: "CSV rows already parsed to key/value objects.",
            items: {
              type: "object",
              additionalProperties: { type: "string" },
            },
          },
          dryRun: { type: "boolean", description: "Validate only without persisting changes. Default: true." },
        },
        required: ["entityType", "rows"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_import_batches",
      description: "List Dispo import batches with pagination and optional status filter via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          status: { type: "string", description: "Optional batch status filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
      },
    },
  },
  {
    type: "function",
    function: {
      name: "get_dispo_import_batch",
      description: "Get one Dispo import batch including staged record counters via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Import batch ID." },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "stage_dispo_import_batch",
      description: "Stage a Dispo import batch via the real dispo router. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          planningWorkbookPath: { type: "string", description: "Filesystem path to the planning workbook." },
          referenceWorkbookPath: { type: "string", description: "Filesystem path to the reference workbook." },
          chargeabilityWorkbookPath: { type: "string", description: "Filesystem path to the chargeability workbook." },
          costWorkbookPath: { type: "string", description: "Optional filesystem path to the cost workbook." },
          rosterWorkbookPath: { type: "string", description: "Optional filesystem path to the roster workbook." },
          notes: { type: "string", description: "Optional import notes." },
        },
        required: ["planningWorkbookPath", "referenceWorkbookPath", "chargeabilityWorkbookPath"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "validate_dispo_import_batch",
      description: "Validate a Dispo import batch readiness check via the real dispo router without committing anything. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          planningWorkbookPath: { type: "string", description: "Filesystem path to the planning workbook." },
          referenceWorkbookPath: { type: "string", description: "Filesystem path to the reference workbook." },
          chargeabilityWorkbookPath: { type: "string", description: "Filesystem path to the chargeability workbook." },
          costWorkbookPath: { type: "string", description: "Optional filesystem path to the cost workbook." },
          rosterWorkbookPath: { type: "string", description: "Optional filesystem path to the roster workbook." },
          importBatchId: { type: "string", description: "Optional existing staged import batch ID." },
          notes: { type: "string", description: "Optional import notes." },
        },
        required: ["planningWorkbookPath", "referenceWorkbookPath", "chargeabilityWorkbookPath"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "cancel_dispo_import_batch",
      description: "Cancel a staged Dispo import batch via the real dispo router. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Import batch ID." },
        },
        required: ["id"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_staged_resources",
      description: "List staged Dispo resources for one import batch via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          status: { type: "string", description: "Optional staged record status filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
        required: ["importBatchId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_staged_projects",
      description: "List staged Dispo projects for one import batch via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          status: { type: "string", description: "Optional staged record status filter." },
          isTbd: { type: "boolean", description: "Optional TBD-project filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
        required: ["importBatchId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_staged_assignments",
      description: "List staged Dispo assignments for one import batch via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          status: { type: "string", description: "Optional staged record status filter." },
          resourceExternalId: { type: "string", description: "Optional resource external ID filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
        required: ["importBatchId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_staged_vacations",
      description: "List staged Dispo vacations for one import batch via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          resourceExternalId: { type: "string", description: "Optional resource external ID filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
        required: ["importBatchId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "list_dispo_staged_unresolved_records",
      description: "List staged unresolved Dispo records for one import batch via the real dispo router. Admin role required.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          recordType: { type: "string", description: "Optional unresolved record type filter." },
          limit: { type: "integer", description: "Max results. Default: 50, max: 200." },
          cursor: { type: "string", description: "Optional pagination cursor." },
        },
        required: ["importBatchId"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "resolve_dispo_staged_record",
      description: "Resolve one staged Dispo record via the real dispo router. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          id: { type: "string", description: "Staged record ID." },
          recordType: { type: "string", description: "Staged record type." },
          action: { type: "string", enum: ["APPROVE", "REJECT", "SKIP"], description: "Resolution action." },
        },
        required: ["id", "recordType", "action"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "commit_dispo_import_batch",
      description: "Commit a staged Dispo import batch via the real dispo router. Admin role required. Always confirm first.",
      parameters: {
        type: "object",
        properties: {
          importBatchId: { type: "string", description: "Import batch ID." },
          allowTbdUnresolved: { type: "boolean", description: "Allow unresolved TBD projects during commit." },
          importTbdProjects: { type: "boolean", description: "Whether TBD projects should be imported." },
        },
        required: ["importBatchId"],
      },
    },
  },
], {
  export_resources_csv: {
    allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES],
  },
  export_projects_csv: {
    allowedSystemRoles: [...CONTROLLER_ASSISTANT_ROLES],
  },
  import_csv_data: {
    requiredPermissions: [PermissionKey.IMPORT_DATA],
    allowedSystemRoles: [...MANAGER_ASSISTANT_ROLES],
  },
  list_dispo_import_batches: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  get_dispo_import_batch: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  stage_dispo_import_batch: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  validate_dispo_import_batch: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  cancel_dispo_import_batch: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  list_dispo_staged_resources: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  list_dispo_staged_projects: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  list_dispo_staged_assignments: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  list_dispo_staged_vacations: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  list_dispo_staged_unresolved_records: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  resolve_dispo_staged_record: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
  commit_dispo_import_batch: {
    allowedSystemRoles: [...ADMIN_ASSISTANT_ROLES],
  },
});

function clampLimit(limit: number | undefined): number | undefined {
  return limit !== undefined ? Math.min(Math.max(limit, 1), 200) : undefined;
}

export function createImportExportDispoExecutors(
  deps: ImportExportDispoDeps,
): Record<string, ToolExecutor> {
  return {
    async export_resources_csv(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createImportExportCaller(deps.createScopedCallerContext(ctx));
      const csv = await caller.exportResourcesCSV();
      return {
        format: "csv",
        lineCount: csv.length === 0 ? 0 : csv.split("\n").length,
        csv,
      };
    },

    async export_projects_csv(_params: Record<string, never>, ctx: ToolContext) {
      const caller = deps.createImportExportCaller(deps.createScopedCallerContext(ctx));
      const csv = await caller.exportProjectsCSV();
      return {
        format: "csv",
        lineCount: csv.length === 0 ? 0 : csv.split("\n").length,
        csv,
      };
    },

    async import_csv_data(
      params: {
        entityType: "resources" | "projects" | "allocations";
        rows: Array<Record<string, string>>;
        dryRun?: boolean;
      },
      ctx: ToolContext,
    ) {
      deps.assertPermission(ctx, PermissionKey.IMPORT_DATA);
      const caller = deps.createImportExportCaller(deps.createScopedCallerContext(ctx));
      return caller.importCSV({
        entityType: params.entityType,
        rows: params.rows,
        dryRun: params.dryRun ?? true,
      });
    },

    async list_dispo_import_batches(
      params: { status?: ImportBatchStatus; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listImportBatches({
        ...(params.status ? { status: params.status } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async get_dispo_import_batch(
      params: { id: string },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      try {
        return await caller.getImportBatch({ id: params.id });
      } catch (error) {
        const mapped = deps.toAssistantDispoImportBatchNotFoundError(error);
        if (mapped) {
          return mapped;
        }
        throw error;
      }
    },

    async stage_dispo_import_batch(
      params: {
        chargeabilityWorkbookPath: string;
        costWorkbookPath?: string;
        notes?: string | null;
        planningWorkbookPath: string;
        referenceWorkbookPath: string;
        rosterWorkbookPath?: string;
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.stageImportBatch({
        chargeabilityWorkbookPath: params.chargeabilityWorkbookPath,
        planningWorkbookPath: params.planningWorkbookPath,
        referenceWorkbookPath: params.referenceWorkbookPath,
        ...(params.costWorkbookPath !== undefined ? { costWorkbookPath: params.costWorkbookPath } : {}),
        ...(params.notes !== undefined ? { notes: params.notes } : {}),
        ...(params.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: params.rosterWorkbookPath } : {}),
      });
    },

    async validate_dispo_import_batch(
      params: {
        chargeabilityWorkbookPath: string;
        costWorkbookPath?: string;
        importBatchId?: string;
        notes?: string | null;
        planningWorkbookPath: string;
        referenceWorkbookPath: string;
        rosterWorkbookPath?: string;
      },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.validateImportBatch({
        chargeabilityWorkbookPath: params.chargeabilityWorkbookPath,
        planningWorkbookPath: params.planningWorkbookPath,
        referenceWorkbookPath: params.referenceWorkbookPath,
        ...(params.costWorkbookPath !== undefined ? { costWorkbookPath: params.costWorkbookPath } : {}),
        ...(params.importBatchId !== undefined ? { importBatchId: params.importBatchId } : {}),
        ...(params.notes !== undefined ? { notes: params.notes } : {}),
        ...(params.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: params.rosterWorkbookPath } : {}),
      });
    },

    async cancel_dispo_import_batch(
      params: { id: string },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.cancelImportBatch({ id: params.id });
    },

    async list_dispo_staged_resources(
      params: { importBatchId: string; status?: StagedRecordStatus; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listStagedResources({
        importBatchId: params.importBatchId,
        ...(params.status !== undefined ? { status: params.status } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async list_dispo_staged_projects(
      params: { importBatchId: string; status?: StagedRecordStatus; isTbd?: boolean; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listStagedProjects({
        importBatchId: params.importBatchId,
        ...(params.status !== undefined ? { status: params.status } : {}),
        ...(params.isTbd !== undefined ? { isTbd: params.isTbd } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async list_dispo_staged_assignments(
      params: { importBatchId: string; status?: StagedRecordStatus; resourceExternalId?: string; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listStagedAssignments({
        importBatchId: params.importBatchId,
        ...(params.status !== undefined ? { status: params.status } : {}),
        ...(params.resourceExternalId !== undefined ? { resourceExternalId: params.resourceExternalId } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async list_dispo_staged_vacations(
      params: { importBatchId: string; resourceExternalId?: string; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listStagedVacations({
        importBatchId: params.importBatchId,
        ...(params.resourceExternalId !== undefined ? { resourceExternalId: params.resourceExternalId } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async list_dispo_staged_unresolved_records(
      params: { importBatchId: string; recordType?: DispoStagedRecordType; limit?: number; cursor?: string },
      ctx: ToolContext,
    ) {
      const limit = clampLimit(params.limit);
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.listStagedUnresolvedRecords({
        importBatchId: params.importBatchId,
        ...(params.recordType !== undefined ? { recordType: params.recordType } : {}),
        ...(params.cursor ? { cursor: params.cursor } : {}),
        ...(limit !== undefined ? { limit } : {}),
      });
    },

    async resolve_dispo_staged_record(
      params: { action: "APPROVE" | "REJECT" | "SKIP"; id: string; recordType: DispoStagedRecordType },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.resolveStagedRecord({
        action: params.action,
        id: params.id,
        recordType: params.recordType,
      });
    },

    async commit_dispo_import_batch(
      params: { allowTbdUnresolved?: boolean; importBatchId: string; importTbdProjects?: boolean },
      ctx: ToolContext,
    ) {
      const caller = deps.createDispoCaller(deps.createScopedCallerContext(ctx));
      return caller.commitImportBatch({
        importBatchId: params.importBatchId,
        ...(params.allowTbdUnresolved !== undefined ? { allowTbdUnresolved: params.allowTbdUnresolved } : {}),
        ...(params.importTbdProjects !== undefined ? { importTbdProjects: params.importTbdProjects } : {}),
      });
    },
  };
}
