package kz.arta.nca_iiscon.util;

import kz.arta.nca_iiscon.data.forward.*;
import kz.arta.nca_iiscon.data.forward.applicant.*;
import kz.arta.nca_iiscon.data.forward.application.*;
import kz.arta.nca_iiscon.data.forward.document.Document;
import kz.arta.nca_iiscon.data.forward.payment.Payment;
import kz.arta.nca_iiscon.data.forward.registration.ApplicationRegistrationData;
import kz.arta.nca_iiscon.data.forward.status.ExecutionStatus;
import kz.arta.nca_iiscon.data.search.SearchOrderByReferenceNumberRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringSubstitutor;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Slf4j
public class XmlTemplateBuilder {
    static ApplicationIdGenerator gen = new ApplicationIdGenerator();

    public static String buildXMLForward(ForwardApplication request, String serviceId, String login, String password) throws IOException {
        Random random = new Random();

        String template = loadTemplate("/templates/request.xml");
        String documentTemplate = loadTemplate("/templates/document.xml");
        String messageID = UUID.randomUUID().toString();

        OffsetDateTime messageDate = OffsetDateTime.now(TimeZone.getDefault().toZoneId());
        String forwardDate = messageDate.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);

        String applicationId = request.getApplicationId();

        ApplicationRegistrationData applicationRegistrationData = request.getRegisterApplication().getApplicationRegistrationData();
        String registerOrgCode = applicationRegistrationData.getRegisterOrgCode();
        Employee employee = applicationRegistrationData.getRegisterEmployee();
        String employeeLastName = employee.getLastName();
        String employeeFirstName = employee.getFirstName();
        String employeeMiddleName = employee.getMiddleName();
        String employeePosition = employee.getPosition();
        String employeeIin = employee.getIin();

        UpdateApplication updateApplication = request.getUpdateApplication();
        ApplicationData applicationData = updateApplication.getApplicationData();
        Person applicant = applicationData.getApplicant().getPerson();

        Contact contact = new Contact();
        if (applicant.getContacts() != null && applicant.getContacts().getContact() != null && !applicant.getContacts().getContact().isEmpty() && applicant.getContacts().getContact().get(0) != null) {
            contact = applicant.getContacts().getContact().get(0);
        }
        IdentityDocument identityDocument = applicant.getIdentityDocument();

        String personIin = applicant.getIin();

        //DOCUMENTS
        StringBuilder documentArray = new StringBuilder();

        List<Document> documents = applicationData.getDocuments().getDocument();
        for (Document doc : documents) {
            Map<String, String> values = new HashMap<>();

            values.put("documentId", doc.getDocumentId());
            values.put("docTypeCode", doc.getDocTypeCode());
            values.put("copyType", doc.getCopyType());
            values.put("docName", doc.getDocName());
            values.put("personIin", personIin);
            values.put("forwardDate", forwardDate);

            StringSubstitutor substitutor = new StringSubstitutor(values);
            documentArray.append(substitutor.replace(documentTemplate));
        }

        String digiSign = applicationData.getData().getDigiSign();


        String personLastName = applicant.getLastName();
        String personFirstName = applicant.getFirstName();
        String personMiddleName = applicant.getMiddleName();
        String contactType = contact.getContactType().toUpperCase();
        ;
        String contactData = contact.getContactData();
        Boolean isForNotification = contact.getIsForNotification();
        String identityDocumentDocType = identityDocument.getDocType();
        String identityDocumentDocDate = identityDocument.getDocDate();
        String docNum = identityDocument.getDocNum();
        String expirationDate = identityDocument.getExpirationDate();
        String identityDocumentDocSource = identityDocument.getDocSource();
        String identityDocumentDocName = identityDocument.getDocName();
        String dateOfBirth = applicant.getDateOfBirth();
        String placeOfBirth = applicant.getPlaceOfBirth();
        String gender = applicant.getGender();
        Boolean isForeigner = applicant.getIsForeigner();
        String citizenship = applicant.getCitizenship();

        OffsetDateTime deadlineDateTime = messageDate.plusDays(3);
        String deadline = deadlineDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);

        int paymentId = 1_000_000 + random.nextInt(9_000_000);
        String language = (applicationData.getLanguage() == null || applicationData.getLanguage().isEmpty())
                ? "RUS"
                : applicationData.getLanguage();


        String subServiceTypeCode = applicationData.getSubServiceTypeCode();


        Map<String, String> values = new HashMap<>();
        values.put("messageId", messageID);
        values.put("serviceId", serviceId);
        values.put("forwardDate", forwardDate);
        values.put("senderId", login);
        values.put("password", password);
        values.put("applicationId", applicationId);
        values.put("registerOrgCode", registerOrgCode);

        values.put("employeeLastName", employeeLastName);
        values.put("employeeFirstName", employeeFirstName);
        values.put("employeeMiddleName", employeeMiddleName);
        values.put("employeePosition", employeePosition);
        values.put("employeeIin", employeeIin);

        // Applicant Person
        values.put("personIin", personIin);
        values.put("personLastName", personLastName);
        values.put("personFirstName", personFirstName);
        values.put("personMiddleName", personMiddleName);

        // Contact
        values.put("contactType", contactType);
        values.put("contactData", contactData);
        values.put("isForNotification", String.valueOf(isForNotification));

        // Identity Document
        values.put("identityDocumentDocType", identityDocumentDocType);
        values.put("identityDocumentDocDate", identityDocumentDocDate);
        values.put("docNum", docNum);
        values.put("expirationDate", expirationDate);
        values.put("identityDocumentDocSource", identityDocumentDocSource);
        values.put("identityDocumentDocName", identityDocumentDocName);

        values.put("dateOfBirth", dateOfBirth);
        values.put("placeOfBirth", placeOfBirth);
        values.put("gender", gender);
        values.put("isForeigner", String.valueOf(isForeigner));
        values.put("citizenship", citizenship);
        values.put("deadline", deadline);
        values.put("paymentId", String.valueOf(paymentId));
        values.put("language", language);
        values.put("document", documentArray.toString());
        values.put("dataDigiSign", escapeXml(digiSign));
        values.put("order", loadOrder(applicationData));
        log.info("order after put: {}", values.get("order"));
        values.put("subServiceTypeCode", subServiceTypeCode); //ПОКА ОСТАВЛЮ ЖДУ

        StringSubstitutor substitutor = new StringSubstitutor(values);
        return substitutor.replace(template);

    }

    private static String loadTemplate(String templatePath) throws IOException {
        try (InputStream is = XmlTemplateBuilder.class.getResourceAsStream(templatePath)) {
            if (is == null) {
                throw new IOException("Шаблон не найден");
            }
            try (Scanner scanner = new Scanner(is, "UTF-8")) {
                scanner.useDelimiter("\\A");
                return scanner.hasNext() ? scanner.next() : "";
            }
        }
    }

    private static String loadOrder(ApplicationData applicationData) throws IOException {
        ConfirmOrder confirmOrder = applicationData.getData().getConfirmOrder();
        ConfirmOrder denyOrder = applicationData.getData().getDenyOrder();

        ConfirmOrder order = confirmOrder != null ? confirmOrder : denyOrder;
        String templatePath = confirmOrder != null
                ? "/templates/confirmOrder.xml"
                : "/templates/denyOrder.xml";

        if (order == null) {
            throw new IllegalStateException("Оба объекта — confirmOrder и denyOrder — равны null");
        }

        String orderTemp = loadTemplate(templatePath);

        String orderDigiSign = escapeXml(order.getDigiSign());

        Map<String, String> values = new HashMap<>();
        values.put("orderDigiSign", orderDigiSign);
        StringSubstitutor substitutor = new StringSubstitutor(values);

        return substitutor.replace(orderTemp);

    }

    /**
     * Экранирует XML символы в строке
     */
    private static String escapeXml(String input) {
        if (input == null) {
            return null;
        }
        return input.replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "&apos;");
    }

    public static String buildXMLReference(SearchOrderByReferenceNumberRequest referenceNumberRequest, String serviceId, String login, String password) throws IOException {

        String request;
        try {
            GregorianCalendar calendar = new GregorianCalendar();
            calendar.setTime(new Date());
            XMLGregorianCalendar xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);

            String template = loadTemplate("/templates/referenceRequest.xml");
            String messageID = UUID.randomUUID().toString();
            log.info("messageID: {}", messageID);

            return String.format(template, messageID, serviceId, xmlCalendar.toString(),
                    login, password, referenceNumberRequest.getReferenceNumber());
        } catch (Exception e) {
            log.error("Ошибка: {}", e.getMessage(), e);
            e.printStackTrace();
            return e.getMessage();
        }
    }
}
