import { mount, flushPromises, DOMWrapper } from '@vue/test-utils';
import ChoresPage from '../ChoresPage.vue'; // Adjust path
import { choreService } from '@/services/choreService';
import { groupService } from '@/services/groupService'; // Assuming loadGroups will use this
import { useNotificationStore } from '@/stores/notifications';
import { vi } from 'vitest';
import { format } from 'date-fns'; // Used by the component

// --- Mocks ---
vi.mock('@/services/choreService');
vi.mock('@/services/groupService');
vi.mock('@/stores/notifications', () => ({
  useNotificationStore: vi.fn(() => ({
    addNotification: vi.fn(),
  })),
}));
vi.mock('vue-router', () => ({
  useRoute: vi.fn(() => ({ query: {} })), // Default mock for useRoute
}));

const mockChoreService = choreService as vi.Mocked<typeof choreService>;
const mockGroupService = groupService as vi.Mocked<typeof groupService>;
let mockNotificationStore: ReturnType<typeof useNotificationStore>;

// --- Test Data ---
const mockUserGroups = [
  { id: 1, name: 'Family', members: [], owner_id: 1, created_at: '', updated_at: '' },
  { id: 2, name: 'Work', members: [], owner_id: 1, created_at: '', updated_at: '' },
];
const mockPersonalChores = [
  { id: 101, name: 'Personal Task 1', type: 'personal' as const, frequency: 'daily' as const, next_due_date: '2023-10-01', description: 'My personal chore' },
];
const mockFamilyChores = [
  { id: 201, name: 'Clean Kitchen', type: 'group' as const, group_id: 1, frequency: 'weekly' as const, next_due_date: '2023-10-05', description: 'Family group chore' },
];
const mockWorkChores = [
  { id: 301, name: 'Project Report', type: 'group' as const, group_id: 2, frequency: 'monthly' as const, next_due_date: '2023-10-10', description: 'Work group chore' },
];
const allMockChores = [...mockPersonalChores, ...mockFamilyChores, ...mockWorkChores];

// Helper function to create a wrapper
const createWrapper = (props = {}) => {
  return mount(ChoresPage, {
    props,
    global: {
      stubs: {
        // Could stub complex child components if needed, but not strictly necessary here yet
      }
    }
  });
};

describe('ChoresPage.vue', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockChoreService.getAllChores.mockResolvedValue([...allMockChores]); // Default success
    // Mock for loadGroups, assuming it will call groupService.getUserGroups
    // The component's loadGroups is a placeholder, so this mock is for when it's implemented.
    // For now, we'll manually set groups ref in tests that need it for the modal.
    mockGroupService.getUserGroups.mockResolvedValue([...mockUserGroups]);

    mockNotificationStore = useNotificationStore() as vi.Mocked<ReturnType<typeof useNotificationStore>>;
    (useNotificationStore as vi.Mock).mockReturnValue(mockNotificationStore);
  });

  describe('Rendering and Initial Data Fetching', () => {
    it('fetches and displays chores on mount, grouped correctly', async () => {
      const wrapper = createWrapper();
      await flushPromises(); // For onMounted, getAllChores, loadGroups

      expect(mockChoreService.getAllChores).toHaveBeenCalled();
      // expect(mockGroupService.getUserGroups).toHaveBeenCalled(); // If loadGroups was implemented

      // Check personal chores
      expect(wrapper.text()).toContain('Personal Chores');
      expect(wrapper.text()).toContain('Personal Task 1');
      expect(wrapper.text()).toContain(format(new Date(mockPersonalChores[0].next_due_date), 'MMM d, yyyy'));


      // Check group chores
      expect(wrapper.text()).toContain(mockUserGroups[0].name); // Family
      expect(wrapper.text()).toContain('Clean Kitchen');
      expect(wrapper.text()).toContain(format(new Date(mockFamilyChores[0].next_due_date), 'MMM d, yyyy'));


      expect(wrapper.text()).toContain(mockUserGroups[1].name); // Work
      expect(wrapper.text()).toContain('Project Report');
      expect(wrapper.text()).toContain(format(new Date(mockWorkChores[0].next_due_date), 'MMM d, yyyy'));
    });

    it('displays "No chores found" message when no chores exist', async () => {
      mockChoreService.getAllChores.mockResolvedValueOnce([]);
      const wrapper = createWrapper();
      await flushPromises();
      expect(wrapper.text()).toContain('No chores found. Get started by adding a new chore!');
    });
    
    it('displays "No chores in this group" if a group has no chores but others do', async () => {
        mockChoreService.getAllChores.mockResolvedValueOnce([...mockPersonalChores]); // Only personal chores
        const wrapper = createWrapper();
        // Manually set groups for the dropdown/display logic
        wrapper.vm.groups = [...mockUserGroups]; 
        await flushPromises();
    
        expect(wrapper.text()).toContain('Personal Chores');
        expect(wrapper.text()).toContain('Personal Task 1');
        // Check for group sections and their "no chores" message
        for (const group of mockUserGroups) {
          expect(wrapper.text()).toContain(group.name); // Group title should be there
          // This relies on group.chores being empty in the computed `groupedChores`
          // which it will be if getAllChores returns no chores for that group.
          const groupSection = wrapper.findAll('h2.chores-group-title').find(h => h.text() === group.name);
          expect(groupSection).toBeTruthy();
          // Find the <p>No chores in this group.</p> within this group's section.
          // This is a bit tricky; better to add test-ids or more specific selectors.
          // For now, a broad check:
          const groupCards = groupSection?.element.parentElement?.querySelectorAll('.neo-grid .neo-card');
          if (!groupCards || groupCards.length === 0) {
            expect(groupSection?.element.parentElement?.textContent).toContain('No chores in this group.');
          }
        }
      });

    it('handles error during chore fetching', async () => {
      const error = new Error('Failed to load');
      mockChoreService.getAllChores.mockRejectedValueOnce(error);
      const wrapper = createWrapper();
      await flushPromises();

      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({
        message: 'Failed to load chores', type: 'error'
      });
    });
  });

  describe('Chore Creation', () => {
    it('opens create modal, submits form for personal chore, and reloads chores', async () => {
      const wrapper = createWrapper();
      await flushPromises(); // Initial load
      // Manually set groups for the dropdown in the modal
      wrapper.vm.groups = [...mockUserGroups]; 
      await flushPromises();


      await wrapper.find('button.btn-primary').filter(b => b.text().includes('New Chore')).trigger('click');
      expect(wrapper.vm.showChoreModal).toBe(true);
      expect(wrapper.vm.isEditing).toBe(false);

      // Fill form
      await wrapper.find('#name').setValue('New Test Personal Chore');
      await wrapper.find('input[type="radio"][value="personal"]').setValue(true);
      await wrapper.find('#description').setValue('A description');
      await wrapper.find('#dueDate').setValue('2023-12-01');
      // Frequency is 'daily' by default

      mockChoreService.createChore.mockResolvedValueOnce({ id: 999, name: 'New Test Personal Chore', type: 'personal' } as any);
      mockChoreService.getAllChores.mockResolvedValueOnce([]); // For reload

      await wrapper.find('.neo-modal-footer .btn-primary').trigger('click'); // Save button
      await flushPromises();

      expect(mockChoreService.createChore).toHaveBeenCalledWith(expect.objectContaining({
        name: 'New Test Personal Chore',
        type: 'personal',
        description: 'A description',
        next_due_date: '2023-12-01',
      }));
      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: 'Chore created successfully', type: 'success' });
      expect(wrapper.vm.showChoreModal).toBe(false);
      expect(mockChoreService.getAllChores).toHaveBeenCalledTimes(2); // Initial + reload
    });

    it('submits form for group chore correctly', async () => {
        const wrapper = createWrapper();
        await flushPromises();
        wrapper.vm.groups = [...mockUserGroups];
        await flushPromises();
  
        await wrapper.find('button.btn-primary').filter(b => b.text().includes('New Chore')).trigger('click');
        
        await wrapper.find('#name').setValue('New Test Group Chore');
        await wrapper.find('input[type="radio"][value="group"]').setValue(true);
        await wrapper.find('#group').setValue(mockUserGroups[0].id.toString()); // Select first group
        await wrapper.find('#description').setValue('Group chore desc');
        await wrapper.find('#dueDate').setValue('2023-12-02');
  
        mockChoreService.createChore.mockResolvedValueOnce({ id: 998, name: 'New Test Group Chore', type: 'group', group_id: mockUserGroups[0].id } as any);
  
        await wrapper.find('.neo-modal-footer .btn-primary').trigger('click'); // Save button
        await flushPromises();
  
        expect(mockChoreService.createChore).toHaveBeenCalledWith(expect.objectContaining({
          name: 'New Test Group Chore',
          type: 'group',
          group_id: mockUserGroups[0].id,
          description: 'Group chore desc',
          next_due_date: '2023-12-02',
        }));
        expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: 'Chore created successfully', type: 'success' });
      });
  });

  describe('Chore Editing', () => {
    it('opens edit modal with chore data, submits changes, and reloads', async () => {
      const wrapper = createWrapper();
      await flushPromises(); // Initial load
      wrapper.vm.groups = [...mockUserGroups];
      await flushPromises();

      // Find the first "Edit" button (for the first personal chore)
      const editButton = wrapper.findAll('.btn-primary.btn-sm').find(b => b.text().includes('Edit'));
      expect(editButton).toBeTruthy();
      await editButton!.trigger('click');
      
      expect(wrapper.vm.showChoreModal).toBe(true);
      expect(wrapper.vm.isEditing).toBe(true);
      expect(wrapper.find<HTMLInputElement>('#name').element.value).toBe(mockPersonalChores[0].name);

      await wrapper.find('#name').setValue('Updated Personal Chore Name');
      // The next_due_date in form is 'yyyy-MM-dd', original data might be different format
      // The component tries to format it.
      expect(wrapper.find<HTMLInputElement>('#dueDate').element.value).toBe(format(new Date(mockPersonalChores[0].next_due_date), 'yyyy-MM-dd'));

      mockChoreService.updateChore.mockResolvedValueOnce({ ...mockPersonalChores[0], name: 'Updated Personal Chore Name' } as any);
      mockChoreService.getAllChores.mockResolvedValueOnce([]); // For reload

      await wrapper.find('.neo-modal-footer .btn-primary').trigger('click'); // Save button
      await flushPromises();

      expect(mockChoreService.updateChore).toHaveBeenCalledWith(
        mockPersonalChores[0].id,
        expect.objectContaining({ name: 'Updated Personal Chore Name' })
      );
      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: 'Chore updated successfully', type: 'success' });
      expect(wrapper.vm.showChoreModal).toBe(false);
      expect(mockChoreService.getAllChores).toHaveBeenCalledTimes(2); // Initial + reload
    });
  });

  describe('Chore Deletion', () => {
    it('opens delete confirmation, confirms deletion, and reloads', async () => {
      const wrapper = createWrapper();
      await flushPromises(); // Initial load

      const choreToDelete = mockPersonalChores[0];
      // Find the first "Delete" button
      const deleteButton = wrapper.findAll('.btn-danger.btn-sm').find(b => b.text().includes('Delete'));
      expect(deleteButton).toBeTruthy();
      await deleteButton!.trigger('click');

      expect(wrapper.vm.showDeleteDialog).toBe(true);
      expect(wrapper.vm.selectedChore).toEqual(choreToDelete);

      mockChoreService.deleteChore.mockResolvedValueOnce(); // Mock successful deletion
      mockChoreService.getAllChores.mockResolvedValueOnce([]); // For reload

      await wrapper.find('.neo-modal-footer .btn-danger').trigger('click'); // Confirm Delete button
      await flushPromises();

      expect(mockChoreService.deleteChore).toHaveBeenCalledWith(choreToDelete.id, choreToDelete.type, choreToDelete.group_id);
      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: 'Chore deleted successfully', type: 'success' });
      expect(wrapper.vm.showDeleteDialog).toBe(false);
      expect(mockChoreService.getAllChores).toHaveBeenCalledTimes(2); // Initial + reload
    });
  });
});
