'use client';

/**
 * Enhanced notifications page with:
 * - WebSocket real-time updates via useNotification hook
 * - Grouping by project/context
 * - Quick actions (mark read, archive, preview)
 * - Extended notification type support
 */

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import {
  Archive,
  AtSign,
  Bell,
  BellOff,
  Check,
  CheckCheck,
  ChevronDown,
  ChevronRight,
  Loader2,
  Mail,
  MessageSquare,
  Settings,
  Shield,
  GitPullRequest,
  CheckCircle,
  Merge,
  AlertCircle,
  SlidersHorizontal,
} from 'lucide-react';
import {
  notificationMarkRead,
  notificationMarkAllRead,
  notificationArchive,
} from '@/client';
import type { NotificationResponse } from '@/client/types.gen';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { getApiErrorMessage } from '@/lib/api-error';
import { useNotification } from '@/hooks/useNotification';
import { cn } from '@/lib/utils';
import { toast } from 'sonner';

type Filter = 'all' | 'unread' | 'archived';
type GroupBy = 'none' | 'project' | 'type';

const NOTIFICATION_TYPE_CONFIG: Record<
  string,
  { label: string; icon: React.ReactNode; color: string }
> = {
  mention: {
    label: 'Mention',
    icon: <AtSign className="h-3.5 w-3.5" />,
    color: 'bg-blue-500/10 text-blue-600 border-blue-500/20',
  },
  invitation: {
    label: 'Invitation',
    icon: <Mail className="h-3.5 w-3.5" />,
    color: 'bg-purple-500/10 text-purple-600 border-purple-500/20',
  },
  project_invitation: {
    label: 'Project Invite',
    icon: <Mail className="h-3.5 w-3.5" />,
    color: 'bg-purple-500/10 text-purple-600 border-purple-500/20',
  },
  workspace_invitation: {
    label: 'Workspace Invite',
    icon: <Mail className="h-3.5 w-3.5" />,
    color: 'bg-purple-500/10 text-purple-600 border-purple-500/20',
  },
  role_change: {
    label: 'Role Change',
    icon: <Shield className="h-3.5 w-3.5" />,
    color: 'bg-orange-500/10 text-orange-600 border-orange-500/20',
  },
  room_created: {
    label: 'Room Created',
    icon: <MessageSquare className="h-3.5 w-3.5" />,
    color: 'bg-green-500/10 text-green-600 border-green-500/20',
  },
  room_deleted: {
    label: 'Room Deleted',
    icon: <MessageSquare className="h-3.5 w-3.5" />,
    color: 'bg-red-500/10 text-red-600 border-red-500/20',
  },
  system_announcement: {
    label: 'Announcement',
    icon: <Bell className="h-3.5 w-3.5" />,
    color: 'bg-yellow-500/10 text-yellow-700 border-yellow-500/20',
  },
  issue_opened: {
    label: 'Issue Opened',
    icon: <AlertCircle className="h-3.5 w-3.5" />,
    color: 'bg-emerald-500/10 text-emerald-600 border-emerald-500/20',
  },
  issue_commented: {
    label: 'Issue Comment',
    icon: <MessageSquare className="h-3.5 w-3.5" />,
    color: 'bg-blue-500/10 text-blue-600 border-blue-500/20',
  },
  issue_closed: {
    label: 'Issue Closed',
    icon: <CheckCircle className="h-3.5 w-3.5" />,
    color: 'bg-violet-500/10 text-violet-600 border-violet-500/20',
  },
  pr_review_requested: {
    label: 'Review Requested',
    icon: <GitPullRequest className="h-3.5 w-3.5" />,
    color: 'bg-amber-500/10 text-amber-600 border-amber-500/20',
  },
  pr_approved: {
    label: 'PR Approved',
    icon: <CheckCircle className="h-3.5 w-3.5" />,
    color: 'bg-green-500/10 text-green-600 border-green-500/20',
  },
  pr_merged: {
    label: 'PR Merged',
    icon: <Merge className="h-3.5 w-3.5" />,
    color: 'bg-purple-500/10 text-purple-600 border-purple-500/20',
  },
};

function formatTime(dateStr: string): string {
  const d = new Date(dateStr);
  const now = new Date();
  const diff = now.getTime() - d.getTime();
  const minutes = Math.floor(diff / 60000);
  if (minutes < 1) return 'just now';
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  if (days < 7) return `${days}d ago`;
  return d.toLocaleDateString();
}

function getTypeLabel(type: string): string {
  return NOTIFICATION_TYPE_CONFIG[type]?.label ?? type;
}

interface NotificationItemProps {
  n: NotificationResponse;
  onMarkRead: (id: string) => void;
  onArchive: (id: string) => void;
  onNavigate: (n: NotificationResponse) => void;
}

function NotificationItem({ n, onMarkRead, onArchive, onNavigate }: NotificationItemProps) {
  const config = NOTIFICATION_TYPE_CONFIG[n.notification_type] ?? {
    label: n.notification_type,
    icon: <Bell className="h-3.5 w-3.5" />,
    color: 'bg-muted text-muted-foreground border-border',
  };

  return (
    <div
      className={cn(
        'group flex items-start gap-3 px-4 py-3 hover:bg-muted/50 transition-colors border-b last:border-b-0',
        !n.is_read && 'bg-primary/5',
      )}
    >
      {/* Unread dot */}
      <div className="flex-shrink-0 pt-1">
        {!n.is_read && <div className="h-2 w-2 rounded-full bg-primary" />}
      </div>

      {/* Icon */}
      <div
        className={cn(
          'flex-shrink-0 mt-0.5 h-8 w-8 rounded-full border flex items-center justify-center',
          config.color,
        )}
      >
        {config.icon}
      </div>

      {/* Content */}
      <div className="flex-1 min-w-0">
        <button
          type="button"
          onClick={() => {
            if (!n.is_read) onMarkRead(n.id);
            onNavigate(n);
          }}
          className="text-left flex-1 min-w-0 w-full"
        >
          <p className={cn('text-sm truncate', !n.is_read ? 'font-semibold' : 'font-medium')}>
            {n.title}
          </p>
          {n.content && (
            <p className="text-xs text-muted-foreground mt-0.5 line-clamp-2">
              {n.content}
            </p>
          )}
          <div className="flex items-center gap-2 mt-1.5">
            <Badge variant="outline" className={cn('text-xs border', config.color)}>
              {config.label}
            </Badge>
            <span className="text-xs text-muted-foreground">{formatTime(n.created_at)}</span>
          </div>
        </button>
      </div>

      {/* Actions */}
      <div className="flex-shrink-0 flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
        {!n.is_read && (
          <Button
            size="sm"
            variant="ghost"
            className="h-7 w-7 p-0"
            onClick={(e) => {
              e.stopPropagation();
              onMarkRead(n.id);
            }}
            title="Mark as read"
          >
            <Check className="h-3.5 w-3.5" />
          </Button>
        )}
        <Button
          size="sm"
          variant="ghost"
          className="h-7 w-7 p-0 text-muted-foreground hover:text-foreground"
          onClick={(e) => {
            e.stopPropagation();
            onArchive(n.id);
          }}
          title="Archive"
        >
          <Archive className="h-3.5 w-3.5" />
        </Button>
      </div>
    </div>
  );
}

interface NotificationGroupProps {
  title: string;
  count: number;
  defaultOpen?: boolean;
  children: React.ReactNode;
}

function NotificationGroup({ title, count, defaultOpen = true, children }: NotificationGroupProps) {
  const [open, setOpen] = useState(defaultOpen);

  return (
    <div className="border rounded-lg bg-card overflow-hidden">
      <button
        type="button"
        className="w-full flex items-center gap-2 px-4 py-2.5 bg-muted/30 hover:bg-muted/50 transition-colors"
        onClick={() => setOpen((o) => !o)}
      >
        {open ? (
          <ChevronDown className="h-4 w-4 text-muted-foreground" />
        ) : (
          <ChevronRight className="h-4 w-4 text-muted-foreground" />
        )}
        <span className="text-sm font-medium">{title}</span>
        <Badge variant="secondary" className="text-xs ml-auto">
          {count}
        </Badge>
      </button>
      {open && children}
    </div>
  );
}

export default function NotifyPage() {
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const [filter, setFilter] = useState<Filter>('all');
  const [groupBy, setGroupBy] = useState<GroupBy>('none');
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
  const [batchMode, setBatchMode] = useState(false);

  const {
    notifications,
    unreadCount,
    markRead,
    markAllRead,
    archive,
    isLive,
  } = useNotification({
    showToast: false,
  });

  const filteredNotifications = notifications.filter((n) => {
    if (filter === 'unread') return !n.is_read;
    if (filter === 'archived') return n.is_archived;
    return !n.is_archived;
  });

  const handleMarkRead = async (id: string) => {
    try {
      await notificationMarkRead({ path: { notification_id: id } });
      markRead(id);
      queryClient.invalidateQueries({ queryKey: ['me'] });
    } catch (err) {
      toast.error(getApiErrorMessage(err, 'Failed to mark as read'));
    }
  };

  const handleArchive = async (id: string) => {
    try {
      await notificationArchive({ path: { notification_id: id } });
      archive(id);
      toast.success('Notification archived');
    } catch (err) {
      toast.error(getApiErrorMessage(err, 'Failed to archive'));
    }
  };

  const handleMarkAllRead = async () => {
    try {
      await notificationMarkAllRead();
      markAllRead();
      queryClient.invalidateQueries({ queryKey: ['me'] });
    } catch (err) {
      toast.error(getApiErrorMessage(err, 'Failed to mark all as read'));
    }
  };

  const handleNavigate = (n: NotificationResponse) => {
    // Determine navigation target from notification metadata
    if (n.related_room_id && n.project) {
      navigate(`/project/${n.project}/room`);
    } else if (n.notification_type.includes('invitation')) {
      navigate('/invitations');
    } else if (n.notification_type.startsWith('issue')) {
      // Navigate to project/issue if project is known
      if (n.project) {
        navigate(`/project/${n.project}/issues`);
      }
    } else if (n.notification_type.startsWith('pr')) {
      if (n.project) {
        navigate(`/project/${n.project}/pull-requests`);
      }
    } else {
      navigate('/notify');
    }
  };

  const toggleSelect = (id: string) => {
    setSelectedIds((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const handleBatchArchive = async () => {
    for (const id of selectedIds) {
      try {
        await notificationArchive({ path: { notification_id: id } });
        archive(id);
      } catch {
        // Continue with others
      }
    }
    setSelectedIds(new Set());
    setBatchMode(false);
    toast.success(`${selectedIds.size} notifications archived`);
  };

  const handleBatchMarkRead = async () => {
    for (const id of selectedIds) {
      try {
        await notificationMarkRead({ path: { notification_id: id } });
        markRead(id);
      } catch {
        // Continue
      }
    }
    queryClient.invalidateQueries({ queryKey: ['me'] });
    setSelectedIds(new Set());
    setBatchMode(false);
  };

  const handleMarkReadForGroup = async (ids: string[]) => {
    for (const id of ids) {
      await notificationMarkRead({ path: { notification_id: id } });
      markRead(id);
    }
    queryClient.invalidateQueries({ queryKey: ['me'] });
  };

  // Group notifications
  const groups = (() => {
    if (groupBy === 'project') {
      const byProject = new Map<string, NotificationResponse[]>();
      for (const n of filteredNotifications) {
        const key = n.project ?? 'Unknown Project';
        if (!byProject.has(key)) byProject.set(key, []);
        byProject.get(key)!.push(n);
      }
      return [...byProject.entries()].map(([title, items]) => ({
        title,
        items,
      }));
    }
    if (groupBy === 'type') {
      const byType = new Map<string, NotificationResponse[]>();
      for (const n of filteredNotifications) {
        const key = getTypeLabel(n.notification_type);
        if (!byType.has(key)) byType.set(key, []);
        byType.get(key)!.push(n);
      }
      return [...byType.entries()].map(([title, items]) => ({
        title,
        items,
      }));
    }
    return [{ title: 'All', items: filteredNotifications }];
  })();

  const total = filteredNotifications.length;

  return (
    <div className="max-w-3xl mx-auto p-6 space-y-4">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-semibold flex items-center gap-2">
            <Bell className="h-5 w-5" />
            Notifications
            {isLive && (
              <span className="flex items-center gap-1 text-xs font-normal text-green-600">
                <span className="h-1.5 w-1.5 rounded-full bg-green-500" />
                Live
              </span>
            )}
          </h1>
          <p className="text-sm text-muted-foreground mt-0.5">
            {unreadCount > 0
              ? `${unreadCount} unread · ${total} total`
              : `${total} notification${total !== 1 ? 's' : ''}`}
          </p>
        </div>
        <div className="flex items-center gap-2">
          {/* Group by selector */}
          <div className="flex items-center gap-1 border rounded-md px-1">
            <SlidersHorizontal className="h-3.5 w-3.5 text-muted-foreground mx-1" />
            <select
              className="bg-transparent text-xs border-0 outline-none py-1 pr-1 cursor-pointer"
              value={groupBy}
              onChange={(e) => setGroupBy(e.target.value as GroupBy)}
            >
              <option value="none">No grouping</option>
              <option value="project">Group by project</option>
              <option value="type">Group by type</option>
            </select>
          </div>

          {batchMode ? (
            <>
              <Button
                size="sm"
                variant="outline"
                onClick={() => {
                  setSelectedIds(new Set());
                  setBatchMode(false);
                }}
              >
                Cancel
              </Button>
              <Button
                size="sm"
                variant="outline"
                onClick={handleBatchMarkRead}
                disabled={selectedIds.size === 0}
              >
                <CheckCheck className="h-3.5 w-3.5 mr-1" />
                Mark read ({selectedIds.size})
              </Button>
              <Button
                size="sm"
                variant="destructive"
                onClick={handleBatchArchive}
                disabled={selectedIds.size === 0}
              >
                <Archive className="h-3.5 w-3.5 mr-1" />
                Archive ({selectedIds.size})
              </Button>
            </>
          ) : (
            <>
              {unreadCount > 0 && filter !== 'archived' && (
                <Button
                  size="sm"
                  variant="outline"
                  onClick={handleMarkAllRead}
                  disabled={markAllRead === undefined}
                >
                  {false ? (
                    <Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />
                  ) : (
                    <CheckCheck className="h-3.5 w-3.5 mr-1.5" />
                  )}
                  Mark all read
                </Button>
              )}
              <Button
                size="sm"
                variant="ghost"
                className="h-8 w-8 p-0"
                onClick={() => navigate('/settings/preferences')}
                title="Notification settings"
              >
                <Settings className="h-4 w-4" />
              </Button>
            </>
          )}
        </div>
      </div>

      {/* Filter tabs */}
      <div className="flex items-center gap-1 border-b">
        {(['all', 'unread', 'archived'] as Filter[]).map((f) => (
          <button
            key={f}
            type="button"
            onClick={() => setFilter(f)}
            className={cn(
              'px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors',
              filter === f
                ? 'border-primary text-primary'
                : 'border-transparent text-muted-foreground hover:text-foreground',
            )}
          >
            {f === 'all' ? 'All' : f === 'unread' ? 'Unread' : 'Archived'}
            {f === 'unread' && unreadCount > 0 && (
              <span className="ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 text-[10px] font-bold text-white px-1">
                {unreadCount}
              </span>
            )}
          </button>
        ))}

        {/* Batch mode toggle */}
        <div className="ml-auto flex items-center">
          <button
            type="button"
            onClick={() => {
              setBatchMode((m) => !m);
              setSelectedIds(new Set());
            }}
            className={cn(
              'px-3 py-2 text-xs font-medium transition-colors',
              batchMode
                ? 'text-primary'
                : 'text-muted-foreground hover:text-foreground',
            )}
          >
            {batchMode ? '✓ Select mode active' : 'Select multiple'}
          </button>
        </div>
      </div>

      {/* List */}
      <div className="space-y-3">
        {total === 0 ? (
          <div className="border rounded-lg bg-card overflow-hidden">
            <div className="flex flex-col items-center justify-center h-48 text-muted-foreground">
              <BellOff className="h-10 w-10 mb-3 opacity-40" />
              <p className="font-medium">
                {filter === 'unread'
                  ? 'No unread notifications'
                  : filter === 'archived'
                  ? 'No archived notifications'
                  : 'No notifications yet'}
              </p>
              <p className="text-sm mt-1">
                {filter === 'unread'
                  ? "You're all caught up!"
                  : filter === 'archived'
                  ? 'Archived notifications will appear here.'
                  : "You'll see notifications here when something happens."}
              </p>
            </div>
          </div>
        ) : groupBy !== 'none' ? (
          groups.map(({ title, items }) => (
            <NotificationGroup key={title} title={title} count={items.length}>
              {items.map((n) => (
                <div key={n.id} className="flex items-start">
                  {batchMode && (
                    <input
                      type="checkbox"
                      className="ml-4 mt-4 mr-0 flex-shrink-0 accent-primary"
                      checked={selectedIds.has(n.id)}
                      onChange={() => toggleSelect(n.id)}
                    />
                  )}
                  <div className="flex-1">
                    <NotificationItem
                      n={n}
                      onMarkRead={handleMarkRead}
                      onArchive={handleArchive}
                      onNavigate={handleNavigate}
                    />
                  </div>
                </div>
              ))}
              <div className="px-4 pb-2 flex justify-end">
                <Button
                  size="sm"
                  variant="ghost"
                  className="h-6 text-xs"
                  onClick={() => handleMarkReadForGroup(items.map((i) => i.id))}
                >
                  <CheckCheck className="h-3 w-3 mr-1" />
                  Mark all read in group
                </Button>
              </div>
            </NotificationGroup>
          ))
        ) : (
          <div className="border rounded-lg bg-card overflow-hidden">
            {filteredNotifications.map((n) => (
              <div key={n.id} className="flex items-start">
                {batchMode && (
                  <input
                    type="checkbox"
                    className="ml-4 mt-4 mr-0 flex-shrink-0 accent-primary"
                    checked={selectedIds.has(n.id)}
                    onChange={() => toggleSelect(n.id)}
                  />
                )}
                <div className="flex-1">
                  <NotificationItem
                    n={n}
                    onMarkRead={handleMarkRead}
                    onArchive={handleArchive}
                    onNavigate={handleNavigate}
                  />
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}
