import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

const { mockListCabinetEvents, mockGetSpendingSummary } = vi.hoisted(() => ({
  mockListCabinetEvents: vi.fn(),
  mockGetSpendingSummary: vi.fn(),
}));

const { mockListMedicines } = vi.hoisted(() => ({ mockListMedicines: vi.fn() }));

vi.mock('@/services/cabinet-events', () => ({
  listCabinetEvents: mockListCabinetEvents,
  getSpendingSummary: mockGetSpendingSummary,
}));

vi.mock('@/services/medicines', () => ({ listMedicines: mockListMedicines }));

import { ActivityTab } from '../../../../src/app/(dashboard)/medicines/ActivityTab';

const emptyMeds = { data: [], pagination: { cursor: null, hasMore: false } };
const emptyEvents = { data: [], pagination: { cursor: null, hasMore: false } };
const emptySummary = {
  totalSpent: 0,
  currency: null,
  byMedicine: [],
  byPeriod: [],
};

beforeEach(() => {
  vi.clearAllMocks();
  mockListMedicines.mockResolvedValue(emptyMeds);
  mockListCabinetEvents.mockResolvedValue(emptyEvents);
  mockGetSpendingSummary.mockResolvedValue(emptySummary);
});

describe('ActivityTab', () => {
  it('renders Spending Summary and Cabinet Activity sections', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(screen.getByText('Spending Summary')).toBeInTheDocument());
    expect(screen.getByText('Cabinet Activity')).toBeInTheDocument();
  });

  it('fetches spending summary on mount', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() =>
      expect(mockGetSpendingSummary).toHaveBeenCalledWith('hh1', expect.any(Object)),
    );
  });

  it('fetches events on mount', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() =>
      expect(mockListCabinetEvents).toHaveBeenCalledWith('hh1', expect.any(Object)),
    );
  });

  it('shows empty state when no events', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() =>
      expect(screen.getByText('No events found for the selected filters.')).toBeInTheDocument(),
    );
  });

  it('shows error when events fail to load', async () => {
    mockListCabinetEvents.mockRejectedValue(new Error('Events error'));

    render(<ActivityTab householdId="hh1" />);

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

  it('shows error when spending summary fails', async () => {
    mockGetSpendingSummary.mockRejectedValue(new Error('Spending error'));

    render(<ActivityTab householdId="hh1" />);

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

  it('renders event entries', async () => {
    mockListCabinetEvents.mockResolvedValue({
      data: [
        {
          _id: 'ev-1',
          eventType: 'purchased',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          quantity: 10,
          quantityBefore: 0,
          quantityAfter: 10,
          unit: 'tablet',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
  });

  it('shows spending summary with data', async () => {
    mockGetSpendingSummary.mockResolvedValue({
      totalSpent: 125.5,
      currency: 'USD',
      byMedicine: [
        {
          medicineId: 'med-1',
          medicineName: 'Metformin',
          totalSpent: 125.5,
          purchaseCount: 2,
          avgUnitPrice: 0.69,
        },
      ],
      byPeriod: [{ period: '2026-01', totalSpent: 125.5 }],
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(screen.getAllByText(/125.50/).length).toBeGreaterThan(0));
    expect(screen.getByText('Metformin')).toBeInTheDocument();
    expect(screen.getByText('By medicine')).toBeInTheDocument();
  });

  it('shows no purchase data message when total is zero', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() =>
      expect(screen.getByText('No purchase data found for this period.')).toBeInTheDocument(),
    );
  });

  it('filters events by event type', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalledTimes(1));
    fireEvent.change(screen.getByDisplayValue('All event types'), {
      target: { value: 'purchased' },
    });

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalledTimes(2));
  });

  it('filters events by medicine', async () => {
    mockListMedicines.mockResolvedValue({
      data: [{ _id: 'med-1', name: 'Metformin' }],
      pagination: { cursor: null, hasMore: false },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() =>
      expect(screen.getAllByDisplayValue('All medicines').length).toBeGreaterThan(1),
    );
    const allMedSelects = screen.getAllByDisplayValue('All medicines');
    // The last select is the cabinet events medicine filter
    fireEvent.change(allMedSelects[allMedSelects.length - 1]!, { target: { value: 'med-1' } });

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalledTimes(2));
  });

  it('shows clear filters button when filter applied and clears them', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalled());
    fireEvent.change(screen.getByDisplayValue('All event types'), {
      target: { value: 'purchased' },
    });

    await waitFor(() => screen.getByText('Clear filters'));
    await userEvent.click(screen.getByText('Clear filters'));

    expect(screen.queryByText('Clear filters')).not.toBeInTheDocument();
  });

  it('dismisses cabinet activity error', async () => {
    mockListCabinetEvents.mockRejectedValue(new Error('Events error'));

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => screen.getByText('Events error'));
    await userEvent.click(screen.getByText('Dismiss'));
    expect(screen.queryByText('Events error')).not.toBeInTheDocument();
  });

  it('changes spending summary period and medicine filters', async () => {
    mockListMedicines.mockResolvedValue({
      data: [{ _id: 'med-1', name: 'Metformin' }],
      pagination: { cursor: null, hasMore: false },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(mockGetSpendingSummary).toHaveBeenCalledTimes(1));

    // Change period
    fireEvent.change(screen.getByDisplayValue('This month'), { target: { value: 'quarter' } });

    await waitFor(() => expect(mockGetSpendingSummary).toHaveBeenCalledTimes(2));

    // Change medicine filter in spending summary (first "All medicines" select)
    const medSelects = screen.getAllByDisplayValue('All medicines');
    fireEvent.change(medSelects[0]!, { target: { value: 'med-1' } });

    await waitFor(() => expect(mockGetSpendingSummary).toHaveBeenCalledTimes(3));
  });

  it('changes start and end date filters', async () => {
    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalledTimes(1));

    const dateInputs = document.querySelectorAll('input[type="date"]');
    fireEvent.change(dateInputs[0]!, { target: { value: '2026-01-01' } });
    fireEvent.change(dateInputs[1]!, { target: { value: '2026-03-31' } });

    await waitFor(() => expect(mockListCabinetEvents).toHaveBeenCalledTimes(3));
  });

  it('loads more events when Load more is clicked', async () => {
    mockListCabinetEvents.mockResolvedValue({
      data: [
        {
          _id: 'ev-1',
          eventType: 'purchased',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          quantity: 10,
          quantityBefore: 0,
          quantityAfter: 10,
          unit: 'tablet',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: 'cursor-1', hasMore: true },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => screen.getByText('Load more'));
    await userEvent.click(screen.getByRole('button', { name: 'Load more' }));

    expect(mockListCabinetEvents).toHaveBeenCalledTimes(2);
  });

  it('renders event with negative quantity (red dot)', async () => {
    mockListCabinetEvents.mockResolvedValue({
      data: [
        {
          _id: 'ev-1',
          eventType: 'taken',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          quantity: -1,
          quantityBefore: 10,
          quantityAfter: 9,
          unit: 'tablet',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
  });

  it('renders event with reason, storeName, totalPrice, and notes', async () => {
    mockListCabinetEvents.mockResolvedValue({
      data: [
        {
          _id: 'ev-1',
          eventType: 'purchased',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          quantity: 30,
          quantityBefore: 0,
          quantityAfter: 30,
          unit: 'tablet',
          reason: 'Monthly refill',
          storeName: 'Pharmacy',
          totalPrice: 25.5,
          currency: 'USD',
          notes: 'On sale',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<ActivityTab householdId="hh1" />);

    await waitFor(() => expect(screen.getByText('Metformin')).toBeInTheDocument());
    expect(screen.getByText(/Monthly refill/)).toBeInTheDocument();
    expect(screen.getByText(/Pharmacy/)).toBeInTheDocument();
    expect(screen.getByText(/25.50/)).toBeInTheDocument();
    expect(screen.getByText(/On sale/)).toBeInTheDocument();
  });
});
