import React from 'react';
import {fireEvent, render, screen, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';

const mockUseAuth = jest.fn();
jest.mock('@/services/covoiturageService');
jest.mock('@/contexts/AuthContext', () => ({useAuth: () => mockUseAuth()}));
jest.mock('next/link', () => ({
  __esModule: true,
  default: ({children, href, ...rest}: {children: React.ReactNode; href: string}) => <a href={href} {...rest}>{children}</a>,
}));

import CovoituragesBoard from '../CovoituragesBoard';
import {deleteCarpool, getCarpoolsAVenir, sendCarpoolContactMessage, updateCarpool, type Carpool} from '@/services/covoiturageService';

const mockedGetCarpoolsAVenir = getCarpoolsAVenir as jest.MockedFunction<typeof getCarpoolsAVenir>;

const carhaix = {eveId: 12, libelle: 'Fest-noz de Carhaix', date: '2026-11-07', vilNom: 'Carhaix'};
const lorient = {eveId: 20, libelle: 'Concert à Lorient', date: '2026-11-20', vilNom: 'Lorient'};
const carpools: Carpool[] = [
  {id: 7, ville: 'Rennes', nombre: 3, heure: '18:00', propose: 1, auteur: {id: 42, pseudo: 'breizh56'}, evenement: carhaix},
  {id: 8, ville: 'Brest', nombre: 1, heure: null, propose: 0, auteur: {id: 43, pseudo: 'yann22'}, evenement: carhaix},
  {id: 9, ville: 'Vannes', nombre: 2, heure: '20:00', propose: 1, auteur: {id: 44, pseudo: 'nolwenn'}, evenement: lorient},
];

function signedInAs(username: string | null) {
  mockUseAuth.mockReturnValue({isAuthenticated: username !== null, user: username === null ? null : {id: '1', username}});
}

describe('CovoituragesBoard', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockedGetCarpoolsAVenir.mockResolvedValue(carpools);
  });

  it('shouldGroupTheUpcomingCarpoolsByEventWithALinkToEachEvent', async () => {
    signedInAs(null);
    render(<CovoituragesBoard locale="fr" />);

    await waitFor(() => expect(screen.getByText('breizh56')).toBeInTheDocument());
    const evenements = screen.getAllByRole('heading', {level: 2});
    expect(evenements.map((h) => h.textContent)).toEqual(['Fest-noz de Carhaix', 'Concert à Lorient']);
    expect(screen.getByRole('link', {name: 'Fest-noz de Carhaix'})).toHaveAttribute('href', '/fr/evenement/12/fest-noz-de-carhaix');
    expect(screen.getByText(/Carhaix/, {selector: 'small, .tk-covoiturages__evenement-infos'})).toBeInTheDocument();
    expect(screen.getAllByText(/seats_label/)).toHaveLength(3);
  });

  it('shouldSayWhenNoCarpoolIsComing', async () => {
    signedInAs(null);
    mockedGetCarpoolsAVenir.mockResolvedValue([]);
    render(<CovoituragesBoard locale="fr" />);

    expect(await screen.findByText('empty_upcoming')).toBeInTheDocument();
  });

  it('shouldInviteAnonymousVisitorsToLogInAndHideTheActions', async () => {
    signedInAs(null);
    render(<CovoituragesBoard locale="fr" />);
    await waitFor(() => expect(screen.getByText('breizh56')).toBeInTheDocument());

    expect(screen.getByRole('link', {name: 'login_to_reply'})).toHaveAttribute('href', '/fr/connexion');
    expect(screen.queryByRole('button', {name: 'contact'})).not.toBeInTheDocument();
    expect(screen.queryByRole('button', {name: 'edit'})).not.toBeInTheDocument();
    expect(screen.queryByRole('button', {name: 'delete'})).not.toBeInTheDocument();
  });

  it('shouldLetAMemberAnswerAnAuthorThroughTheContactModal', async () => {
    signedInAs('yann22');
    (sendCarpoolContactMessage as jest.Mock).mockResolvedValue(undefined);
    const user = userEvent.setup();
    render(<CovoituragesBoard locale="fr" />);
    await waitFor(() => expect(screen.getByText('breizh56')).toBeInTheDocument());

    expect(screen.queryByRole('link', {name: 'login_to_reply'})).not.toBeInTheDocument();
    // yann22 est l'auteur de l'annonce 8 : il ne peut contacter que les deux autres.
    const contacts = screen.getAllByRole('button', {name: 'contact'});
    expect(contacts).toHaveLength(2);
    await user.click(contacts[0]);
    await user.type(screen.getByRole('textbox'), 'Bonjour !');
    await user.click(screen.getByRole('button', {name: 'contact_send'}));

    await waitFor(() => expect(sendCarpoolContactMessage).toHaveBeenCalledWith(7, 'Bonjour !'));
  });

  it('shouldLetTheAuthorEditAndDeleteTheirOwnCarpool', async () => {
    signedInAs('breizh56');
    (updateCarpool as jest.Mock).mockResolvedValue({...carpools[0], ville: 'Saint-Brieuc', nombre: 2});
    (deleteCarpool as jest.Mock).mockResolvedValue(undefined);
    jest.spyOn(window, 'confirm').mockReturnValue(true);
    render(<CovoituragesBoard locale="fr" />);
    await waitFor(() => expect(screen.getByText('breizh56')).toBeInTheDocument());

    expect(screen.getAllByRole('button', {name: 'edit'})).toHaveLength(1);
    fireEvent.click(screen.getByRole('button', {name: 'edit'}));
    const ville = screen.getByLabelText('city') as HTMLInputElement;
    expect(ville.value).toBe('Rennes');
    fireEvent.change(ville, {target: {value: 'Saint-Brieuc'}});
    fireEvent.change(screen.getByLabelText('seats'), {target: {value: '2'}});
    fireEvent.click(screen.getByRole('button', {name: 'save'}));

    await waitFor(() => expect(updateCarpool).toHaveBeenCalledWith(7, {ville: 'Saint-Brieuc', nombre: 2, heure: '18:00', propose: 1}));
    expect(await screen.findByText(/Saint-Brieuc — 2 seats_label/)).toBeInTheDocument();

    fireEvent.click(screen.getByRole('button', {name: 'delete'}));
    await waitFor(() => expect(deleteCarpool).toHaveBeenCalledWith(7));
    expect(screen.queryByText('breizh56')).not.toBeInTheDocument();
  });
});
