package de.votian.services.service;

import de.votian.services.config.ParameterKeys;
import de.votian.services.entity.Courier;
import de.votian.services.entity.Customer;
import de.votian.services.entity.Employee;
import de.votian.services.entity.Headquarters;
import de.votian.services.entity.User;
import de.votian.services.repository.CourierRepository;
import de.votian.services.repository.CustomerRepository;
import de.votian.services.repository.EmployeeRepository;
import de.votian.services.repository.HeadquartersRepository;
import de.votian.services.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Map;
import java.util.Optional;

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

@SuppressWarnings("null")
class LegacyXmlMetaobjectIntegrationServiceTest {

    private LegacyXmlSystemAuthService authService;
    private CustomerRepository customerRepository;
    private CourierRepository courierRepository;
    private EmployeeRepository employeeRepository;
    private UserRepository userRepository;
    private HeadquartersRepository headquartersRepository;
    private ParameterService parameterService;
    private JdbcTemplate jdbcTemplate;
    private LegacyXmlMetaobjectIntegrationService service;

    @BeforeEach
    void setUp() {
        authService = mock(LegacyXmlSystemAuthService.class);
        customerRepository = mock(CustomerRepository.class);
        courierRepository = mock(CourierRepository.class);
        employeeRepository = mock(EmployeeRepository.class);
        userRepository = mock(UserRepository.class);
        headquartersRepository = mock(HeadquartersRepository.class);
        parameterService = mock(ParameterService.class);
        jdbcTemplate = mock(JdbcTemplate.class);
        service = new LegacyXmlMetaobjectIntegrationService(
                authService,
                customerRepository,
                courierRepository,
                employeeRepository,
                userRepository,
                headquartersRepository,
                parameterService,
                jdbcTemplate
        );
    }

    @Test
    void customerGetReturnsExistingHeaderWithoutMutation() {
        Customer customer = new Customer();
        customer.setId(7L);
        customer.setEid("AG-100");
        customer.setHeadquartersId(1L);
        customer.setAdminEmployeeId(30L);

        Employee employee = new Employee();
        employee.setId(30L);
        employee.setUserId(50L);

        User user = new User();
        user.setId(50L);
        user.setAccount("customer-root");

        Headquarters headquarters = new Headquarters();
        headquarters.setId(1L);
        headquarters.setMnemonic("HQ");

        when(parameterService.getParameterValue(ParameterKeys.GLOBAL_UNIQUE_DB_INSTANCE_NO, 0L)).thenReturn("100");
        when(customerRepository.findByEid("AG-100")).thenReturn(Optional.of(customer));
        when(employeeRepository.findById(30L)).thenReturn(Optional.of(employee));
        when(userRepository.findById(50L)).thenReturn(Optional.of(user));
        when(headquartersRepository.findById(1L)).thenReturn(Optional.of(headquarters));
        when(jdbcTemplate.queryForList(anyString(), eq("100"), eq("usr"), eq(50L)))
                .thenReturn(List.of(Map.of("mo_hash", "SESSION-HASH")));

        String response = service.handleMetaobjectRequest("""
                <xml><metaobject><auth><session_id>SYSTEM</session_id><passphrase>PASS</passphrase></auth><obj_type>cs</obj_type><obj_eid>AG-100</obj_eid></metaobject></xml>
                """);

        verify(authService).authenticate("SYSTEM", "PASS");
        verify(userRepository, never()).save(org.mockito.ArgumentMatchers.any(User.class));
        verify(userRepository, never()).updatePasswordWithMysqlFunction(anyLong(), anyString());
        verify(jdbcTemplate, never()).update(anyString(), eq("100"), eq("usr"), eq(50L), anyString());
        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<customer><![CDATA[AG-100]]></customer>");
        assertThat(response).contains("<account><![CDATA[customer-root]]></account>");
        assertThat(response).contains("<password><![CDATA[]]></password>");
        assertThat(response).contains("<session_id><![CDATA[SESSION-HASH]]></session_id>");
        assertThat(response).contains("<costcenter_name><![CDATA[]]></costcenter_name>");
    }

    @Test
    void customerGenerateCreatesMissingHashAndCredentials() {
        Customer customer = new Customer();
        customer.setId(7L);
        customer.setEid("AG-100");
        customer.setHeadquartersId(1L);
        customer.setAdminEmployeeId(30L);

        Employee employee = new Employee();
        employee.setId(30L);
        employee.setUserId(50L);

        User user = new User();
        user.setId(50L);
        user.setAccount("");

        Headquarters headquarters = new Headquarters();
        headquarters.setId(1L);
        headquarters.setMnemonic("HQ");

        when(parameterService.getParameterValue(ParameterKeys.GLOBAL_UNIQUE_DB_INSTANCE_NO, 0L)).thenReturn("100");
        when(customerRepository.findById(7L)).thenReturn(Optional.of(customer));
        when(employeeRepository.findById(30L)).thenReturn(Optional.of(employee));
        when(userRepository.findById(50L)).thenReturn(Optional.of(user));
        when(headquartersRepository.findById(1L)).thenReturn(Optional.of(headquarters));
        when(jdbcTemplate.queryForList(anyString(), eq("100"), eq("usr"), eq(50L))).thenReturn(List.of());

        String response = service.handleMetaobjectRequest("""
                <xml><metaobject><auth><session_id>SYSTEM</session_id><passphrase>PASS</passphrase></auth><operation>GENERATE</operation><obj_type>cs</obj_type><obj_id>7</obj_id></metaobject></xml>
                """);

        verify(userRepository).save(org.mockito.ArgumentMatchers.any(User.class));
        verify(userRepository).updatePasswordWithMysqlFunction(eq(50L), anyString());
        verify(jdbcTemplate).update(anyString(), eq("100"), eq("usr"), eq(50L), anyString());
        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<customer><![CDATA[AG-100]]></customer>");
        assertThat(response).contains("<account><![CDATA[AG-100]]></account>");
        assertThat(response).containsPattern("<password><!\\[CDATA\\[HQ\\d{5}]]></password>");
        assertThat(response).containsPattern("<session_id><!\\[CDATA\\[[0-9a-f]{128}]]></session_id>");
    }

    @Test
    void contractorGetUsesContractorHeaderAndRotatesPassword() {
        Courier courier = new Courier();
        courier.setId(9L);
        courier.setEid("CR-7");
        courier.setHeadquartersId(1L);
        courier.setUserId(70L);

        User user = new User();
        user.setId(70L);
        user.setAccount("");

        Headquarters headquarters = new Headquarters();
        headquarters.setId(1L);
        headquarters.setMnemonic("SB");

        when(parameterService.getParameterValue(ParameterKeys.GLOBAL_UNIQUE_DB_INSTANCE_NO, 0L)).thenReturn("100");
        when(courierRepository.findByEid("CR-7")).thenReturn(Optional.of(courier));
        when(userRepository.findById(70L)).thenReturn(Optional.of(user));
        when(headquartersRepository.findById(1L)).thenReturn(Optional.of(headquarters));
        when(jdbcTemplate.queryForList(anyString(), eq("100"), eq("usr"), eq(70L)))
                .thenReturn(List.of(Map.of("mo_hash", "CR-HASH")));

        String response = service.handleMetaobjectRequest("""
                <xml><metaobject><auth><session_id>SYSTEM</session_id><passphrase>PASS</passphrase></auth><obj_type>cr</obj_type><obj_eid>CR-7</obj_eid></metaobject></xml>
                """);

        verify(userRepository).save(org.mockito.ArgumentMatchers.any(User.class));
        verify(userRepository).updatePasswordWithMysqlFunction(eq(70L), anyString());
        verify(jdbcTemplate, never()).update(anyString(), eq("100"), eq("usr"), eq(70L), anyString());
        assertThat(response).contains("<state>OK</state>");
        assertThat(response).contains("<contractor><![CDATA[CR-7]]></contractor>");
        assertThat(response).contains("<account><![CDATA[CR-7]]></account>");
        assertThat(response).containsPattern("<password><!\\[CDATA\\[SB\\d{5}]]></password>");
        assertThat(response).contains("<session_id><![CDATA[CR-HASH]]></session_id>");
    }
}
