import { mount, flushPromises } from '@vue/test-utils';
import LoginPage from '../LoginPage.vue'; // Adjust path
import { useAuthStore } from '@/stores/auth';
import { useNotificationStore } from '@/stores/notifications';
import { useRouter, useRoute } from 'vue-router';
import { vi } from 'vitest';

// --- Mocks ---
vi.mock('@/stores/auth', () => ({
  useAuthStore: vi.fn(() => ({
    login: vi.fn(),
  })),
}));

vi.mock('@/stores/notifications', () => ({
  useNotificationStore: vi.fn(() => ({
    addNotification: vi.fn(),
  })),
}));

const mockRouterPush = vi.fn();
const mockRouteQuery = ref({}); // Use ref for reactive query params if needed by component

vi.mock('vue-router', () => ({
  useRouter: vi.fn(() => ({
    push: mockRouterPush,
  })),
  useRoute: vi.fn(() => mockRouteQuery.value), // Access .value for potential reactivity
}));


// --- Test Data & Helpers ---
let mockAuthStore: ReturnType<typeof useAuthStore>;
let mockNotificationStore: ReturnType<typeof useNotificationStore>;

const createWrapper = (props = {}) => {
  return mount(LoginPage, {
    props,
    global: {
      stubs: {
        SocialLoginButtons: true, // Stub the SocialLoginButtons component
        RouterLink: { template: '<a><slot/></a>' } // Basic stub for RouterLink
      }
    }
  });
};

// --- Test Suite ---
describe('LoginPage.vue', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockAuthStore = useAuthStore() as vi.Mocked<ReturnType<typeof useAuthStore>>;
    (useAuthStore as vi.Mock).mockReturnValue(mockAuthStore);
    mockNotificationStore = useNotificationStore() as vi.Mocked<ReturnType<typeof useNotificationStore>>;
    (useNotificationStore as vi.Mock).mockReturnValue(mockNotificationStore);
    mockRouteQuery.value = { query: {} }; // Reset route query for each test
  });

  describe('Rendering', () => {
    it('renders the login form correctly', () => {
      const wrapper = createWrapper();
      expect(wrapper.find('h3').text()).toBe('mitlist');
      expect(wrapper.find('input#email').exists()).toBe(true);
      expect(wrapper.find('input#password').exists()).toBe(true);
      expect(wrapper.find('button[type="submit"]').text()).toBe('Login');
      expect(wrapper.find('a[href="/auth/signup"]').text()).toBe("Don't have an account? Sign up");
    });

    it('renders the SocialLoginButtons component', () => {
      const wrapper = createWrapper();
      expect(wrapper.findComponent({ name: 'SocialLoginButtons' }).exists()).toBe(true);
    });

    it('renders password visibility toggle button', () => {
      const wrapper = createWrapper();
      expect(wrapper.find('button[aria-label="Toggle password visibility"]').exists()).toBe(true);
    });
  });

  describe('Form Input and Validation', () => {
    it('binds email and password to data properties', async () => {
      const wrapper = createWrapper();
      const emailInput = wrapper.find('input#email');
      const passwordInput = wrapper.find('input#password');

      await emailInput.setValue('test@example.com');
      await passwordInput.setValue('password123');

      expect(wrapper.vm.email).toBe('test@example.com');
      expect(wrapper.vm.password).toBe('password123');
    });

    it('validates empty email and shows error', async () => {
      const wrapper = createWrapper();
      await wrapper.find('input#password').setValue('password123');
      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(wrapper.vm.formErrors.email).toBe('Email is required');
      expect(wrapper.text()).toContain('Email is required');
      expect(mockAuthStore.login).not.toHaveBeenCalled();
    });

    it('validates invalid email format and shows error', async () => {
      const wrapper = createWrapper();
      await wrapper.find('input#email').setValue('invalid-email');
      await wrapper.find('input#password').setValue('password123');
      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(wrapper.vm.formErrors.email).toBe('Invalid email format');
      expect(wrapper.text()).toContain('Invalid email format');
      expect(mockAuthStore.login).not.toHaveBeenCalled();
    });

    it('validates empty password and shows error', async () => {
      const wrapper = createWrapper();
      await wrapper.find('input#email').setValue('test@example.com');
      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(wrapper.vm.formErrors.password).toBe('Password is required');
      expect(wrapper.text()).toContain('Password is required');
      expect(mockAuthStore.login).not.toHaveBeenCalled();
    });
  });

  describe('Login Submission and Outcomes', () => {
    it('calls authStore.login on successful submission and redirects to home', async () => {
      const wrapper = createWrapper();
      const email = 'test@example.com';
      const password = 'password123';

      await wrapper.find('input#email').setValue(email);
      await wrapper.find('input#password').setValue(password);

      mockAuthStore.login.mockResolvedValueOnce(undefined); // Simulate successful login

      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(mockAuthStore.login).toHaveBeenCalledWith(email, password);
      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: 'Login successful', type: 'success' });
      expect(mockRouterPush).toHaveBeenCalledWith('/');
      expect(wrapper.vm.formErrors.general).toBeUndefined();
    });

    it('redirects to query parameter on successful login if present', async () => {
      const redirectPath = '/dashboard/special';
      mockRouteQuery.value = { query: { redirect: redirectPath } }; // Set route query before mounting for this test
      const wrapper = createWrapper(); // Re-mount for new route context

      await wrapper.find('input#email').setValue('test@example.com');
      await wrapper.find('input#password').setValue('password123');
      mockAuthStore.login.mockResolvedValueOnce(undefined);

      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(mockRouterPush).toHaveBeenCalledWith(redirectPath);
    });

    it('displays general error message on login failure', async () => {
      const wrapper = createWrapper();
      const email = 'test@example.com';
      const password = 'wrongpassword';
      const errorMessage = 'Invalid credentials provided.';

      await wrapper.find('input#email').setValue(email);
      await wrapper.find('input#password').setValue(password);

      mockAuthStore.login.mockRejectedValueOnce(new Error(errorMessage));

      await wrapper.find('form').trigger('submit.prevent');
      await flushPromises();

      expect(mockAuthStore.login).toHaveBeenCalledWith(email, password);
      expect(wrapper.vm.formErrors.general).toBe(errorMessage);
      expect(wrapper.find('.alert-error.form-error-text').text()).toBe(errorMessage);
      expect(mockNotificationStore.addNotification).toHaveBeenCalledWith({ message: errorMessage, type: 'error' });
      expect(mockRouterPush).not.toHaveBeenCalled();
    });

    it('shows loading spinner during login attempt', async () => {
      const wrapper = createWrapper();
      await wrapper.find('input#email').setValue('test@example.com');
      await wrapper.find('input#password').setValue('password123');

      mockAuthStore.login.mockImplementationOnce(() => new Promise(resolve => setTimeout(resolve, 100))); // Delayed promise

      wrapper.find('form').trigger('submit.prevent'); // Don't await flushPromises immediately
      await wrapper.vm.$nextTick(); // Allow loading state to set

      expect(wrapper.vm.loading).toBe(true);
      expect(wrapper.find('button[type="submit"] .spinner-dots-sm').exists()).toBe(true);
      expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeDefined();

      await flushPromises(); // Now resolve the login
      expect(wrapper.vm.loading).toBe(false);
      expect(wrapper.find('button[type="submit"] .spinner-dots-sm').exists()).toBe(false);
      expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeUndefined();
    });
  });

  describe('Password Visibility Toggle', () => {
    it('toggles password input type when visibility button is clicked', async () => {
      const wrapper = createWrapper();
      const passwordInput = wrapper.find('input#password');
      const toggleButton = wrapper.find('button[aria-label="Toggle password visibility"]');

      // Initially password type
      expect(passwordInput.attributes('type')).toBe('password');
      expect(wrapper.vm.isPwdVisible).toBe(false);

      // Click to make visible
      await toggleButton.trigger('click');
      expect(passwordInput.attributes('type')).toBe('text');
      expect(wrapper.vm.isPwdVisible).toBe(true);
      // Placeholder for icon check: expect(toggleButton.find('use').attributes('xlink:href')).toBe('#icon-eye-open');

      // Click to hide again
      await toggleButton.trigger('click');
      expect(passwordInput.attributes('type')).toBe('password');
      expect(wrapper.vm.isPwdVisible).toBe(false);
      // Placeholder for icon check: expect(toggleButton.find('use').attributes('xlink:href')).toBe('#icon-eye-closed');
    });
  });
});

// Need to use `ref` from 'vue' for mockRouteQuery if it's meant to be reactive
// However, for basic query params, direct object might be fine for useRoute mock.
// Let's import it for correctness if the component structure relies on reactivity from useRoute.
import { ref } from 'vue';
