package de.votian.services.service;

import de.votian.services.entity.Address;
import de.votian.services.entity.CostCenter;
import de.votian.services.entity.CostCenterAddress;
import de.votian.services.entity.CourierVehicle;
import de.votian.services.entity.Customer;
import de.votian.services.entity.Headquarters;
import de.votian.services.entity.Job;
import de.votian.services.entity.Tour;
import de.votian.services.repository.AddressRepository;
import de.votian.services.repository.CompanyRepository;
import de.votian.services.repository.CostCenterAddressRepository;
import de.votian.services.repository.CostCenterRepository;
import de.votian.services.repository.CourierVehicleRepository;
import de.votian.services.repository.GenericDataContainerRepository;
import de.votian.services.repository.HeadquartersRepository;
import de.votian.services.repository.MetaTypeRepository;
import de.votian.services.repository.TourRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@SuppressWarnings({"null", "unchecked"})
class LegacyXmlOrderRequestIntegrationServiceTest {

    private LegacyXmlB2BAuthService authService;
    private JobService jobService;
    private AddressRepository addressRepository;
    private CostCenterRepository costCenterRepository;
    private CostCenterAddressRepository costCenterAddressRepository;
    private CourierVehicleRepository courierVehicleRepository;
    private HeadquartersRepository headquartersRepository;
    private MetaTypeRepository metaTypeRepository;
    private TourRepository tourRepository;
    private CompanyRepository companyRepository;
    private GenericDataContainerRepository genericDataContainerRepository;
    private JdbcTemplate jdbcTemplate;
    private LegacyXmlOrderRequestIntegrationService service;

    @BeforeEach
    void setUp() {
        authService = mock(LegacyXmlB2BAuthService.class);
        jobService = mock(JobService.class);
        addressRepository = mock(AddressRepository.class);
        costCenterRepository = mock(CostCenterRepository.class);
        costCenterAddressRepository = mock(CostCenterAddressRepository.class);
        courierVehicleRepository = mock(CourierVehicleRepository.class);
        headquartersRepository = mock(HeadquartersRepository.class);
        metaTypeRepository = mock(MetaTypeRepository.class);
        tourRepository = mock(TourRepository.class);
        companyRepository = mock(CompanyRepository.class);
        genericDataContainerRepository = mock(GenericDataContainerRepository.class);
        jdbcTemplate = mock(JdbcTemplate.class);
        service = new LegacyXmlOrderRequestIntegrationService(
                authService,
                jobService,
                addressRepository,
                costCenterRepository,
                costCenterAddressRepository,
                courierVehicleRepository,
                headquartersRepository,
                metaTypeRepository,
                tourRepository,
                companyRepository,
                genericDataContainerRepository,
                jdbcTemplate
        );
    }

    @Test
    void orderRequestInsertCreatesJobAndReturnsLegacyCarrierPayload() {
        when(authService.authenticate(any())).thenReturn(authContext());
        when(headquartersRepository.findByMnemonic("HH")).thenReturn(headquarters());

        CourierVehicle vehicle = new CourierVehicle();
        vehicle.setId(77L);
        vehicle.setCourierId(44L);
        vehicle.setSid("CAR-1");
        when(courierVehicleRepository.findBySid("CAR-1")).thenReturn(Optional.of(vehicle));

        when(addressRepository.findByStreetAndZipcodeAndCityAndCountry("Testweg", "10115", "Berlin", "DE"))
                .thenReturn(Optional.empty());
        Address savedAddress = new Address();
        savedAddress.setId(501L);
        when(addressRepository.save(any(Address.class))).thenReturn(savedAddress);

        Job created = new Job();
        created.setId(700L);
        when(jobService.createJob(any(Job.class), any(List.class), eq(List.of()))).thenReturn(created);

        String response = service.handleOrderRequest("""
                <xml><order>
                  <auth><customer>AG-100</customer><account>x</account><password>y</password><session_id>z</session_id></auth>
                  <operation>INS</operation>
                  <no>ABC-1</no>
                  <hq>HH</hq>
                  <vehicle><type_no>3</type_no></vehicle>
                  <ordertime>2026-03-28 09:15:00</ordertime>
                  <courier>CAR-1</courier>
                  <remark>Lieferung Test</remark>
                  <stations>
                    <station><company>Pickup GmbH</company><street>Testweg</street><houseno>12a</houseno><zipcode>10115</zipcode><city>Berlin</city></station>
                    <station><company>Delivery GmbH</company><street>Testweg</street><houseno>8</houseno><zipcode>10115</zipcode><city>Berlin</city></station>
                  </stations>
                </order></xml>
                """);

        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<orderno>700</orderno>");
        verify(jobService).createJob(any(Job.class), any(List.class), eq(List.of()));
    }

    @Test
    void stationRequestInsertCreatesBaseJobAndReturnsCreatedStationIds() {
        when(authService.authenticate(any())).thenReturn(authContext());
        when(headquartersRepository.findByMnemonic("HH")).thenReturn(headquarters());
        when(jdbcTemplate.queryForList(any(String.class), eq(7L), eq("station_request"), eq(LocalDate.of(2026, 3, 28).toString())))
                .thenReturn(List.of());

        CostCenterAddress pickupAddress = new CostCenterAddress();
        pickupAddress.setAddressId(301L);
        pickupAddress.setComp("Root GmbH");
        pickupAddress.setHsno("1");
        when(costCenterAddressRepository.findByCostCenterId(10L)).thenReturn(List.of(pickupAddress));

        Address address = new Address();
        address.setId(301L);
        address.setStreet("Lagerweg");
        address.setZipcode("10115");
        address.setCity("Berlin");
        address.setCountry("DE");
        when(addressRepository.findById(301L)).thenReturn(Optional.of(address));
        when(addressRepository.findByStreetAndZipcodeAndCityAndCountry("Zielweg", "20095", "Hamburg", "DE"))
                .thenReturn(Optional.empty());
        Address deliveryAddress = new Address();
        deliveryAddress.setId(401L);
        when(addressRepository.save(any(Address.class))).thenReturn(deliveryAddress);

        Job created = new Job();
        created.setId(900L);
        when(jobService.createJob(any(Job.class), any(List.class), eq(List.of()))).thenReturn(created);

        Tour pickupTour = new Tour();
        pickupTour.setId(1L);
        pickupTour.setSort(1);
        Tour deliveryTour = new Tour();
        deliveryTour.setId(2L);
        deliveryTour.setSort(2);
        when(jobService.getTours(900L)).thenReturn(List.of(pickupTour, deliveryTour));

        String response = service.handleStationRequest("""
                <xml><order>
                  <auth><customer>AG-100</customer><account>x</account><password>y</password><session_id>z</session_id></auth>
                  <operation>INS</operation>
                  <no>ST-1</no>
                  <hq>HH</hq>
                  <vehicle><type_no>3</type_no></vehicle>
                  <ordertime>2026-03-28 08:00:00</ordertime>
                  <remark>Tagesroute</remark>
                  <stations>
                    <station><company>Placeholder</company><street>Leer</street><houseno>1</houseno><zipcode>00000</zipcode><city>Leer</city></station>
                    <station><company>Ziel GmbH</company><street>Zielweg</street><houseno>9</houseno><zipcode>20095</zipcode><city>Hamburg</city></station>
                  </stations>
                </order></xml>
                """);

        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<orderno>900</orderno>");
        assertThat(response).contains("<station_no>2</station_no>");
    }

    @Test
    void orderRequestDeleteCancelsExistingJob() {
        when(authService.authenticate(any())).thenReturn(authContext());

        Job existing = new Job();
        existing.setId(700L);
        when(jobService.findByCommissionNoAndCustomer("ABC-1", 7L)).thenReturn(List.of(existing));

        Tour tour = new Tour();
        tour.setId(1L);
        tour.setCommissionNo("ABC-1");
        when(jobService.getTours(700L)).thenReturn(List.of(tour));
        when(jobService.saveTour(any(Tour.class))).thenAnswer(invocation -> invocation.getArgument(0));

        String response = service.handleOrderRequest("""
                <xml><order>
                  <auth><customer>AG-100</customer><account>x</account><password>y</password><session_id>z</session_id></auth>
                  <operation>DEL</operation>
                  <no>ABC-1</no>
                </order></xml>
                """);

        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<orderno>700</orderno>");
        verify(jobService).cancelJob(700L);
    }

    private LegacyXmlB2BAuthService.AuthContext authContext() {
        Customer customer = new Customer();
        customer.setId(7L);
        customer.setEid("AG-100");
        customer.setHeadquartersId(1L);
        customer.setCompanyId(20L);

        CostCenter costCenter = new CostCenter();
        costCenter.setId(10L);
        costCenter.setCustomerId(7L);
        costCenter.setName("Root");
        return new LegacyXmlB2BAuthService.AuthContext(customer, null, null, null, null, costCenter);
    }

    private Headquarters headquarters() {
        Headquarters headquarters = new Headquarters();
        headquarters.setId(1L);
        headquarters.setMnemonic("HH");
        return headquarters;
    }
}
