/*
 * Copyright (C) 2022 Inera AB (http://www.inera.se)
 *
 * This file is part of sklintyg (https://github.com/sklintyg).
 *
 * sklintyg is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * sklintyg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package se.inera.intyg.webcert.web.web.controller.api;
import com.google.common.base.Strings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import se.inera.intyg.common.fk7263.support.Fk7263EntryPoint;
import se.inera.intyg.common.luse.support.LuseEntryPoint;
import se.inera.intyg.common.services.texts.IntygTextsService;
import se.inera.intyg.common.support.model.UtkastStatus;
import se.inera.intyg.common.support.model.common.internal.Patient;
import se.inera.intyg.common.support.modules.registry.IntygModule;
import se.inera.intyg.common.support.modules.registry.IntygModuleRegistry;
import se.inera.intyg.common.support.modules.registry.ModuleNotFoundException;
import se.inera.intyg.infra.integration.hsatk.model.legacy.SelectableVardenhet;
import se.inera.intyg.infra.integration.hsatk.model.legacy.Vardenhet;
import se.inera.intyg.infra.integration.hsatk.model.legacy.Vardgivare;
import se.inera.intyg.infra.integration.hsatk.services.HsatkEmployeeService;
import se.inera.intyg.infra.security.common.model.AuthoritiesConstants;
import se.inera.intyg.infra.security.common.model.Feature;
import se.inera.intyg.infra.security.common.model.Privilege;
import se.inera.intyg.infra.security.common.model.RequestOrigin;
import se.inera.intyg.schemas.contract.Personnummer;
import se.inera.intyg.webcert.common.model.SekretessStatus;
import se.inera.intyg.webcert.persistence.utkast.model.Utkast;
import se.inera.intyg.webcert.persistence.utkast.model.VardpersonReferens;
import se.inera.intyg.webcert.web.converter.util.IntygDraftDecorator;
import se.inera.intyg.webcert.web.service.access.AccessEvaluationParameters;
import se.inera.intyg.webcert.web.service.access.AccessResult;
import se.inera.intyg.webcert.web.service.access.DraftAccessServiceHelper;
import se.inera.intyg.webcert.web.service.log.LogService;
import se.inera.intyg.webcert.web.service.patient.PatientDetailsResolver;
import se.inera.intyg.webcert.web.service.patient.PatientDetailsResolverResponse;
import se.inera.intyg.webcert.web.service.user.WebCertUserService;
import se.inera.intyg.webcert.web.service.user.dto.WebCertUser;
import se.inera.intyg.webcert.web.service.utkast.UtkastService;
import se.inera.intyg.webcert.web.service.utkast.dto.CreateNewDraftRequest;
import se.inera.intyg.webcert.web.service.utkast.dto.PreviousIntyg;
import se.inera.intyg.webcert.web.web.controller.api.dto.CreateUtkastRequest;
import se.inera.intyg.webcert.web.web.controller.api.dto.QueryIntygParameter;
import se.inera.intyg.webcert.web.web.controller.api.dto.QueryIntygResponse;
import se.riv.infrastructure.directory.v1.PersonInformationType;
import javax.ws.rs.core.Response;
import javax.xml.ws.WebServiceException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.OK;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.Silent.class)
public class UtkastApiControllerTest {
    private static final String PATIENT_EFTERNAMN = "Tolvansson";
    private static final String PATIENT_FORNAMN = "Tolvan";
    private static final String PATIENT_MELLANNAMN = "Von";
    private static final String PATIENT_POSTADRESS = "Testadress";
    private static final String PATIENT_POSTNUMMER = "12345";
    private static final String PATIENT_POSTORT = "Testort";
    private static final Personnummer PATIENT_PERSONNUMMER = createPnr("19121212-1212");
    private static final Personnummer PATIENT_PERSONNUMMER_PU_SEKRETESS = createPnr("20121212-1212");
    private static final java.lang.String INTYG_TYPE_VERSION = "1.2";
    @Mock
    private UtkastService utkastService;
    @Mock
    private WebCertUserService webcertUserService;
    @Mock
    private PatientDetailsResolver patientDetailsResolver;
    @Mock
    private IntygModuleRegistry moduleRegistry;
    @Mock
    private IntygTextsService intygTextsService;
    @Mock
    private DraftAccessServiceHelper draftAccessServiceHelper;
    @Mock
    private HsatkEmployeeService hsaEmployeeService;
    @Mock
    private IntygDraftDecorator intygDraftDecorator;
    @Mock
    private LogService logService;
    @InjectMocks
    private UtkastApiController utkastController;
    @Before
    public void setup() throws ModuleNotFoundException {
        when(patientDetailsResolver.getSekretessStatus(eq(PATIENT_PERSONNUMMER))).thenReturn(SekretessStatus.FALSE);
        when(patientDetailsResolver.resolvePatient(any(Personnummer.class), anyString(), anyString())).thenReturn(buildPatient());
        when(moduleRegistry.getIntygModule(eq(LuseEntryPoint.MODULE_ID)))
            .thenReturn(new IntygModule("luse", "", "", "", "", "", "", "", "", false));
        when(moduleRegistry.getIntygModule(eq(Fk7263EntryPoint.MODULE_ID)))
            .thenReturn(new IntygModule("fk7263", "", "", "", "", "", "", "", "", true));
        Map<String, Map<String, PreviousIntyg>> hasPrevious = new HashMap<>();
        Map<String, PreviousIntyg> hasPreviousIntyg = new HashMap<>();
        hasPreviousIntyg.put("luse", PreviousIntyg.of(true, false, false, "Enhet", "intygsId", null));
        hasPrevious.put("intyg", hasPreviousIntyg);
        when(utkastService.checkIfPersonHasExistingIntyg(eq(PATIENT_PERSONNUMMER), any(), any())).thenReturn(hasPrevious);
        when(intygTextsService.getLatestVersion(any(String.class))).thenReturn(INTYG_TYPE_VERSION);
        // Return hsaId as name
        when(hsaEmployeeService.getEmployee(anyString(), any())).thenAnswer(invocation -> {
            PersonInformationType personInformation = new PersonInformationType();
            personInformation.setMiddleAndSurName((String) invocation.getArguments()[0]);
            List<PersonInformationType> personInformationTypeList = new ArrayList<>();
            personInformationTypeList.add(personInformation);
            return personInformationTypeList;
        });
        Map<Personnummer, PatientDetailsResolverResponse> statusMap = mock(Map.class);
        PatientDetailsResolverResponse response = new PatientDetailsResolverResponse();
        response.setTestIndicator(false);
        response.setDeceased(false);
        response.setProtectedPerson(SekretessStatus.FALSE);
        when(statusMap.get(any(Personnummer.class))).thenReturn(response);
        Mockito.when(patientDetailsResolver.getPersonStatusesForList(any())).thenReturn(statusMap);
    }
    @Test
    public void testCreateUtkastFailsForDeprecated() {
        String intygsTyp = "fk7263";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        Response response = utkastController.createUtkast(intygsTyp, buildRequest("fk7263"));
        assertEquals(BAD_REQUEST.getStatusCode(), response.getStatus());
    }
    @Test
    public void testCreateUtkast() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        when(utkastService.createNewDraft(any(CreateNewDraftRequest.class))).thenReturn(new Utkast());
        doReturn(AccessResult.noProblem()).when(draftAccessServiceHelper).evaluateAllowToCreateUtkast(anyString(), any(Personnummer.class));
        Response response = utkastController.createUtkast(intygsTyp, buildRequest("luse"));
        assertEquals(OK.getStatusCode(), response.getStatus());
    }
    @Test
    public void testCreateUtkastSetsPatientFullName() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        doReturn(AccessResult.noProblem()).when(draftAccessServiceHelper).evaluateAllowToCreateUtkast(anyString(), any(Personnummer.class));
        when(utkastService.createNewDraft(any(CreateNewDraftRequest.class))).thenReturn(new Utkast());
        Response response = utkastController.createUtkast(intygsTyp, buildRequest("luse"));
        assertEquals(OK.getStatusCode(), response.getStatus());
        ArgumentCaptor<CreateNewDraftRequest> requestCaptor = ArgumentCaptor.forClass(CreateNewDraftRequest.class);
        verify(utkastService).createNewDraft(requestCaptor.capture());
        assertNotNull(requestCaptor.getValue().getPatient().getFullstandigtNamn());
        assertEquals(PATIENT_FORNAMN + " " + PATIENT_MELLANNAMN + " " + PATIENT_EFTERNAMN,
            requestCaptor.getValue().getPatient().getFullstandigtNamn());
    }
    @Test
    public void testCreateUtkastSetsPatientFullNameWithoutMiddlename() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        when(utkastService.createNewDraft(any(CreateNewDraftRequest.class))).thenReturn(new Utkast());
        // Fake PU service being down
        when(patientDetailsResolver.resolvePatient(PATIENT_PERSONNUMMER, intygsTyp, INTYG_TYPE_VERSION)).thenReturn(null);
        doReturn(AccessResult.noProblem()).when(draftAccessServiceHelper).evaluateAllowToCreateUtkast(anyString(), any(Personnummer.class));
        CreateUtkastRequest utkastRequest = buildRequest("luse");
        utkastRequest.setPatientMellannamn(null); // no middlename
        Response response = utkastController.createUtkast(intygsTyp, utkastRequest);
        assertEquals(OK.getStatusCode(), response.getStatus());
        ArgumentCaptor<CreateNewDraftRequest> requestCaptor = ArgumentCaptor.forClass(CreateNewDraftRequest.class);
        verify(utkastService).createNewDraft(requestCaptor.capture());
        assertNotNull(requestCaptor.getValue().getPatient().getFullstandigtNamn());
        assertEquals(PATIENT_FORNAMN + " " + PATIENT_EFTERNAMN,
            requestCaptor.getValue().getPatient().getFullstandigtNamn());
    }
    @Test
    public void testCreateUtkastFornamnOk() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        when(utkastService.createNewDraft(any(CreateNewDraftRequest.class))).thenReturn(new Utkast());
        doReturn(AccessResult.noProblem()).when(draftAccessServiceHelper).evaluateAllowToCreateUtkast(anyString(), any(Personnummer.class));
        CreateUtkastRequest utkastRequest = buildRequest(intygsTyp);
        utkastRequest.setPatientFornamn(Strings.repeat("a", 255));
        Response response = utkastController.createUtkast(intygsTyp, utkastRequest);
        assertEquals(OK.getStatusCode(), response.getStatus());
    }
    @Test
    public void testCreateUtkastFornamnTooLong() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        CreateUtkastRequest utkastRequest = buildRequest(intygsTyp);
        utkastRequest.setPatientFornamn(Strings.repeat("a", 256));
        Response response = utkastController.createUtkast(intygsTyp, utkastRequest);
        assertEquals(BAD_REQUEST.getStatusCode(), response.getStatus());
    }
    @Test
    public void testCreateUtkastEfternamnOk() {
        String intygsTyp = "luse";
        setupUser(AuthoritiesConstants.PRIVILEGE_SKRIVA_INTYG, intygsTyp, AuthoritiesConstants.FEATURE_HANTERA_INTYGSUTKAST);
        when(utkastService.createNewDraft(any(CreateNewDraftRequest.class))).thenReturn(new Utkast());
        doReturn(AccessResult.noProblem()).when(draftAccessServiceHelper).evaluateAllowToCreateUtkast(anyString(), any(Personnummer.class));


Answer:         CreateUtkastRequest utkastRequest = buildRequest(intygsTyp);