import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { createMockProxyHost } from '../../testUtils/createMockProxyHost';
import ProxyHosts from '../ProxyHosts';
import * as proxyHostsApi from '../../api/proxyHosts';
import * as backupsApi from '../../api/backups';
import * as certificatesApi from '../../api/certificates';
import * as accessListsApi from '../../api/accessLists';
import * as settingsApi from '../../api/settings';
import { toast } from 'react-hot-toast';

// Mock toast
vi.mock('react-hot-toast', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
    loading: vi.fn(),
    dismiss: vi.fn(),
  },
}));

// Mock API modules
vi.mock('../../api/proxyHosts', () => ({
  getProxyHosts: vi.fn(),
  createProxyHost: vi.fn(),
  updateProxyHost: vi.fn(),
  deleteProxyHost: vi.fn(),
  bulkUpdateProxyHostACL: vi.fn(),
  testProxyHostConnection: vi.fn(),
}));

vi.mock('../../api/backups', () => ({
  createBackup: vi.fn(),
  getBackups: vi.fn(),
  restoreBackup: vi.fn(),
  deleteBackup: vi.fn(),
}));

vi.mock('../../api/certificates', () => ({
  getCertificates: vi.fn(),
}));

vi.mock('../../api/accessLists', () => ({
  accessListsApi: {
    list: vi.fn(),
    get: vi.fn(),
    getTemplates: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    testIP: vi.fn(),
  },
}));

vi.mock('../../api/settings', () => ({
  getSettings: vi.fn(),
}));

const mockProxyHosts = [
  createMockProxyHost({ uuid: 'host-1', name: 'Test Host 1', domain_names: 'test1.example.com', forward_host: '192.168.1.10' }),
  createMockProxyHost({ uuid: 'host-2', name: 'Test Host 2', domain_names: 'test2.example.com', forward_host: '192.168.1.20' }),
  createMockProxyHost({ uuid: 'host-3', name: 'Test Host 3', domain_names: 'test3.example.com', forward_host: '192.168.1.30' }),
];

const createQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      gcTime: 0,
    },
    mutations: {
      retry: false,
    },
  },
});

const renderWithProviders = (ui: React.ReactNode) => {
  const queryClient = createQueryClient();
  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter>
        {ui}
      </MemoryRouter>
    </QueryClientProvider>
  );
};

describe('ProxyHosts - Bulk Delete with Backup', () => {
  beforeEach(() => {
    vi.clearAllMocks();

    // Setup default mocks
    vi.mocked(proxyHostsApi.getProxyHosts).mockResolvedValue(mockProxyHosts);
    vi.mocked(certificatesApi.getCertificates).mockResolvedValue([]);
    vi.mocked(accessListsApi.accessListsApi.list).mockResolvedValue([]);
    vi.mocked(settingsApi.getSettings).mockResolvedValue({});
    vi.mocked(backupsApi.createBackup).mockResolvedValue({
      filename: 'backup-2024-01-01-12-00-00.db',
    });
  });

  it('renders bulk delete button when hosts are selected', async () => {
    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using the select-all checkbox (checkboxes[0])
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Bulk delete button should appear in the selection bar
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
  });

  it('shows confirmation modal when delete button is clicked', async () => {
    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Modal should appear
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Should list hosts to be deleted (hosts appear in both table and modal)
    expect(screen.getAllByText('Test Host 1').length).toBeGreaterThan(0);
    expect(screen.getAllByText('Test Host 2').length).toBeGreaterThan(0);
    expect(screen.getAllByText('Test Host 3').length).toBeGreaterThan(0);

    // Should mention automatic backup
    expect(screen.getByText(/automatic backup/i)).toBeTruthy();
  });

  it('creates backup before deleting hosts', async () => {
    vi.mocked(proxyHostsApi.deleteProxyHost).mockResolvedValue();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons and click delete
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Click confirm delete
    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Should create backup first
    await waitFor(() => {
      expect(backupsApi.createBackup).toHaveBeenCalled();
    });

    // Should show loading toast
    expect(toast.loading).toHaveBeenCalledWith('Creating backup before deletion...');

    // Should show success toast with backup filename
    await waitFor(() => {
      expect(toast.success).toHaveBeenCalledWith('Backup created: backup-2024-01-01-12-00-00.db');
    });

    // Should then delete the hosts
    await waitFor(() => {
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalledWith('host-1');
    });
  });

  it('deletes multiple selected hosts after backup', async () => {
    vi.mocked(proxyHostsApi.deleteProxyHost).mockResolvedValue();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Click confirm delete
    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Should create backup first
    await waitFor(() => {
      expect(backupsApi.createBackup).toHaveBeenCalled();
    });

    // Should delete all selected hosts
    await waitFor(() => {
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalledWith('host-1');
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalledWith('host-2');
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalledWith('host-3');
    });

    // Should show success message
    await waitFor(() => {
      expect(toast.success).toHaveBeenCalledWith(
        'Successfully deleted 3 host(s). Backup available for restore.'
      );
    });
  });

  it('reports partial success when some deletions fail', async () => {
    // Make second deletion fail
    vi.mocked(proxyHostsApi.deleteProxyHost)
      .mockResolvedValueOnce()  // host-1 succeeds
      .mockRejectedValueOnce(new Error('Network error'))  // host-2 fails
      .mockResolvedValueOnce();  // host-3 succeeds

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal and confirm
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Wait for backup
    await waitFor(() => {
      expect(backupsApi.createBackup).toHaveBeenCalled();
    });

    // Should show partial success
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('Deleted 2 host(s), 1 failed');
    });
  });

  it('handles backup creation failure', async () => {
    vi.mocked(backupsApi.createBackup).mockRejectedValue(new Error('Backup failed'));

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using select-all checkbox
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal and confirm
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Should show error
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('Backup failed');
    });

    // Should NOT delete hosts if backup fails
    expect(proxyHostsApi.deleteProxyHost).not.toHaveBeenCalled();
  });

  it('closes modal after successful deletion', async () => {
    vi.mocked(proxyHostsApi.deleteProxyHost).mockResolvedValue();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using select-all checkbox
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Click confirm delete
    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Wait for completion
    await waitFor(() => {
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalled();
    });

    // Modal should close
    await waitFor(() => {
      expect(screen.queryByText(/Delete 3 Proxy Hosts?/i)).toBeNull();
    });
  });

  it('clears selection after successful deletion', async () => {
    vi.mocked(proxyHostsApi.deleteProxyHost).mockResolvedValue();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using select-all checkbox
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Should show selection count
    await waitFor(() => {
      expect(screen.getByText(/selected/)).toBeTruthy();
    });

    // Click bulk delete button and confirm (find it via Manage ACL sibling)
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Wait for completion
    await waitFor(() => {
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalled();
    });

    // Selection should be cleared - bulk action buttons should disappear
    await waitFor(() => {
      expect(screen.queryByText('Manage ACL')).toBeNull();
    });
  });

  it('disables confirm button while creating backup', async () => {
    // Make backup creation take time
    vi.mocked(backupsApi.createBackup).mockImplementation(
      () => new Promise(resolve => setTimeout(() => resolve({ filename: 'backup.db' }), 100))
    );
    vi.mocked(proxyHostsApi.deleteProxyHost).mockResolvedValue();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using select-all checkbox
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Click confirm delete
    const confirmButton = screen.getByText('Delete Permanently');
    await userEvent.click(confirmButton);

    // Backup should be called (confirms the button works and backup process starts)
    await waitFor(() => {
      expect(backupsApi.createBackup).toHaveBeenCalled();
    });

    // Wait for deletion to complete to prevent test pollution
    await waitFor(() => {
      expect(proxyHostsApi.deleteProxyHost).toHaveBeenCalled();
    });
  });

  it('can cancel deletion from modal', async () => {
    // Clear mocks to ensure no pollution from previous tests
    vi.mocked(backupsApi.createBackup).mockClear();
    vi.mocked(proxyHostsApi.deleteProxyHost).mockClear();

    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using select-all checkbox
    const checkboxes = screen.getAllByRole('checkbox');
    await userEvent.click(checkboxes[0]);

    // Wait for bulk action buttons to appear, then click bulk delete button
    await waitFor(() => {
      expect(screen.getByText('Manage ACL')).toBeTruthy();
    });
    const manageACLButton = screen.getByText('Manage ACL');
    const deleteButton = manageACLButton.parentElement?.querySelector('button:last-child') as HTMLButtonElement;
    await userEvent.click(deleteButton);

    // Wait for modal
    await waitFor(() => {
      expect(screen.getByText(/Delete 3 Proxy Hosts?/i)).toBeTruthy();
    });

    // Click cancel
    const cancelButton = screen.getByText('Cancel');
    await userEvent.click(cancelButton);

    // Modal should close
    await waitFor(() => {
      expect(screen.queryByText(/Delete 3 Proxy Hosts?/i)).toBeNull();
    });

    // Should NOT create backup or delete
    expect(backupsApi.createBackup).not.toHaveBeenCalled();
    expect(proxyHostsApi.deleteProxyHost).not.toHaveBeenCalled();

    // Selection should remain
    expect(screen.getByText(/selected/i)).toBeTruthy();
  });

  it('shows (all) indicator when all hosts selected for deletion', async () => {
    renderWithProviders(<ProxyHosts />);

    await waitFor(() => {
      expect(screen.getByText('Test Host 1')).toBeTruthy();
    });

    // Select all hosts using the select-all checkbox
    const selectAll = screen.getByLabelText('Select all rows');
    await userEvent.click(selectAll);

    // Should show "(all)" indicator - format is "<strong>3</strong> hosts selected (all)"
    await waitFor(() => {
      expect(screen.getByText(/hosts?\s*selected/)).toBeTruthy();
      expect(screen.getByText(/\(all\)/)).toBeTruthy();
    });
  });
});
