/* eslint-disable import-x/order */
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { ListingDetail } from '@/lib/listings-api';

// ─── Mock next/navigation ────────────────────────────────────────────────────

const mockPush = vi.fn();
vi.mock('next/navigation', () => ({
  useRouter: () => ({ push: mockPush }),
  usePathname: () => '/vi/listings',
  useSearchParams: () => new URLSearchParams(),
}));

// ─── Mock next-intl (sử dụng bởi PropertyCard → AddToCompareButton) ──────────

vi.mock('next-intl', () => ({
  useTranslations: () => (key: string) => key,
  useLocale: () => 'vi',
  NextIntlClientProvider: ({ children }: { children: React.ReactNode }) => children,
}));

// ─── Mock next/link & next/image ─────────────────────────────────────────────

vi.mock('next/link', () => ({
  default: ({ children, href, ...props }: { children: React.ReactNode; href: string; [key: string]: unknown }) => (
    <a href={href} {...props}>{children}</a>
  ),
}));

vi.mock('next/image', () => ({
  default: (props: Record<string, unknown>) => <img {...props} />,
}));

// ─── Mock comparison button (dùng next-intl bên trong) ───────────────────────

vi.mock('@/components/comparison/add-to-compare-button', () => ({
  AddToCompareButton: () => null,
}));

// ─── Mock listings API ───────────────────────────────────────────────────────

vi.mock('@/lib/listings-api', () => ({
  listingsApi: {
    search: vi.fn(),
  },
}));

// ─── Helpers ─────────────────────────────────────────────────────────────────

function makeProperty(overrides: Partial<ListingDetail['property']> = {}): ListingDetail['property'] {
  return {
    id: 'prop-default',
    propertyType: 'APARTMENT',
    title: 'Căn hộ test',
    description: 'Mô tả',
    address: '123 Test',
    ward: 'Phường Test',
    district: 'Quận 7',
    city: 'Hồ Chí Minh',
    areaM2: 75,
    usableAreaM2: null,
    bedrooms: 2,
    bathrooms: 2,
    floors: null,
    floor: null,
    totalFloors: null,
    direction: null,
    yearBuilt: null,
    legalStatus: null,
    amenities: null,
    nearbyPOIs: null,
    metroDistanceM: null,
    projectName: null,
    latitude: 10.73,
    longitude: 106.73,
    furnishing: null,
    propertyCondition: null,
    balconyDirection: null,
    maintenanceFeeVND: null,
    parkingSlots: null,
    viewType: [],
    petFriendly: null,
    suitableFor: [],
    whyThisLocation: null,
    media: [],
    thumbnail: null,
    ...overrides,
  };
}

function makeListing(id: string, priceVND: string, district: string): ListingDetail {
  return {
    id,
    status: 'ACTIVE',
    transactionType: 'SALE',
    priceVND,
    pricePerM2: null,
    rentPriceMonthly: null,
    commissionPct: null,
    viewCount: 42,
    saveCount: 3,
    inquiryCount: 1,
    publishedAt: '2025-01-01T00:00:00.000Z',
    createdAt: '2025-01-01T00:00:00.000Z',
    valuationEstimate: null,
    agentQualityScore: null,
    similarCount: 0,
    property: makeProperty({ id: `prop-${id}`, district }),
    seller: { id: 'seller-1', fullName: 'Nguyễn Văn A', phone: '0912345678' },
    agent: null,
  };
}

// id rõ ràng để shortId dễ distinguish:
// shortId lấy slice(0,5): 'aaaaa' → 'AAAAA', 'bbbbb' → 'BBBBB', 'ccccc' → 'CCCCC'
const LISTING_A = makeListing('aaaaa-cheap', '1500000000', 'Quận 1');
const LISTING_B = makeListing('bbbbb-mid',   '5000000000', 'Quận 7');
const LISTING_C = makeListing('ccccc-dear',  '8000000000', 'Quận 3');

const mockListings = {
  data: [LISTING_A, LISTING_B, LISTING_C],
  total: 3,
  page: 1,
  limit: 50,
  totalPages: 1,
};

// ─── Imports phụ thuộc mock ───────────────────────────────────────────────────

import { listingsApi } from '@/lib/listings-api';
import ListingsPage from '../page';

const mockedApi = vi.mocked(listingsApi);

function renderWithProviders(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  return render(
    <QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>,
  );
}

// ─── Tests ────────────────────────────────────────────────────────────────────

describe('ListingsPage — ticker table', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedApi.search.mockResolvedValue(mockListings as never);
  });

  // ── Render cơ bản ──────────────────────────────────────────────────────────

  it('hiển thị tiêu đề trang', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByText('Thị Trường BĐS')).toBeInTheDocument();
    });
  });

  it('gọi API với status=ACTIVE khi mount', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(mockedApi.search).toHaveBeenCalledWith(
        expect.objectContaining({ status: 'ACTIVE' }),
      );
    });
  });

  it('hiển thị header cột bảng đúng', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      const table = screen.getByRole('table');
      const headers = table.querySelectorAll('thead th');
      const headerTexts = Array.from(headers).map((h) => h.textContent?.trim());
      expect(headerTexts).toContain('#');
      expect(headerTexts).toContain('Mã');
      expect(headerTexts).toContain('Quận/Phường');
      expect(headerTexts).toContain('Giá');
      expect(headerTexts).toContain('Δ30d');
      expect(headerTexts).toContain('DT m²');
    });
  });

  it('hiển thị dấu — cho cột Δ30d (chưa có dữ liệu API)', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      // Tất cả 3 rows phải hiển thị "—" vì API chưa có field priceDelta30d.
      const dashes = screen.getAllByText('—');
      expect(dashes.length).toBeGreaterThanOrEqual(3);
    });
  });

  it('hiển thị mã tin dạng GG-XXXXX', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByText('GG-AAAAA')).toBeInTheDocument();
      expect(screen.getByText('GG-BBBBB')).toBeInTheDocument();
      expect(screen.getByText('GG-CCCCC')).toBeInTheDocument();
    });
  });

  it('hiển thị số lượng kết quả khi load xong', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByText(/3 bất động sản đang niêm yết/)).toBeInTheDocument();
    });
  });

  it('hiển thị thông báo lỗi khi API thất bại', async () => {
    mockedApi.search.mockRejectedValue(new Error('Network error'));
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByText(/Không thể tải danh sách/)).toBeInTheDocument();
    });
  });

  // ── Sort ───────────────────────────────────────────────────────────────────

  it('bảng hiển thị đúng 3 rows dữ liệu', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      const rows = screen.getAllByRole('row');
      // 1 header row + 3 data rows
      expect(rows.length).toBe(4);
    });
  });

  it('sort desc theo Ngày đăng mặc định — rows hiển thị theo thứ tự API', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      const rows = screen.getAllByRole('row');
      // 1 header + 3 data rows
      expect(rows.length).toBe(4);
      // All 3 listings should be visible
      expect(rows[1]?.textContent).toContain('GG-AAAAA');
    });
  });

  it('toggle sort Giá: click header Giá 2 lần để đổi chiều sort', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      expect(screen.getByRole('table')).toBeInTheDocument();
    });

    const table = screen.getByRole('table');
    const giaHeader = Array.from(table.querySelectorAll('thead th')).find(
      (th) => th.textContent?.trim() === 'Giá',
    ) as HTMLElement;

    expect(giaHeader).toBeTruthy();

    // Click một lần (desc đầu tiên) — listing đắt nhất phải lên đầu
    await user.click(giaHeader);
    let rows = screen.getAllByRole('row').slice(1);
    expect(rows.length).toBe(3);
    expect(rows[0]?.textContent).toContain('GG-CCCCC');

    // Click lần hai (asc) — listing rẻ nhất lên đầu
    await user.click(giaHeader);
    rows = screen.getAllByRole('row').slice(1);
    expect(rows[0]?.textContent).toContain('GG-AAAAA');
  });

  it('sort theo DT m² khi click header đó', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      expect(screen.getByRole('table')).toBeInTheDocument();
    });

    const table = screen.getByRole('table');
    const dtHeader = Array.from(table.querySelectorAll('thead th')).find(
      (th) => th.textContent?.trim().includes('DT m²'),
    ) as HTMLElement;

    await user.click(dtHeader);
    // Sau sort không crash — rows vẫn hiển thị
    const rows = screen.getAllByRole('row').slice(1);
    expect(rows.length).toBe(3);
  });

  // ── Toggle view ────────────────────────────────────────────────────────────

  it('hiển thị bảng mặc định (table mode)', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByRole('table')).toBeInTheDocument();
    });
  });

  it('chuyển sang card mode khi click nút Chế độ thẻ', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      expect(screen.getByRole('button', { name: /chế độ thẻ/i })).toBeInTheDocument();
    });

    await user.click(screen.getByRole('button', { name: /chế độ thẻ/i }));

    // Bảng biến mất
    expect(screen.queryByRole('table')).not.toBeInTheDocument();
  });

  it('quay lại table mode khi click nút Chế độ bảng', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      expect(screen.getByRole('button', { name: /chế độ thẻ/i })).toBeInTheDocument();
    });

    await user.click(screen.getByRole('button', { name: /chế độ thẻ/i }));
    expect(screen.queryByRole('table')).not.toBeInTheDocument();

    await user.click(screen.getByRole('button', { name: /chế độ bảng/i }));
    await waitFor(() => {
      expect(screen.getByRole('table')).toBeInTheDocument();
    });
  });

  it('nút toggle giữ aria-pressed đúng trạng thái', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      const tableBtn = screen.getByRole('button', { name: /chế độ bảng/i });
      const cardBtn = screen.getByRole('button', { name: /chế độ thẻ/i });
      expect(tableBtn).toHaveAttribute('aria-pressed', 'true');
      expect(cardBtn).toHaveAttribute('aria-pressed', 'false');
    });

    await user.click(screen.getByRole('button', { name: /chế độ thẻ/i }));

    const tableBtn = screen.getByRole('button', { name: /chế độ bảng/i });
    const cardBtn = screen.getByRole('button', { name: /chế độ thẻ/i });
    expect(tableBtn).toHaveAttribute('aria-pressed', 'false');
    expect(cardBtn).toHaveAttribute('aria-pressed', 'true');
  });

  // ── Filter ─────────────────────────────────────────────────────────────────

  it('hiển thị filter bar với các select', async () => {
    renderWithProviders(<ListingsPage />);
    await waitFor(() => {
      expect(screen.getByRole('combobox', { name: /loại giao dịch/i })).toBeInTheDocument();
      expect(screen.getByRole('combobox', { name: /loại bđs/i })).toBeInTheDocument();
      expect(screen.getByRole('combobox', { name: /quận\/huyện/i })).toBeInTheDocument();
      expect(screen.getByRole('combobox', { name: /khoảng giá/i })).toBeInTheDocument();
    });
  });

  // ── Navigation ─────────────────────────────────────────────────────────────

  it('điều hướng đến trang chi tiết khi click row', async () => {
    renderWithProviders(<ListingsPage />);
    const user = userEvent.setup();

    await waitFor(() => {
      expect(screen.getAllByRole('row').length).toBe(4);
    });

    const dataRows = screen.getAllByRole('row').slice(1) as HTMLElement[];
    await user.click(dataRows[0]!);

    await waitFor(() => {
      expect(mockPush).toHaveBeenCalledWith(expect.stringContaining('/listings/'));
    });
  });
});
