package com.nebula.erp.sales.service;

import com.nebula.erp.sales.model.PaymentHistory;
import com.nebula.erp.sales.model.Sales;
import com.nebula.erp.sales.repository.PaymentHistoryRepository;
import com.nebula.erp.sales.repository.SalesRepository;
import com.nebula.erp.sales.requestmodel.*;
import com.nebula.erp.sales.utility.JwtRequestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class PaymentHistoryService {

    @Autowired
    private PaymentHistoryRepository repo;

    @Autowired
    private JwtRequestUtils jwt;

    @Autowired
    private SalesRepository salesRepository;

    @Autowired
    private RestTemplate restTemplate;

    @Value("${organization.api}")
    private String organizationAPI;

    @Value("${patient.api}")
    private String patientAPI;

    // ADD CREDIT (Advance)
    public PaymentHistory addCredit(PaymentCreditRequest req) {

        PaymentHistory ph = new PaymentHistory();

        ph.setPatient_id(req.getPatient_id());
        ph.setMrn_no(req.getMrn_no());
        ph.setTenant(jwt.getTenantName());
        ph.setCreated_by(jwt.getUserId());
        ph.setSales_id(req.getSales_id());
        ph.setEncounter_id(req.getEncounter_id());
        ph.setCustom_encounter_id(req.getCustom_encounter_id());
        ph.setPayment_method(req.getPayment_method());

        if (req.getSales_id() == null) {
            ph.setAdvance(Math.abs(req.getAmount()));
            ph.setCredit(0.0);
            ph.setEntry_type("ADVANCE_ADDED");
        } else {
            ph.setAdvance(0.0);
            ph.setCredit(Math.abs(req.getAmount()));
            ph.setEntry_type("PAYMENT");
        }

        ph.setDebit(0.0);
        ph.setAdvance_used(0.0);

        return repo.save(ph);
    }

    // ADD DEBIT (Bill Entry)
    public PaymentHistory addDebit(PaymentDebitRequest req) {

        List<PaymentHistory> existing = repo.findBySalesIdAndTenant(
                req.getSales_id(),
                jwt.getTenantName());

        for (PaymentHistory e : existing) {
            if ("DEBIT".equals(e.getEntry_type())) {
                return e; // prevent duplicates
            }
        }

        PaymentHistory ph = new PaymentHistory();

        ph.setPatient_id(req.getPatient_id());
        ph.setMrn_no(req.getMrn_no());
        ph.setDebit(req.getAmount());
        ph.setCredit(0.0);
        ph.setAdvance(0.0);
        ph.setAdvance_used(0.0);
        ph.setSales_id(req.getSales_id());
        ph.setEncounter_id(req.getEncounter_id());
        ph.setCustom_encounter_id(req.getCustom_encounter_id());
        ph.setTenant(jwt.getTenantName());
        ph.setCreated_by(jwt.getUserId());
        ph.setEntry_type("DEBIT");

        return repo.save(ph);
    }

    // CREDIT ADVANCE USED
    public PaymentHistory addAdvanceUsed(PaymentDebitRequest req) {

        PaymentHistory ph = new PaymentHistory();

        ph.setPatient_id(req.getPatient_id());
        ph.setMrn_no(req.getMrn_no());
        ph.setSales_id(req.getSales_id());
        ph.setEncounter_id(req.getEncounter_id());
        ph.setCustom_encounter_id(req.getCustom_encounter_id());
        ph.setAdvance_used(req.getAdvance_used());
        ph.setCredit(0.0);
        ph.setAdvance(0.0);
        ph.setDebit(0.0);
        ph.setEntry_type("ADVANCE_USED");
        ph.setTenant(jwt.getTenantName());
        ph.setCreated_by(jwt.getUserId());

        return repo.save(ph);
    }

    // Debit Refund
    public PaymentHistory addRefund(PaymentCreditRequest req) {

        PaymentHistory ph = new PaymentHistory();

        ph.setPatient_id(req.getPatient_id());
        ph.setMrn_no(req.getMrn_no());
        ph.setCredit(-Math.abs(req.getAmount()));
        ph.setAdvance(0.0);
        ph.setDebit(0.0);
        ph.setAdvance_used(0.0);
        ph.setEntry_type("REFUND");
        ph.setSales_id(null);
        ph.setEncounter_id(req.getEncounter_id());
        ph.setCustom_encounter_id(req.getCustom_encounter_id());
        ph.setTenant(jwt.getTenantName());
        ph.setCreated_by(jwt.getUserId());

        return repo.save(ph);
    }

    // BALANCE
    public PatientBalanceResponse getBalance(String patientId) {

        String tenant = jwt.getTenantName();

        double totalAdvance = repo.findOriginalAdvance(patientId, tenant);
        double totalRefund = repo.findTotalRefund(patientId, tenant);

        List<PaymentHistory> ledger = repo.findByPatient(patientId, tenant);

        Map<Long, Double> saleDebitMap = ledger.stream()
                .filter(e -> e.getSales_id() != null && e.getDebit() > 0)
                .collect(Collectors.groupingBy(
                        PaymentHistory::getSales_id,
                        Collectors.summingDouble(PaymentHistory::getDebit)));

        Map<Long, Double> salePaymentMap = ledger.stream()
                .filter(e -> e.getSales_id() != null && "PAYMENT".equals(e.getEntry_type()))
                .collect(Collectors.groupingBy(
                        PaymentHistory::getSales_id,
                        Collectors.summingDouble(PaymentHistory::getCredit)));

        Map<Long, Double> saleAdvanceUsedMap = ledger.stream()
                .filter(e -> e.getSales_id() != null && "ADVANCE_USED".equals(e.getEntry_type()))
                .collect(Collectors.groupingBy(
                        PaymentHistory::getSales_id,
                        Collectors.summingDouble(PaymentHistory::getAdvance_used)));

        double due = saleDebitMap.keySet().stream()
                .mapToDouble(saleId -> {
                    double debit = saleDebitMap.getOrDefault(saleId, 0.0);
                    double paid = salePaymentMap.getOrDefault(saleId, 0.0)
                            + saleAdvanceUsedMap.getOrDefault(saleId, 0.0);
                    return Math.max(debit - paid, 0);
                })
                .sum();

        if (due < 0)
            due = 0;

        double advanceUsed = ledger.stream()
                .filter(e -> "ADVANCE_USED".equals(e.getEntry_type()))
                .mapToDouble(PaymentHistory::getAdvance_used)
                .sum();

        double availableAdvance = totalAdvance - advanceUsed - totalRefund;

        PatientBalanceResponse res = new PatientBalanceResponse();
        res.setPatient_id(patientId);
        res.setAdvance_balance_value(availableAdvance);
        res.setAdvance_balance_type(availableAdvance > 0 ? "ADVANCE" : "NONE");
        res.setTotal_balance_value(due);
        res.setTotal_balance_type(due > 0 ? "DUE" : "PAID");

        return res;
    }

    // RAZORPAY PUBLIC CREDIT
    public PaymentHistory addCreditPublic(PaymentCreditRequest req) {

        Sales sale = salesRepository.findById(req.getSales_id())
                .orElseThrow(() -> new RuntimeException("Sale not found"));

        PaymentHistory ph = new PaymentHistory();

        ph.setPatient_id(req.getPatient_id());
        ph.setMrn_no(req.getMrn_no());
        ph.setSales_id(req.getSales_id());
        ph.setEncounter_id(req.getEncounter_id());
        ph.setCustom_encounter_id(req.getCustom_encounter_id());
        ph.setCredit(Math.abs(req.getAmount()));
        ph.setAdvance(0.0);
        ph.setDebit(0.0);
        ph.setAdvance_used(0.0);
        ph.setEntry_type("PAYMENT");
        ph.setTenant(sale.getTenant());
        ph.setCreated_by("SYSTEM_RAZORPAY");

        return repo.save(ph);
    }

    // Export Advance
    public Map<String, Object> exportAdvanceByEncounter(String encounterId) {

        String tenant = jwt.getTenantName();

        List<PaymentHistory> advances = repo.findAdvanceAddedByEncounter(encounterId, tenant);

        if (advances.isEmpty()) {
            throw new IllegalArgumentException("No advance found for encounter");
        }

        PaymentHistory first = advances.get(0);

        double totalAdvance = advances.stream()
                .mapToDouble(PaymentHistory::getAdvance)
                .sum();

        Map<String, Object> data = new LinkedHashMap<>();

        data.put("bill_type", "ADVANCE RECEIPT");
        data.put("bill_date", first.getCreated_at());
        data.put("patient_id", first.getPatient_id());
        data.put("mrn_no", first.getMrn_no());
        data.put("encounter_id", first.getEncounter_id());
        data.put("custom_encounter_id", first.getCustom_encounter_id());
        String patientName = "-";
        String customerName = "-";
        String customerMobile = "-";
        String customerEmail = null;
        String customerDob = null;
        String customerGender = "-";
        String customerAddress = "-";

        try {
            HttpHeaders headers = jwt.getAuthorizationHeaders();
            HttpEntity<String> entity = new HttpEntity<>(headers);

            Map<String, Object> patientResp = restTemplate
                    .exchange(patientAPI + first.getPatient_id(),
                            HttpMethod.GET,
                            entity,
                            Map.class)
                    .getBody();

            if (patientResp != null && patientResp.get("data") instanceof Map) {

                Map<String, Object> p = (Map<String, Object>) patientResp.get("data");

                patientName = String.valueOf(
                        p.getOrDefault("patient_name",
                                p.getOrDefault("full_name", "-")));

                customerName = patientName;

                customerMobile = String.valueOf(
                        p.getOrDefault("mobile",
                                p.getOrDefault("mobile_no",
                                        p.getOrDefault("customer_mobile", "-"))));

                customerEmail = (String) p.getOrDefault("email",
                        p.getOrDefault("customer_email", null));

                customerDob = (String) p.getOrDefault("dob",
                        p.getOrDefault("customer_dob", null));

                customerGender = String.valueOf(
                        p.getOrDefault("gender",
                                p.getOrDefault("customer_gender", "-")));

                customerAddress = String.valueOf(
                        p.getOrDefault("address",
                                p.getOrDefault("customer_address", "-")));
            }

        } catch (Exception ignored) {

        }

        data.put("patient_name", patientName);
        data.put("customer_name", customerName);
        data.put("customer_mobile", customerMobile);
        data.put("customer_email", customerEmail);
        data.put("customer_dob", customerDob);
        data.put("customer_gender", customerGender);
        data.put("customer_address", customerAddress);
        data.put("total_advance_amount", totalAdvance);
        List<Map<String, Object>> items = advances.stream().map(ph -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("date", ph.getCreated_at());
            m.put("advance_amount", ph.getAdvance());
            m.put("collected_by", ph.getCreated_by());
            m.put("payment_method", ph.getPayment_method());
            return m;
        }).collect(Collectors.toList());

        data.put("items", items);
        Map<String, String> columns = new LinkedHashMap<>();
        columns.put("bill_type", "BILL TYPE");
        columns.put("bill_date", "BILL DATE");
        columns.put("patient_id", "PATIENT ID");
        columns.put("mrn_no", "MRN NO");
        columns.put("encounter_id", "ENCOUNTER ID");
        columns.put("custom_encounter_id", "CUSTOM ENCOUNTER ID");

        columns.put("patient_name", "PATIENT NAME");
        columns.put("customer_name", "CUSTOMER NAME");
        columns.put("customer_mobile", "CUSTOMER MOBILE");
        columns.put("customer_email", "CUSTOMER EMAIL");
        columns.put("customer_dob", "DATE OF BIRTH");
        columns.put("customer_gender", "GENDER");
        columns.put("customer_address", "ADDRESS");

        // Advance summary
        columns.put("total_advance_amount", "TOTAL ADVANCE AMOUNT");

        // Line items
        columns.put("date", "DATE");
        columns.put("advance_amount", "ADVANCE AMOUNT");
        columns.put("collected_by", "COLLECTED BY");
        columns.put("payment_method", "PAYMENT METHOD");

        data.put("columns", columns);
        HttpHeaders headers = jwt.getAuthorizationHeaders();
        HttpEntity<String> entity = new HttpEntity<>(headers);

        Map<String, Object> orgResp = restTemplate
                .exchange(organizationAPI, HttpMethod.GET, entity, Map.class)
                .getBody();

        if (orgResp != null) {
            data.put("organization", orgResp.get("data"));
        }

        return data;
    }

    // Export Refund
    public Map<String, Object> exportRefundByEncounter(String encounterId) {

        String tenant = jwt.getTenantName();

        List<PaymentHistory> refunds = repo.findRefundByEncounter(encounterId, tenant);

        if (refunds.isEmpty()) {
            throw new IllegalArgumentException("No refund found for encounter");
        }

        PaymentHistory first = refunds.get(0);

        double totalRefund = refunds.stream()
                .mapToDouble(ph -> Math.abs(ph.getCredit()))
                .sum();

        Map<String, Object> data = new LinkedHashMap<>();

        data.put("bill_type", "REFUND RECEIPT");
        data.put("bill_date", first.getCreated_at());
        data.put("patient_id", first.getPatient_id());
        data.put("mrn_no", first.getMrn_no());
        data.put("encounter_id", first.getEncounter_id());
        data.put("custom_encounter_id", first.getCustom_encounter_id());
        data.put("total_refund_amount", totalRefund);

        String patientName = "-";
        String customerMobile = "-";
        String customerEmail = null;

        try {
            HttpHeaders headers = jwt.getAuthorizationHeaders();
            HttpEntity<String> entity = new HttpEntity<>(headers);

            Map<String, Object> patientResp = restTemplate
                    .exchange(patientAPI + first.getPatient_id(),
                            HttpMethod.GET,
                            entity,
                            Map.class)
                    .getBody();

            if (patientResp != null && patientResp.get("data") instanceof Map) {

                Map<String, Object> p = (Map<String, Object>) patientResp.get("data");

                patientName = String.valueOf(
                        p.getOrDefault("patient_name",
                                p.getOrDefault("full_name", "-")));

                customerMobile = String.valueOf(
                        p.getOrDefault("mobile",
                                p.getOrDefault("mobile_no", "-")));

                customerEmail = (String) p.getOrDefault("email", null);
            }

        } catch (Exception ignored) {
        }

        data.put("patient_name", patientName);
        data.put("customer_mobile", customerMobile);
        data.put("customer_email", customerEmail);

        List<Map<String, Object>> items = refunds.stream().map(ph -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("date", ph.getCreated_at());
            m.put("refund_amount", Math.abs(ph.getCredit()));
            m.put("refunded_by", ph.getCreated_by());
            return m;
        }).collect(Collectors.toList());

        data.put("items", items);

    
        Map<String, String> columns = new LinkedHashMap<>();
        columns.put("bill_type", "BILL TYPE");
        columns.put("bill_date", "BILL DATE");
        columns.put("patient_id", "PATIENT ID");
        columns.put("mrn_no", "MRN NO");
        columns.put("encounter_id", "ENCOUNTER ID");
        columns.put("custom_encounter_id", "CUSTOM ENCOUNTER ID");
        columns.put("patient_name", "PATIENT NAME");
        columns.put("customer_mobile", "CUSTOMER MOBILE");
        columns.put("customer_email", "CUSTOMER EMAIL");
        columns.put("total_refund_amount", "TOTAL REFUND AMOUNT");
        columns.put("date", "DATE");
        columns.put("refund_amount", "REFUND AMOUNT");
        columns.put("refunded_by", "REFUNDED BY");

        data.put("columns", columns);

    
        try {
            HttpHeaders headers = jwt.getAuthorizationHeaders();
            HttpEntity<String> entity = new HttpEntity<>(headers);

            Map<String, Object> orgResp = restTemplate
                    .exchange(organizationAPI,
                            HttpMethod.GET,
                            entity,
                            Map.class)
                    .getBody();

            if (orgResp != null) {
                data.put("organization", orgResp.get("data"));
            }

        } catch (Exception ignored) {
        }

        return data;
    }

}