"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateAuditLogSidebar = generateAuditLogSidebar;
const Generator = __importStar(require("@postxl/generator"));
/**
 * Generator function that generates the Audit Log Sidebar component for admin pages.
 * This is a reusable component that accepts model name as a prop.
 */
function generateAuditLogSidebar({ context }) {
    const imports = Generator.ImportGenerator
        //
        .from(Generator.toBackendModuleLocation('@ui/admin/AuditLogSidebar'))
        .addImport({
        from: Generator.toPackageName('react'),
        items: [
            Generator.toFunctionName('useState'),
            Generator.toFunctionName('useMemo'),
            Generator.toFunctionName('useCallback'),
        ],
    })
        .addImport({
        from: Generator.toPackageName('@radix-ui/react-icons'),
        items: [
            Generator.toFunctionName('ChevronRightIcon'),
            Generator.toFunctionName('ChevronLeftIcon'),
            Generator.toFunctionName('ReloadIcon'),
            Generator.toFunctionName('PersonIcon'),
            Generator.toFunctionName('ClockIcon'),
        ],
    })
        .addImport({
        from: Generator.toPackageName('@tanstack/react-query'),
        items: [Generator.toFunctionName('useQuery'), Generator.toFunctionName('useQueryClient')],
    })
        .add(context.trpcClient);
    // Generate action labels map from models with custom actions
    const actionLabelsEntries = [];
    for (const m of context.models.values()) {
        if (m.source && typeof m.source === 'object' && 'actions' in m.source) {
            const actions = m.source.actions;
            if (actions) {
                for (const [actionName, actionLabel] of Object.entries(actions)) {
                    actionLabelsEntries.push(`  '${m.name.toLowerCase()}.${actionName}': '${actionLabel}',`);
                }
            }
        }
    }
    return `
${imports.generate()}

/**
 * Map of custom action labels from the schema
 */
const ACTION_LABELS: Record<string, string> = {
${actionLabelsEntries.join('\n')}
}

/**
 * Map of standard mutation kinds to human-readable labels
 */
const MUTATION_KIND_LABELS: Record<string, string> = {
  Create: 'Created',
  CreateMany: 'Created (batch)',
  Update: 'Updated',
  UpdateMany: 'Updated (batch)',
  Delete: 'Deleted',
  DeleteMany: 'Deleted (batch)',
  Clone: 'Cloned',
  CloneMany: 'Cloned (batch)',
}

type AuditLogEntry = Readonly<{
  id: string
  createdAt: Date | null
  actionId: string | null
  actionScope: string | null
  actionType: string | null
  userId: string | null
  userName: string | null
  mutationKind: string | null
  model: string | null
  entityId: string | null
  entityLabel: string | null
  sourceEntity: Record<string, unknown> | null
  resultEntity: Record<string, unknown> | null
  changedFields: ReadonlyArray<{
    field: string
    oldValue: unknown
    newValue: unknown
  }>
}>

export type AuditLogSidebarProps = Readonly<{
  /**
   * The model name to filter audit logs by
   */
  model: string
  /**
   * Optional entity ID to filter by (when a row is selected)
   */
  entityId?: string | null
  /**
   * Optional entity IDs to filter by (when multiple rows are selected via checkbox)
   */
  entityIds?: readonly string[]
  /**
   * Optional field name to filter by (when a cell is selected)
   */
  field?: string | null
  /**
   * Optional label field name for the model
   */
  labelField?: string
  /**
   * Callback to clear field filter
   */
  onClearField?: () => void
  /**
   * Callback to clear entity filter
   */
  onClearEntity?: () => void
}>

/**
 * Formats a date for display
 */
function formatDate(date: Date | null): string {
  if (!date) return 'Unknown'
  return new Intl.DateTimeFormat('en-US', {
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  }).format(new Date(date))
}

/**
 * Formats a value for display, handling all types safely
 */
function formatValue(value: unknown): string {
  if (value === null || value === undefined) return 'null'
  if (typeof value === 'string') {
    return value.length > 50 ? value.slice(0, 50) + '...' : value
  }
  if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
    const str = String(value) // NOSONAR - the above type checks ensure that no objects reach here
    return str.length > 50 ? str.slice(0, 50) + '...' : str
  }
  // For objects, arrays, symbols, or any other types - use JSON.stringify
  const json = JSON.stringify(value)
  return json.length > 50 ? json.slice(0, 50) + '...' : json
}

/**
 * Gets the action label for display
 */
function getActionLabel(entry: AuditLogEntry): string {
  // Check for custom action label
  if (entry.actionScope && entry.actionType) {
    const customKey = \`\${entry.actionScope.toLowerCase()}.\${entry.actionType}\`
    if (ACTION_LABELS[customKey]) {
      return ACTION_LABELS[customKey]
    }
  }

  // Fall back to mutation kind label
  if (entry.mutationKind) {
    return MUTATION_KIND_LABELS[entry.mutationKind] || entry.mutationKind
  }

  return entry.actionType || 'Unknown action'
}

/**
 * Gets the CSS classes for the mutation kind badge
 */
function getMutationKindClasses(mutationKind: string | null): string {
  const isCreate = mutationKind === 'Create' || mutationKind === 'CreateMany'
  const isDelete = mutationKind === 'Delete' || mutationKind === 'DeleteMany'

  if (isCreate) {
    return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
  }
  if (isDelete) {
    return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
  }
  return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'
}

/**
 * Gets the display text for entity count
 */
function getEntityCountText(count: number): string {
  if (count === 1) {
    return 'Selected row'
  }
  return \`\${count} rows\`
}

type AuditLogEntryCardProps = Readonly<{
  entry: AuditLogEntry
  isExpanded: boolean
  onToggle: () => void
}>

/**
 * Renders the inline change display for single-field updates
 */
function InlineChangeDisplay({ change }: Readonly<{ change: { field: string; oldValue: unknown; newValue: unknown } }>) {
  return (
    <div className="mt-1 flex flex-col gap-0.5 text-xs">
      {change.newValue !== undefined && (
        <span className="text-green-600 dark:text-green-400">
          {formatValue(change.newValue)}
        </span>
      )}
      {change.oldValue !== undefined && (
        <span className="text-red-600 dark:text-red-400 line-through text-[10px]">
          {formatValue(change.oldValue)}
        </span>
      )}
    </div>
  )
}

/**
 * Renders the expanded changes list
 */
function ExpandedChangesList({ changedFields }: Readonly<{ changedFields: ReadonlyArray<{ field: string; oldValue: unknown; newValue: unknown }> }>) {
  return (
    <div className="px-3 pb-3">
      <div className="mt-2 space-y-2">
        {changedFields.map((change) => (
          <div key={change.field} className="text-xs bg-gray-50 dark:bg-gray-800 rounded p-2">
            <div className="font-medium text-gray-700 dark:text-gray-300 mb-1">
              {change.field}
            </div>
            <div className="flex flex-col gap-0.5">
              {change.newValue !== undefined && (
                <span className="text-green-600 dark:text-green-400">
                  {formatValue(change.newValue)}
                </span>
              )}
              {change.oldValue !== undefined && (
                <span className="text-red-600 dark:text-red-400 line-through text-[10px]">
                  {formatValue(change.oldValue)}
                </span>
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

/**
 * Single audit log entry component
 */
function AuditLogEntryCard({ entry, isExpanded, onToggle }: AuditLogEntryCardProps) {
  const actionLabel = getActionLabel(entry)
  const hasChanges = entry.changedFields.length > 0

  // Show inline for single-field updates (Update/UpdateMany with exactly 1 field changed)
  const isUpdateField = (entry.mutationKind === 'Update' || entry.mutationKind === 'UpdateMany') && entry.changedFields.length === 1
  const inlineChange = isUpdateField ? entry.changedFields[0] : null

  // Only show expand button if there are multiple changes and it's not an inline update
  const showExpandButton = hasChanges && !isUpdateField
  const showExpandedContent = isExpanded && hasChanges && !isUpdateField

  return (
    <div className="border-b border-gray-200 dark:border-gray-700 last:border-b-0">
      <button
        onClick={onToggle}
        className={\`w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors \${showExpandButton ? '' : 'cursor-default'}\`}
        disabled={!showExpandButton}
      >
        <div className="flex items-start justify-between gap-2">
          <div className="flex-1 min-w-0">
            <div className="flex items-center gap-2">
              <span className={\`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium \${getMutationKindClasses(entry.mutationKind)}\`}>
                {actionLabel}
              </span>
              {inlineChange && (
                <span className="text-xs text-gray-500 dark:text-gray-400">
                  {inlineChange.field}
                </span>
              )}
            </div>
            <p className="mt-1 text-sm text-gray-900 dark:text-gray-100 truncate">
              {entry.entityLabel || entry.entityId || 'Unknown entity'}
            </p>
            {inlineChange && <InlineChangeDisplay change={inlineChange} />}
            <div className="mt-1 flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
              <span className="flex items-center gap-1">
                <ClockIcon className="w-3 h-3" />
                {formatDate(entry.createdAt)}
              </span>
              {entry.userName && (
                <span className="flex items-center gap-1">
                  <PersonIcon className="w-3 h-3" />
                  {entry.userName}
                </span>
              )}
            </div>
          </div>
          {showExpandButton && (
            <ChevronRightIcon
              className={\`w-4 h-4 text-gray-400 transition-transform \${isExpanded ? 'rotate-90' : ''}\`}
            />
          )}
        </div>
      </button>

      {showExpandedContent && <ExpandedChangesList changedFields={entry.changedFields} />}
    </div>
  )
}

/**
 * Renders the breadcrumb navigation for entity/field filters
 */
function BreadcrumbNavigation({
  model,
  hasEntityFilter,
  hasFieldFilter,
  entityCount,
  field,
  onClearEntity,
  onClearField,
}: Readonly<{
  model: string
  hasEntityFilter: boolean
  hasFieldFilter: boolean
  entityCount: number
  field: string | null | undefined
  onClearEntity: () => void
  onClearField: () => void
}>) {
  const entityCountText = getEntityCountText(entityCount)

  return (
    <div className="px-4 py-2 border-b border-gray-200 dark:border-gray-700 flex items-center gap-1 text-xs">
      <button
        onClick={onClearEntity}
        className="text-blue-600 dark:text-blue-400 hover:underline"
      >
        All {model}s
      </button>
      {hasEntityFilter && (
        <>
          <ChevronRightIcon className="w-3 h-3 text-gray-400" />
          {hasFieldFilter ? (
            <button
              onClick={onClearField}
              className="text-blue-600 dark:text-blue-400 hover:underline"
            >
              {entityCountText}
            </button>
          ) : (
            <span className="text-gray-700 dark:text-gray-300">
              {entityCountText}
            </span>
          )}
        </>
      )}
      {hasFieldFilter && (
        <>
          <ChevronRightIcon className="w-3 h-3 text-gray-400" />
          <span className="text-gray-700 dark:text-gray-300">{field}</span>
        </>
      )}
    </div>
  )
}

/**
 * Audit Log Sidebar component for admin pages
 */
export function AuditLogSidebar({ model, entityId, entityIds, field, labelField: _labelField = 'name', onClearField, onClearEntity }: AuditLogSidebarProps) {
  const [isOpen, setIsOpen] = useState(false)
  const [limit, setLimit] = useState(20)
  const [expandedEntries, setExpandedEntries] = useState<Set<string>>(new Set())

  const trpc = ${context.trpcClient.name}()
  const queryClient = useQueryClient()

  // Combine entityId and entityIds for query
  const allEntityIds = useMemo(() => {
    const ids: string[] = []
    if (entityId) ids.push(entityId)
    if (entityIds) ids.push(...entityIds.filter(id => id !== entityId))
    return ids.length > 0 ? ids : undefined
  }, [entityId, entityIds])

  // Build query input
  const queryInput = useMemo(() => ({
    model,
    entityId: undefined, // Use entityIds instead for consistency
    entityIds: allEntityIds,
    field: field || undefined,
    limit,
    offset: 0,
  }), [model, allEntityIds, field, limit])

  const queryOptions = trpc.auditLogs.query.queryOptions(queryInput)

  const { data, isPending: isLoading, isFetching } = useQuery({
    ...queryOptions,
    enabled: isOpen,
    refetchInterval: isOpen ? 30000 : false, // Refetch every 30s when open
  })

  const refetch = useCallback(() => {
    void queryClient.invalidateQueries({ queryKey: queryOptions.queryKey })
  }, [queryClient, queryOptions.queryKey])

  const toggleEntry = useCallback((id: string) => {
    setExpandedEntries((prev) => {
      const next = new Set(prev)
      if (next.has(id)) {
        next.delete(id)
      } else {
        next.add(id)
      }
      return next
    })
  }, [])

  const loadMore = useCallback(() => {
    setLimit((prev) => prev + 20)
  }, [])

  // Get filter description and breadcrumb state
  const hasEntityFilter = (allEntityIds?.length ?? 0) > 0
  const hasFieldFilter = Boolean(field)
  const entityCount = allEntityIds?.length ?? 0

  const filterDescription = useMemo(() => {
    if (field && entityCount > 0) {
      return \`Changes to "\${field}" field\`
    }
    if (entityCount > 1) {
      return \`Changes to \${entityCount} selected rows\`
    }
    if (entityCount === 1) {
      return 'Changes to selected row'
    }
    return 'All changes'
  }, [field, entityCount])

  // Handle breadcrumb navigation
  const handleClearField = useCallback(() => {
    onClearField?.()
  }, [onClearField])

  const handleClearEntity = useCallback(() => {
    onClearEntity?.()
  }, [onClearEntity])

  const showBreadcrumbs = hasEntityFilter || hasFieldFilter

  return (
    <>
      {/* Toggle button - fixed on right edge */}
      <button
        onClick={() => setIsOpen(!isOpen)}
        className={\`
          fixed right-0 top-1/2 -translate-y-1/2 z-40
          flex items-center justify-center
          w-6 h-24 bg-white dark:bg-gray-900
          border border-r-0 border-gray-200 dark:border-gray-700
          rounded-l-lg shadow-lg
          hover:bg-gray-50 dark:hover:bg-gray-800
          transition-colors
          \${isOpen ? 'right-80' : 'right-0'}
        \`}
        aria-label={isOpen ? 'Close audit log' : 'Open audit log'}
      >
        {isOpen ? (
          <ChevronRightIcon className="w-4 h-4 text-gray-600 dark:text-gray-400" />
        ) : (
          <ChevronLeftIcon className="w-4 h-4 text-gray-600 dark:text-gray-400" />
        )}
      </button>

      {/* Sidebar */}
      <aside
        className={\`
          fixed right-0 top-0 h-screen w-80 bg-white dark:bg-gray-900
          border-l border-gray-200 dark:border-gray-700
          overflow-hidden transition-transform duration-300 z-30
          \${isOpen ? 'translate-x-0' : 'translate-x-full'}
        \`}
      >
        <div className="flex flex-col h-full">
          {/* Header */}
          <div className="p-4 border-b border-gray-200 dark:border-gray-700">
            <div className="flex items-center justify-between">
              <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
                Audit Log
              </h3>
              <button
                onClick={() => void refetch()}
                disabled={isFetching}
                className="p-1 hover:bg-gray-100 dark:hover:bg-gray-800 rounded transition-colors disabled:opacity-50"
                aria-label="Refresh"
              >
                <ReloadIcon className={\`w-4 h-4 \${isFetching ? 'animate-spin' : ''}\`} />
              </button>
            </div>
            <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
              {filterDescription}
            </p>
            {data && (
              <p className="mt-1 text-xs text-gray-400 dark:text-gray-500">
                {data.total} {data.total === 1 ? 'change' : 'changes'} found
              </p>
            )}
          </div>

          {/* Breadcrumb navigation */}
          {showBreadcrumbs && (
            <BreadcrumbNavigation
              model={model}
              hasEntityFilter={hasEntityFilter}
              hasFieldFilter={hasFieldFilter}
              entityCount={entityCount}
              field={field}
              onClearEntity={handleClearEntity}
              onClearField={handleClearField}
            />
          )}

          {/* Limit selector */}
          <div className="px-4 py-2 border-b border-gray-200 dark:border-gray-700">
            <label className="text-xs text-gray-500 dark:text-gray-400">
              Show:{' '}
              <select
                value={limit}
                onChange={(e) => setLimit(Number(e.target.value))}
                className="ml-1 px-2 py-1 text-xs border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800"
              >
                <option value={10}>10</option>
                <option value={20}>20</option>
                <option value={50}>50</option>
                <option value={100}>100</option>
              </select>
            </label>
          </div>

          {/* Content */}
          <div className="flex-1 overflow-y-auto">
            {isLoading && (
              <div className="flex items-center justify-center h-32">
                <ReloadIcon className="w-6 h-6 animate-spin text-gray-400" />
              </div>
            )}
            {!isLoading && data?.entries.length === 0 && (
              <div className="flex flex-col items-center justify-center h-32 text-gray-500 dark:text-gray-400">
                <p className="text-sm">No changes found</p>
                <p className="text-xs mt-1">Changes will appear here when entities are modified</p>
              </div>
            )}
            {!isLoading && data && data.entries.length > 0 && (
              <div>
                {data.entries.map((entry: AuditLogEntry) => (
                  <AuditLogEntryCard
                    key={entry.id}
                    entry={entry}
                    isExpanded={expandedEntries.has(entry.id)}
                    onToggle={() => toggleEntry(entry.id)}
                  />
                ))}
                {data.hasMore && (
                  <div className="p-3">
                    <button
                      onClick={loadMore}
                      className="w-full py-2 text-sm text-blue-600 dark:text-blue-400 hover:bg-gray-50 dark:hover:bg-gray-800 rounded transition-colors"
                    >
                      Load more...
                    </button>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>
      </aside>
    </>
  )
}
`;
}
//# sourceMappingURL=audit-log-sidebar.generator.js.map