package de.dnb.resolvingdienste.urnimporter.urn.export;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status.Family;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.dnb.resolvingdienste.urnadministration.restapi.client.RESTUrnResource;
import de.dnb.resolvingdienste.urnadministration.restapi.client.RestCallResponse;
import de.dnb.resolvingdienste.urnadministration.restapi.client.helpers.HttpBasicAuthenticationStrategy;
import de.dnb.resolvingdienste.urnadministration.urn.restapi.model.RESTCreateUrlRequest;
import de.dnb.resolvingdienste.urnadministration.urn.restapi.model.RESTCreateUrnRequest;
import de.dnb.resolvingdienste.urnadministration.urn.restapi.model.RESTReplaceUrlRequest;
import de.dnb.resolvingdienste.urnimporter.entity.Url;
import de.dnb.resolvingdienste.urnimporter.entity.UrnRecord;

public class UrnRestApiExporter implements UrnExporter {
    private static final Logger LOGGER = LoggerFactory.getLogger(UrnRestApiExporter.class);

    private final URI endpoint;
    private final HttpBasicAuthenticationStrategy strategy;

    public UrnRestApiExporter(URI endpoint, String adminLogin, String password) {
        this.endpoint = endpoint;
        strategy = new HttpBasicAuthenticationStrategy(adminLogin, password);
        LOGGER.info("A new UrnRestApiExporter is born: " + endpoint.toASCIIString());
    }

    /**
     * Asks the first URN in system. No matter which one. If not OK delivered it is
     * assumed that the system is not available
     */
    @Override
    public boolean isUrnApiAvailable() {
        final RESTUrnResource resource = new RESTUrnResource(endpoint, strategy);
        final Response response = resource.getUrns("", "1", "1", "", "", "");
        if (response.getStatus() == HttpServletResponse.SC_OK)
            return true;
        return false;
    }

    @Override
    public boolean isUrnAlreadyRegistered(String urnString) {
        final RESTUrnResource resource = new RESTUrnResource(endpoint, strategy);
        final Response response = resource.getUrnForHeadRequest(urnString, "");
        if (response.getStatus() == HttpServletResponse.SC_OK)
            return true;
        if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND)
            return false;
	    // TODO: implement Exception
        LOGGER.error("Server returned: " + response.readEntity(String.class));
        return false;
    }

    @Override
    public ExportResult insertUrn(UrnRecord record, String runAs) {
        final String urnStr = record.getUrn().getUrn();
        LOGGER.info("Inserting URN: " + urnStr + " as user " + runAs);

        final ExportResult result = new ExportResult();
        final RESTCreateUrnRequest urnRequest = new RESTCreateUrnRequest();
        urnRequest.setUrn(urnStr);
        urnRequest.setUrls(getCreateUrlRequests(record));

        LOGGER.info("Initializing the RESTUrnResource with endpoint " + endpoint);
        final RESTUrnResource resource = new RESTUrnResource(endpoint, strategy);

        LOGGER.info("Calling .createUrn()");
        final RestCallResponse response = resource.createUrn(runAs, urnRequest);

        final int status = response.getStatusCode();
        LOGGER.info("Returned with status: " + status);

        switch (status) {

        case HttpServletResponse.SC_CREATED:
            result.finishedSuccessfully();
            LOGGER.info(urnStr + " successfully inserted. Returning.");
            break;

        default:
            final String message = "Could not insert: " + urnStr
                    + "\nMessage: " + response.getErrorMessage()
                    + "\nHTTP Body:\n" + response.getHttpBody();
            LOGGER.info(message);
            result.finishedWithError(status, message, response.getException());
            break;
        }

        return result;
    }

    @Override
    public ExportResult updateUrn(UrnRecord record, String runAs) {
        final String urnStr = record.getUrn().getUrn();
        LOGGER.info("Updating URN: " + urnStr + " as user " + runAs);

        final ExportResult result = new ExportResult();
        final List<RESTReplaceUrlRequest> requests = getReplaceUrlRequests(record);

        LOGGER.info("Initializing the RESTUrnResource with endpoint " + endpoint);
        final RESTUrnResource resource = new RESTUrnResource(endpoint, strategy);

        LOGGER.info("Calling .replaceAllUrlsOfPartner()");
        final RestCallResponse response = resource.replaceAllUrlsOfPartner(urnStr, runAs, requests);

        final int status = response.getStatusCode();
        LOGGER.info("Returned with status: " + status);

        if (Family.SUCCESSFUL.equals(Family.familyOf(status))) {
            result.finishedSuccessfully();
            LOGGER.info("All URLs of " + urnStr + " replaced successfully. Returning.");
        } else {
            final String message = "Could not replace the URLs of: " + urnStr
                    + "\nMessage: " + response.getErrorMessage()
                    + "\nHTTP Body:\n" + response.getHttpBody();
            LOGGER.info(message);
            result.finishedWithError(status, message, response.getException());
        }

        return result;
    }

    // private methods
    private List<RESTCreateUrlRequest> getCreateUrlRequests(UrnRecord record) {
        final List<RESTCreateUrlRequest> urls = new ArrayList<>();
        final List<Url> urlList = record.getUrn().getUrls();

        for (final Url url : urlList) {
            final RESTCreateUrlRequest urlRequest = new RESTCreateUrlRequest();
            urlRequest.setUrl(url.getUrl());
            if (url.isPrimary()) {
                urlRequest.setPriority(1);
            } else {
                urlRequest.setPriority(0);
            }

            urls.add(urlRequest);
        }

        return urls;
    }

    private List<RESTReplaceUrlRequest> getReplaceUrlRequests(UrnRecord record) {
        final List<RESTReplaceUrlRequest> urls = new ArrayList<>();
        final List<Url> urlList = record.getUrn().getUrls();


        for (final Url url : urlList) {
            final RESTReplaceUrlRequest urlRequest = new RESTReplaceUrlRequest();
            urlRequest.setUrl(url.getUrl());
            if (url.isPrimary()) {
                urlRequest.setPriority(1);
            } else {
                urlRequest.setPriority(0);
            }

            urls.add(urlRequest);
        }

        return urls;
    }

}
