package de.assecutor.votianlt.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.model.InvoiceTemplate;
import de.assecutor.votianlt.pages.domain.CustomerRepository;
import de.assecutor.votianlt.repository.AppUserRepository;
import de.assecutor.votianlt.repository.BarcodeRepository;
import de.assecutor.votianlt.repository.CargoItemRepository;
import de.assecutor.votianlt.repository.CommentRepository;
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
import de.assecutor.votianlt.repository.InvoiceTemplateRepository;
import de.assecutor.votianlt.repository.JobHistoryRepository;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.repository.LocationPositionRepository;
import de.assecutor.votianlt.repository.MessageRepository;
import de.assecutor.votianlt.repository.PhotoRepository;
import de.assecutor.votianlt.repository.ServiceRepository;
import de.assecutor.votianlt.repository.SignatureRepository;
import de.assecutor.votianlt.repository.TaskRepository;
import de.assecutor.votianlt.repository.TaskTemplateRepository;
import de.assecutor.votianlt.repository.UserInvoiceDataRepository;
import de.assecutor.votianlt.repository.UserRepository;
import de.assecutor.votianlt.security.SessionAuthenticationService;
import java.util.List;
import java.util.Optional;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.crypto.password.PasswordEncoder;

@ExtendWith(MockitoExtension.class)
class DemoModeServiceTest {

    @Mock
    private SessionAuthenticationService sessionAuthenticationService;
    @Mock
    private UserRepository userRepository;
    @Mock
    private CustomerRepository customerRepository;
    @Mock
    private JobRepository jobRepository;
    @Mock
    private CargoItemRepository cargoItemRepository;
    @Mock
    private TaskRepository taskRepository;
    @Mock
    private JobHistoryRepository jobHistoryRepository;
    @Mock
    private CustomerInvoiceRepository customerInvoiceRepository;
    @Mock
    private ServiceRepository serviceRepository;
    @Mock
    private AppUserRepository appUserRepository;
    @Mock
    private TaskTemplateRepository taskTemplateRepository;
    @Mock
    private InvoiceTemplateRepository invoiceTemplateRepository;
    @Mock
    private UserInvoiceDataRepository userInvoiceDataRepository;
    @Mock
    private MessageRepository messageRepository;
    @Mock
    private LocationPositionRepository locationPositionRepository;
    @Mock
    private PhotoRepository photoRepository;
    @Mock
    private SignatureRepository signatureRepository;
    @Mock
    private BarcodeRepository barcodeRepository;
    @Mock
    private CommentRepository commentRepository;
    @Mock
    private PasswordEncoder passwordEncoder;

    @Captor
    private ArgumentCaptor<List<Job>> jobsCaptor;
    @Captor
    private ArgumentCaptor<List<de.assecutor.votianlt.model.Service>> servicesCaptor;
    @Captor
    private ArgumentCaptor<InvoiceTemplate> invoiceTemplateCaptor;

    private DemoSessionRegistry demoSessionRegistry;
    private DemoModeService demoModeService;

    @BeforeEach
    void setUp() {
        demoSessionRegistry = new DemoSessionRegistry();
        demoModeService = new DemoModeService(demoSessionRegistry, sessionAuthenticationService, userRepository,
                customerRepository, jobRepository, cargoItemRepository, taskRepository, jobHistoryRepository,
                customerInvoiceRepository, serviceRepository, appUserRepository, taskTemplateRepository,
                invoiceTemplateRepository, userInvoiceDataRepository, messageRepository, locationPositionRepository,
                photoRepository, signatureRepository, barcodeRepository, commentRepository, passwordEncoder);
    }

    @Test
    void tryPrepareDemoSessionSeedsTwoDemoJobs() {
        User demoUser = new User();
        demoUser.setId(new ObjectId());

        when(userRepository.findByEmail(DemoModeService.DEMO_USERNAME)).thenReturn(Optional.of(demoUser));
        when(passwordEncoder.encode(DemoModeService.DEMO_PASSWORD)).thenReturn("encoded-demo");
        when(customerRepository.findByOwner(demoUser.getId())).thenReturn(List.of());
        when(jobRepository.findByCreatedBy(demoUser.getId().toHexString())).thenReturn(List.of());
        when(appUserRepository.findByErstelltVon(demoUser.getId())).thenReturn(List.of());
        when(taskTemplateRepository.findByUserIdOrderByTemplateNameAsc(demoUser.getId())).thenReturn(List.of());
        when(customerInvoiceRepository.findByUserId(demoUser.getId().toHexString())).thenReturn(List.of());
        when(customerRepository.saveAll(anyList())).thenAnswer(invocation -> invocation.getArgument(0));
        when(serviceRepository.saveAll(anyList())).thenAnswer(invocation -> invocation.getArgument(0));
        when(jobRepository.saveAll(anyList())).thenAnswer(invocation -> invocation.getArgument(0));

        boolean prepared = demoModeService.tryPrepareDemoSession("demo-session");

        assertThat(prepared).isTrue();
        verify(serviceRepository).saveAll(servicesCaptor.capture());
        assertThat(servicesCaptor.getValue()).hasSize(2);
        verify(invoiceTemplateRepository).save(invoiceTemplateCaptor.capture());
        assertThat(invoiceTemplateCaptor.getValue().getUserId()).isEqualTo(demoUser.getId().toHexString());
        assertThat(invoiceTemplateCaptor.getValue().getTemplateData())
                .contains("elements")
                .contains("services.list")
                .contains("invoice.gross_total")
                .contains("frei angepasst werden")
                .contains("textAlign")
                .contains("center")
                .contains("fontSize")
                .contains("20");
        verify(jobRepository).saveAll(jobsCaptor.capture());
        assertThat(jobsCaptor.getValue()).hasSize(2);
        assertThat(jobsCaptor.getValue()).extracting(Job::getJobNumber)
                .containsExactly("DEMO-AKTIV-001", "DEMO-ERLEDIGT-001");
        assertThat(jobsCaptor.getValue()).extracting(Job::getStatus)
                .containsExactly(JobStatus.IN_PROGRESS, JobStatus.COMPLETED);
        assertThat(jobsCaptor.getValue()).allSatisfy(job -> {
            assertThat(job.getServiceIds()).hasSize(1);
            assertThat(job.getSelectedServices()).hasSize(1);
            assertThat(job.getSelectedServices().get(0).getDeliveryStationOrder()).isEqualTo(0);
            assertThat(job.getSelectedServices().get(0).getServiceId()).isEqualTo(job.getServiceIds().get(0));
        });
        assertThat(demoSessionRegistry.isHeldBy("demo-session")).isTrue();
    }

    @Test
    void cleanupAndReleaseIfOwnedSkipsForeignSessions() {
        demoSessionRegistry.acquire("active-session");

        demoModeService.cleanupAndReleaseIfOwned("other-session");

        verify(userRepository, never()).findByEmail(anyString());
        assertThat(demoSessionRegistry.isHeldBy("active-session")).isTrue();
    }
}
