import React from 'react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import NotificationsBell, { linkFor, labelFor } from './NotificationsBell';
import { useNotificationsStore } from '../store/notifications';

const mockNavigate = vi.fn();
vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useNavigate: () => mockNavigate,
  };
});

vi.mock('../store/api', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
  },
}));

vi.mock('../store/realtime', () => ({
  useRealtimeStore: {
    getState: () => ({
      subscribe: vi.fn(() => vi.fn()),
    }),
  },
}));

describe('NotificationsBell Deep-Link Routing (linkFor & labelFor)', () => {
  it('correctly maps related_resource types and metadata to target routes with linkFor', () => {
    expect(linkFor('message:123')).toBe('/messages?chat=123');
    expect(linkFor('message:123', { sender_id: 45 })).toBe('/messages?chat=45');
    expect(linkFor('announcement:45')).toBe('/messages?chat=announcements');
    expect(linkFor('club_message:7')).toBe('/messages?chat=7');
    expect(linkFor('exam_group:10')).toBe('/exams/review?id=10');
    expect(linkFor('exam:99')).toBe('/exams/review?id=99');
    expect(linkFor('notice:3')).toBe('/notice-board?id=3');
    expect(linkFor('notice_board:4')).toBe('/notice-board?id=4');
    expect(linkFor('event:5')).toBe('/events?id=5');
    expect(linkFor('leave:12')).toBe('/hr/leave-approval?id=12');
    expect(linkFor('medical:8')).toBe('/sanatorium?id=8');
    expect(linkFor('sanatorium:9')).toBe('/sanatorium?id=9');
    expect(linkFor('/finance/expenses')).toBe('/finance/expenses');
    expect(linkFor('custom_unknown:1')).toBeNull();
    expect(linkFor(null)).toBeNull();
  });

  it('correctly formats category labels with labelFor', () => {
    expect(labelFor('announcement:1')).toBe('Announcement');
    expect(labelFor('message:1')).toBe('Message');
    expect(labelFor('club_message:1')).toBe('Message');
    expect(labelFor('exam_group:1')).toBe('Exam Review');
    expect(labelFor('notice:1')).toBe('Notice');
    expect(labelFor('event:1')).toBe('Event');
    expect(labelFor('leave:1')).toBe('Leave Request');
    expect(labelFor('medical:1')).toBe('Sanatorium');
    expect(labelFor('/enrollments')).toBe('Direct Link');
    expect(labelFor(null)).toBe('Notification');
  });
});

describe('NotificationsBell Component Integration', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    useNotificationsStore.setState({
      items: [],
      unread: 0,
      loading: false,
      inboxOpen: false,
    });
  });

  it('renders the bell button with unread count badge', () => {
    useNotificationsStore.setState({ unread: 5 });

    render(
      <MemoryRouter>
        <NotificationsBell />
      </MemoryRouter>
    );

    expect(screen.getByRole('button', { name: /notifications/i })).toBeTruthy();
    expect(screen.getByText('5')).toBeTruthy();
  });

  it('toggles inbox dropdown and renders notification items', async () => {
    const fetchInboxMock = vi.fn().mockImplementation(async () => {
      useNotificationsStore.setState({
        items: [
          {
            id: 1,
            uid: 'u1',
            type: 'in_app',
            subject: 'Exam Rejected',
            body: 'Physics paper 1 requires revision.',
            related_resource: 'exam_group:42',
            status: 'sent',
            metadata: null,
            created_at: new Date().toISOString(),
            read_at: null,
          },
        ],
        unread: 1,
      });
    });

    useNotificationsStore.setState({
      unread: 1,
      fetchInbox: fetchInboxMock,
    });

    render(
      <MemoryRouter>
        <NotificationsBell />
      </MemoryRouter>
    );

    // Open dropdown
    const bellBtn = screen.getByRole('button', { name: /notifications/i });
    fireEvent.click(bellBtn);

    await waitFor(() => {
      expect(screen.getByText('Inbox')).toBeTruthy();
      expect(screen.getByText('Exam Rejected')).toBeTruthy();
      expect(screen.getByText('Exam Review')).toBeTruthy();
    });
  });

  it('marks row as read and deep-links directly to notification origin when clicked', async () => {
    const markReadMock = vi.fn();
    const mockItem = {
      id: 10,
      uid: 'u10',
      type: 'in_app' as const,
      subject: 'New Message',
      body: 'Hello teacher',
      related_resource: 'message:99',
      status: 'sent' as const,
      metadata: { sender_id: 99 },
      created_at: new Date().toISOString(),
      read_at: null,
    };
    useNotificationsStore.setState({
      inboxOpen: true,
      unread: 1,
      items: [mockItem],
      markRead: markReadMock,
      fetchInbox: vi.fn().mockImplementation(async () => {
        useNotificationsStore.setState({ items: [mockItem], unread: 1 });
      }),
    });

    render(
      <MemoryRouter>
        <NotificationsBell />
      </MemoryRouter>
    );

    const rowItem = screen.getByText('New Message');
    fireEvent.click(rowItem);

    expect(markReadMock).toHaveBeenCalledWith(10);
    expect(mockNavigate).toHaveBeenCalledWith('/messages?chat=99');
    expect(useNotificationsStore.getState().inboxOpen).toBe(false);
  });
});
