import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { useRouter } from 'next/navigation';
import DashboardPage from '../page';
import api from '@/lib/api';
import { useAuthStore } from '@/stores/authStore';
import type { Beneficiary } from '@/types';

// Mock dependencies
jest.mock('next/navigation', () => ({
  useRouter: jest.fn(),
}));

jest.mock('@/lib/api');
jest.mock('@/stores/authStore');

describe('DashboardPage', () => {
  const mockRouter = {
    push: jest.fn(),
  };

  const mockBeneficiaries: Beneficiary[] = [
    {
      id: 1,
      name: 'Maria Garcia',
      displayName: 'Maria Garcia',
      email: 'maria@example.com',
      status: 'offline',
      hasDevices: true,
      equipmentStatus: 'active',
      subscription: {
        status: 'active',
        planType: 'monthly',
        endDate: '2025-12-31',
        cancelAtPeriodEnd: false,
      },
      role: 'custodian',
      address: '123 Main St, City',
      avatar: 'https://example.com/avatar.jpg',
    },
    {
      id: 2,
      name: 'John Smith',
      displayName: 'John Smith',
      email: 'john@example.com',
      status: 'offline',
      hasDevices: false,
      equipmentStatus: 'none',
      role: 'caretaker',
    },
  ];

  beforeEach(() => {
    jest.clearAllMocks();
    (useRouter as jest.Mock).mockReturnValue(mockRouter);
    (useAuthStore as unknown as jest.Mock).mockReturnValue({
      isAuthenticated: true,
      user: { firstName: 'Test', user_id: 1 },
    });
  });

  describe('Loading State', () => {
    it('should show loading spinner while fetching data', () => {
      (api.getAllBeneficiaries as jest.Mock).mockImplementation(
        () => new Promise(() => {}) // Never resolves
      );

      render(<DashboardPage />);

      expect(screen.getByRole('status')).toBeInTheDocument();
    });
  });

  describe('Authentication', () => {
    it('should redirect to login if not authenticated', () => {
      (useAuthStore as unknown as jest.Mock).mockReturnValue({
        isAuthenticated: false,
        user: null,
      });

      render(<DashboardPage />);

      expect(mockRouter.push).toHaveBeenCalledWith('/login');
    });

    it('should not redirect if authenticated', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [],
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(mockRouter.push).not.toHaveBeenCalled();
      });
    });
  });

  describe('Data Fetching', () => {
    it('should fetch beneficiaries on mount', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(api.getAllBeneficiaries).toHaveBeenCalledTimes(1);
      });
    });

    it('should display beneficiaries after successful fetch', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('Maria Garcia')).toBeInTheDocument();
        expect(screen.getByText('John Smith')).toBeInTheDocument();
      });
    });

    it('should display error message on fetch failure', async () => {
      const errorMessage = 'Failed to load beneficiaries';
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: false,
        error: { message: errorMessage },
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText(errorMessage)).toBeInTheDocument();
      });
    });

    it('should handle network errors gracefully', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockRejectedValue(
        new Error('Network error')
      );

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('Network error')).toBeInTheDocument();
      });
    });
  });

  describe('Header and Welcome Message', () => {
    it('should display welcome message with user first name', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [],
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText(/Welcome, Test/i)).toBeInTheDocument();
      });
    });

    it('should display welcome message without name if not available', async () => {
      (useAuthStore as unknown as jest.Mock).mockReturnValue({
        isAuthenticated: true,
        user: { user_id: 1 },
      });

      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [],
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText(/Welcome$/i)).toBeInTheDocument();
      });
    });
  });

  describe('Summary Cards', () => {
    it('should display correct total beneficiaries count', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('Total Beneficiaries')).toBeInTheDocument();
        const totalCard = screen.getByText('Total Beneficiaries').closest('div');
        expect(totalCard).toHaveTextContent('2');
      });
    });

    it('should display correct equipment count', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('With Equipment')).toBeInTheDocument();
        const equipmentCard = screen.getByText('With Equipment').closest('div');
        expect(equipmentCard).toHaveTextContent('1'); // Only Maria has equipment
      });
    });

    it('should display correct active subscriptions count', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('Active Subscriptions')).toBeInTheDocument();
        const subscriptionCard = screen.getByText('Active Subscriptions').closest('div');
        expect(subscriptionCard).toHaveTextContent('1'); // Only Maria has active subscription
      });
    });
  });

  describe('Empty State', () => {
    it('should display empty state when no beneficiaries', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [],
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('No beneficiaries yet')).toBeInTheDocument();
        expect(
          screen.getByText(/Add your first loved one to start monitoring/)
        ).toBeInTheDocument();
      });
    });

    it('should show add beneficiary button in empty state', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [],
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const addButton = screen.getByText('Add Your First Beneficiary');
        expect(addButton).toBeInTheDocument();
      });
    });
  });

  describe('Beneficiary Cards', () => {
    it('should display beneficiary avatar if available', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const avatar = screen.getByRole('img', { name: 'Maria Garcia' });
        expect(avatar).toBeInTheDocument();
        expect(avatar).toHaveAttribute('src', 'https://example.com/avatar.jpg');
      });
    });

    it('should display default avatar icon if no image', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: [mockBeneficiaries[1]], // John has no avatar
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const card = screen.getByText('John Smith').closest('button');
        expect(card).toHaveTextContent('👤');
      });
    });

    it('should display equipment status badges correctly', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const activeBadges = screen.getAllByText('Active');
        expect(activeBadges.length).toBeGreaterThan(0);
        expect(screen.getByText('No Equipment')).toBeInTheDocument();
      });
    });

    it('should display subscription status badges', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        // Maria has active subscription - "Active" text appears twice (equipment + subscription)
        const activeBadges = screen.getAllByText('Active');
        expect(activeBadges.length).toBeGreaterThanOrEqual(1);
      });
    });

    it('should display role badges', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText('Custodian')).toBeInTheDocument();
        expect(screen.getByText('Caretaker')).toBeInTheDocument();
      });
    });

    it('should display address if available', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        expect(screen.getByText(/📍 123 Main St, City/)).toBeInTheDocument();
      });
    });
  });

  describe('Navigation', () => {
    it('should navigate to add beneficiary page when clicking add button', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const addButton = screen.getByText('+ Add Beneficiary');
        fireEvent.click(addButton);
      });

      expect(mockRouter.push).toHaveBeenCalledWith('/add-loved-one');
    });

    it('should navigate to beneficiary detail when clicking card', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: true,
        data: mockBeneficiaries,
      });

      render(<DashboardPage />);

      await waitFor(() => {
        const mariaCard = screen.getByText('Maria Garcia').closest('button');
        if (mariaCard) {
          fireEvent.click(mariaCard);
        }
      });

      expect(mockRouter.push).toHaveBeenCalledWith('/beneficiaries/1');
    });
  });

  describe('Equipment Status Badge Colors', () => {
    it.each([
      ['none', 'No Equipment', 'bg-slate-100'],
      ['ordered', 'Ordered', 'bg-blue-100'],
      ['shipped', 'Shipped', 'bg-yellow-100'],
      ['delivered', 'Delivered', 'bg-green-100'],
      ['active', 'Active', 'bg-green-100'],
      ['demo', 'Demo', 'bg-purple-100'],
    ])(
      'should display %s status with correct badge',
      async (status, text, colorClass) => {
        const beneficiary: Beneficiary = {
          ...mockBeneficiaries[0],
          equipmentStatus: status as any,
          // Remove subscription for "active" status test to avoid duplicate "Active" text
          subscription: status === 'active' ? undefined : mockBeneficiaries[0].subscription,
        };

        (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
          ok: true,
          data: [beneficiary],
        });

        const { container } = render(<DashboardPage />);

        await waitFor(() => {
          // Use getAllByText for "Active" which may appear in both equipment and subscription
          const badges = screen.getAllByText(text);
          expect(badges.length).toBeGreaterThan(0);
          expect(badges[0].className).toContain(colorClass);
        }, { timeout: 3000 });
      }
    );
  });

  describe('Subscription Status Badge Colors', () => {
    it.each([
      ['active', 'Active', 'bg-green-100'],
      ['trial', 'Trial', 'bg-blue-100'],
      ['expired', 'Expired', 'bg-red-100'],
      ['cancelled', 'Cancelled', 'bg-slate-100'],
    ])(
      'should display %s subscription with correct badge',
      async (status, text, colorClass) => {
        const beneficiary: Beneficiary = {
          ...mockBeneficiaries[0],
          subscription: {
            status: status as any,
            planType: 'monthly',
            endDate: '2025-12-31',
            cancelAtPeriodEnd: false,
          },
        };

        (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
          ok: true,
          data: [beneficiary],
        });

        const { container } = render(<DashboardPage />);

        await waitFor(() => {
          const badges = screen.getAllByText(text);
          expect(badges.length).toBeGreaterThan(0);
          expect(badges[0].className).toContain(colorClass);
        }, { timeout: 3000 });
      }
    );
  });

  describe('Error Retry', () => {
    it('should show retry button on error', async () => {
      (api.getAllBeneficiaries as jest.Mock).mockResolvedValue({
        ok: false,
        error: { message: 'Test error' },
      });

      render(<DashboardPage />);

      await waitFor(() => {
        // ErrorMessage component uses "Retry" button text
        const retryButton = screen.getByText('Retry');
        expect(retryButton).toBeInTheDocument();
      });
    });
  });
});
