'use client';

import { useQuery } from '@tanstack/react-query';
import { formatDistanceToNow } from 'date-fns';

import { apiFetch } from '@/lib/api/client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { CardSkeleton } from '@/components/shared/loading-skeleton';
import { WidgetErrorBoundary } from './widget-error-boundary';

interface ActivityItem {
  id: string;
  action: string;
  entityType: string;
  entityId: string | null;
  /** Server-resolved human label (client name, yacht name, …) when the
   *  underlying entity still exists. Falls back to the id prefix in the UI. */
  label: string | null;
  userId: string | null;
  fieldChanged: string | null;
  oldValue: unknown;
  newValue: unknown;
  metadata: Record<string, unknown> | null;
  createdAt: string;
}

/** camelCase / snake_case field name → "Title Case" so the audit log
 *  reads naturally ("fullName" → "Full Name", "phone_number" → "Phone
 *  Number"). Single-word fields stay capitalized. */
function humanizeFieldName(name: string): string {
  return name
    .replace(/_/g, ' ')
    .replace(/([a-z])([A-Z])/g, '$1 $2')
    .replace(/\b\w/g, (c) => c.toUpperCase());
}

/** Render a JSON-ish value as a short, single-line preview. Strings come
 *  through as-is; objects flatten to "k: v, k: v"; arrays compress to a
 *  count; nulls / empty render as em-dash. */
function shortValue(value: unknown): string {
  if (value === null || value === undefined || value === '') return '—';
  if (typeof value === 'string') return value;
  if (typeof value === 'number' || typeof value === 'boolean') return String(value);
  if (Array.isArray(value)) return `${value.length} item${value.length === 1 ? '' : 's'}`;
  if (typeof value === 'object') {
    const entries = Object.entries(value as Record<string, unknown>);
    if (entries.length === 0) return '—';
    return entries
      .slice(0, 3)
      .map(([k, v]) => `${humanizeFieldName(k)}: ${typeof v === 'string' ? v : JSON.stringify(v)}`)
      .join(', ');
  }
  return String(value);
}

/** Build a "Field: old → new" diff string for the activity row's second
 *  line. Returns null when there's nothing useful to show.
 *
 *  Audit logs for updates store the per-field diff inside `oldValue` as
 *  `{ field: { old, new }, … }` (see entity-diff.ts), so that's the
 *  shape we pattern-match first. Falls back to a fieldChanged/old→new
 *  pair when those are present, and finally to a key-by-key compare of
 *  two flat objects in `oldValue` vs `newValue`. */
function buildDiffLine(item: ActivityItem): string | null {
  // Shape A: oldValue = { field: { old, new }, … }
  if (
    item.action === 'update' &&
    item.oldValue &&
    typeof item.oldValue === 'object' &&
    !Array.isArray(item.oldValue)
  ) {
    const diffMap = item.oldValue as Record<string, unknown>;
    const entries = Object.entries(diffMap).filter(([, v]) => {
      return v && typeof v === 'object' && 'old' in (v as object) && 'new' in (v as object);
    });
    if (entries.length > 0) {
      return entries
        .slice(0, 2)
        .map(([field, v]) => {
          const { old, new: nextValue } = v as { old: unknown; new: unknown };
          return `${humanizeFieldName(field)}: ${shortValue(old)} → ${shortValue(nextValue)}`;
        })
        .join(' · ');
    }
  }

  // Shape B: single-field change with explicit columns.
  if (item.fieldChanged) {
    return `${humanizeFieldName(item.fieldChanged)}: ${shortValue(item.oldValue)} → ${shortValue(item.newValue)}`;
  }

  // Shape C: flat oldValue vs flat newValue.
  if (
    item.action === 'update' &&
    item.oldValue &&
    typeof item.oldValue === 'object' &&
    item.newValue &&
    typeof item.newValue === 'object'
  ) {
    const oldObj = item.oldValue as Record<string, unknown>;
    const newObj = item.newValue as Record<string, unknown>;
    const keys = Object.keys(oldObj).filter((k) => k in newObj);
    if (keys.length === 0) return null;
    return keys
      .slice(0, 2)
      .map((k) => `${humanizeFieldName(k)}: ${shortValue(oldObj[k])} → ${shortValue(newObj[k])}`)
      .join(' · ');
  }

  return null;
}

const ACTION_VARIANTS: Record<string, 'default' | 'secondary' | 'destructive' | 'outline'> = {
  create: 'default',
  update: 'secondary',
  delete: 'destructive',
  archive: 'outline',
  restore: 'secondary',
};

function ActionBadge({ action }: { action: string }) {
  const variant = ACTION_VARIANTS[action] ?? 'outline';
  return (
    <Badge variant={variant} className="shrink-0 capitalize text-xs">
      {action}
    </Badge>
  );
}

function ActivityFeedInner() {
  const { data, isLoading } = useQuery<ActivityItem[]>({
    queryKey: ['dashboard', 'activity'],
    queryFn: () => apiFetch<ActivityItem[]>('/api/v1/dashboard/activity'),
    staleTime: 30_000,
    retry: 2,
  });

  if (isLoading) {
    return <CardSkeleton />;
  }

  const items = data ?? [];

  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">Recent Activity</CardTitle>
      </CardHeader>
      <CardContent>
        {items.length === 0 ? (
          <p className="text-sm text-muted-foreground">
            No recent activity yet - your team&apos;s actions (interests created, stages changed,
            invoices sent) will appear here.
          </p>
        ) : (
          <div className="max-h-80 overflow-y-auto space-y-3 pr-1">
            {items.map((item) => {
              const diffLine = buildDiffLine(item);
              return (
                <div
                  key={item.id}
                  className="flex items-start gap-3 text-sm border-b border-border pb-3 last:border-0 last:pb-0"
                >
                  <ActionBadge action={item.action} />
                  <div className="min-w-0 flex-1">
                    <p className="truncate text-foreground">
                      {item.label ? (
                        <>
                          <span className="font-medium">{item.label}</span>
                          <span className="ml-1.5 text-muted-foreground text-xs capitalize">
                            {item.entityType}
                          </span>
                        </>
                      ) : (
                        <>
                          <span className="font-medium capitalize">{item.entityType}</span>
                          {item.entityId && (
                            <span className="ml-1 text-muted-foreground font-mono text-xs">
                              {item.entityId.slice(0, 8)}
                            </span>
                          )}
                        </>
                      )}
                    </p>
                    {diffLine ? (
                      <p
                        className="truncate text-xs text-muted-foreground mt-0.5"
                        title={diffLine}
                      >
                        {diffLine}
                      </p>
                    ) : null}
                    <p className="text-[11px] text-muted-foreground/80 mt-0.5">
                      {formatDistanceToNow(new Date(item.createdAt), { addSuffix: true })}
                    </p>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </CardContent>
    </Card>
  );
}

export function ActivityFeed() {
  return (
    <WidgetErrorBoundary>
      <ActivityFeedInner />
    </WidgetErrorBoundary>
  );
}
