package hr.com.port.ips.eracun.provider.pondi.mapper;

import hr.com.port.ips.eracun.provider.model.doc.DocumentBinary;
import hr.com.port.ips.eracun.provider.model.doc.DocumentHeader;
import hr.com.port.ips.eracun.provider.model.doc.DocumentKind;
import hr.com.port.ips.eracun.provider.model.doc.DocumentRef;
import hr.com.port.ips.eracun.provider.model.doc.DocumentSnapshot;
import hr.com.port.ips.eracun.provider.model.ids.DocumentKey;
import hr.com.port.ips.eracun.provider.model.ids.RemoteId;
import hr.com.port.ips.eracun.provider.model.money.Money;
import hr.com.port.ips.eracun.provider.model.money.Totals;
import hr.com.port.ips.eracun.provider.model.party.Party;
import hr.com.port.ips.eracun.provider.model.party.PartyId;
import hr.com.port.ips.eracun.provider.model.status.BusinessStatus;
import hr.com.port.ips.eracun.provider.model.status.EventType;
import hr.com.port.ips.eracun.provider.model.status.SettlementStatus;
import hr.com.port.ips.eracun.provider.model.status.TimelineEvent;
import hr.com.port.ips.eracun.provider.model.status.TransportStatus;
import hr.com.port.ips.eracun.provider.pondi.dto.document.DocumentStatusHistory;
import hr.com.port.ips.eracun.provider.pondi.dto.document.DocumentStatusResponse;
import hr.com.port.ips.eracun.provider.pondi.dto.ereporting.EReportingRequest;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class PondiModelMapper {

    public static DocumentRef toRef(long id, String insertedOn) {
        DocumentRef r = new DocumentRef();
        r.id = new RemoteId(String.valueOf(id));
        r.insertedOn = insertedOn;
        return r;
    }

    public static DocumentBinary xmlToBinary(String xmlBase64) {
        DocumentBinary b = new DocumentBinary();
        b.contentBase64 = xmlBase64;
        b.mime = "application/xml";
        return b;
    }

    public static DocumentBinary pdfToBinary(String pdfBase64) {
        DocumentBinary b = new DocumentBinary();
        b.contentBase64 = pdfBase64;
        b.mime = "application/pdf";
        return b;
    }

    public static DocumentHeader toHeader(DocumentStatusResponse s) {
        DocumentHeader h = new DocumentHeader();
        h.id = new RemoteId(String.valueOf(s.id));
        h.key = toKey(s);
        h.kind = "CreditNote".equalsIgnoreCase(s.documentType) ? DocumentKind.CREDIT_NOTE : DocumentKind.INVOICE;
        h.issuedOn = s.issuedOn;
        h.modifiedOn = s.modifiedOn;
        h.totals = new Totals();
        h.totals.gross = new Money();
        if (s.amount != null) {
            h.totals.gross.amount = new BigDecimal(String.valueOf(s.amount));
        }
        h.totals.gross.currency = s.currency;
        h.transportStatus = mapTransport(s.status);
        h.businessStatus = mapBusiness(s.status);
        h.settlementStatus = mapSettlement(s.status);
        return h;
    }

    public static DocumentSnapshot toSnapshot(DocumentStatusResponse s) {
        DocumentSnapshot d = new DocumentSnapshot();
        d.id = new RemoteId(String.valueOf(s.id));
        d.key = toKey(s);
        d.kind = "CreditNote".equalsIgnoreCase(s.documentType) ? DocumentKind.CREDIT_NOTE : DocumentKind.INVOICE;
        d.issuedOn = s.issuedOn;
        d.modifiedOn = s.modifiedOn;
        d.totals = new Totals();
        d.totals.gross = new Money();
        if (s.amount != null) {
            d.totals.gross.amount = new BigDecimal(String.valueOf(s.amount));
        }
        d.totals.gross.currency = s.currency;

        Party sup = new Party();
        sup.name = s.supplierPartyName;
        sup.ids = new PartyId[] { new PartyId("9934", s.supplierPartyVATId) };
        d.supplier = sup;

        Party cust = new Party();
        cust.name = s.customerPartyName;
        cust.ids = new PartyId[] { new PartyId("9934", s.customerPartyVATId) };
        d.customer = cust;

        List<TimelineEvent> events = new ArrayList<TimelineEvent>();
        if (s.statusHistory != null) {
            for (DocumentStatusHistory h : s.statusHistory) {
                TimelineEvent te = new TimelineEvent();
                te.when = h.createdOn;
                te.type = mapEventType(h.status);
                te.note = h.statusText;
                events.add(te);
            }
        }
        if (s.eReportingRequests != null) {
            for (EReportingRequest r : s.eReportingRequests) {
                TimelineEvent te = new TimelineEvent();
                te.when = r.createdOn;
                te.type = EventType.EREPORT_REPORTED; // generic; no finer granularity in DTO
                te.note = r.description;
                te.refId = r.referenceId;
                events.add(te);
            }
        }
        d.timeline = events.toArray(new TimelineEvent[events.size()]);
        return d;
    }

    private static DocumentKey toKey(DocumentStatusResponse s) {
        DocumentKey k = new DocumentKey();
        k.documentId = s.documentId;
        k.issueDate = s.issuedOn != null && s.issuedOn.length() >= 10 ? s.issuedOn.substring(0,10) : null;
        k.supplier = new PartyId("9934", s.supplierPartyVATId);
        k.customer = new PartyId("9934", s.customerPartyVATId);
        return k;
    }

    private static TransportStatus mapTransport(Integer status) {
        if (status == null) return TransportStatus.UNKNOWN;
        switch (status.intValue()) {
            case 1: return TransportStatus.PENDING;
            case 2: return TransportStatus.PENDING;
            case 3: return TransportStatus.SENT;
            case 4: return TransportStatus.DELIVERED;
            case 9: return TransportStatus.FAILED;
            default: return TransportStatus.UNKNOWN;
        }
    }

    private static BusinessStatus mapBusiness(Integer status) {
        if (status == null) return BusinessStatus.UNKNOWN;
        switch (status.intValue()) {
            case 5: return BusinessStatus.APPROVED;
            case 6: return BusinessStatus.REJECTED;
            default: return BusinessStatus.PENDING;
        }
    }

    private static SettlementStatus mapSettlement(Integer status) {
        if (status == null) return SettlementStatus.UNKNOWN;
        switch (status.intValue()) {
            case 7: return SettlementStatus.PAID;
            case 8: return SettlementStatus.PARTIALLY_PAID;
            default: return SettlementStatus.UNPAID;
        }
    }

    private static EventType mapEventType(Integer status) {
        if (status == null) return EventType.OTHER;
        switch (status.intValue()) {
            case 3: return EventType.TRANSPORT_SENT;
            case 4: return EventType.TRANSPORT_DELIVERED;
            case 5: return EventType.BUSINESS_APPROVED;
            case 6: return EventType.BUSINESS_REJECTED;
            case 9: return EventType.TRANSPORT_FAILED;
            default: return EventType.OTHER;
        }
    }
}
