package com.example.petstoremobile.viewmodels;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.dtos.SaleDTO;
import com.example.petstoremobile.repositories.SaleRepository;
import com.example.petstoremobile.utils.Resource;

import androidx.lifecycle.Observer;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

import javax.inject.Inject;

import dagger.hilt.android.lifecycle.HiltViewModel;

@HiltViewModel
public class AnalyticsViewModel extends ViewModel {
    private final SaleRepository saleRepository;
    private final TokenManager tokenManager;

    private final MutableLiveData<AnalyticsData> analyticsData = new MutableLiveData<>();
    private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
    private final MutableLiveData<String> errorMessage = new MutableLiveData<>();
    private final MutableLiveData<List<String>> availablePaymentMethods = new MutableLiveData<>(new ArrayList<>());
    private final MutableLiveData<List<String>> availableStores = new MutableLiveData<>(new ArrayList<>());

    private List<SaleDTO> cachedSales = new ArrayList<>();
    private FilterState currentFilter = new FilterState();
    private String viewMode = "store";
    private String storeFilter = "All Stores";

    @Inject
    public AnalyticsViewModel(SaleRepository saleRepository, TokenManager tokenManager) {
        this.saleRepository = saleRepository;
        this.tokenManager = tokenManager;
    }

    public LiveData<AnalyticsData> getAnalyticsData() { return analyticsData; }
    public LiveData<Boolean> getIsLoading() { return isLoading; }
    public LiveData<String> getErrorMessage() { return errorMessage; }
    public LiveData<List<String>> getAvailablePaymentMethods() { return availablePaymentMethods; }
    public LiveData<List<String>> getAvailableStores() { return availableStores; }

    public void loadAnalytics() {
        isLoading.setValue(true);
        errorMessage.setValue(null);
        observeOnce(saleRepository.getAllSales(0, 2000, null, null, null, null, null, "saleDate,desc"), resource -> {
            if (resource != null) {
                if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
                    cachedSales = resource.data.getContent();
                    derivePaymentMethods();
                    deriveStores();
                    applyCurrentFilter();
                    isLoading.setValue(false);
                } else if (resource.status == Resource.Status.ERROR) {
                    errorMessage.setValue(resource.message);
                    isLoading.setValue(false);
                }
            }
        });
    }

    public void applyFilter(FilterState filter) {
        currentFilter = filter;
        applyCurrentFilter();
    }

    public void resetFilter() {
        currentFilter = new FilterState();
        storeFilter = "All Stores";
        applyCurrentFilter();
    }

    public void setViewMode(String mode) {
        viewMode = mode;
        applyCurrentFilter();
    }

    public String getViewMode() {
        return viewMode;
    }

    public void setStoreFilter(String store) {
        storeFilter = (store != null && !store.isEmpty()) ? store : "All Stores";
        applyCurrentFilter();
    }

    public String getStoreFilter() {
        return storeFilter;
    }

    private void applyCurrentFilter() {
        List<SaleDTO> salesForMode;
        if (viewMode.equals("mine")) {
            String currentUser = tokenManager.getUsername();
            salesForMode = cachedSales.stream()
                .filter(s -> currentUser != null && currentUser.equalsIgnoreCase(s.getEmployeeName() != null ? s.getEmployeeName() : ""))
                .collect(Collectors.toList());
        } else {
            salesForMode = cachedSales;
        }
        if (!storeFilter.equals("All Stores") && !storeFilter.isEmpty()) {
            final String sf = storeFilter;
            salesForMode = salesForMode.stream()
                .filter(s -> sf.equalsIgnoreCase(s.getStoreName() != null ? s.getStoreName() : ""))
                .collect(Collectors.toList());
        }
        List<SaleDTO> filtered = filterSales(salesForMode, currentFilter);
        computeAnalytics(filtered, currentFilter);
    }

    private void deriveStores() {
        java.util.Set<String> stores = new java.util.TreeSet<>();
        for (SaleDTO s : cachedSales) {
            if (s.getStoreName() != null && !s.getStoreName().isEmpty()) {
                stores.add(s.getStoreName());
            }
        }
        List<String> result = new ArrayList<>();
        result.add("All Stores");
        result.addAll(stores);
        availableStores.setValue(result);
    }

    private void derivePaymentMethods() {
        java.util.Set<String> methods = new java.util.TreeSet<>();
        for (SaleDTO s : cachedSales) {
            if (s.getPaymentMethod() != null && !s.getPaymentMethod().isEmpty()) {
                methods.add(s.getPaymentMethod());
            }
        }
        List<String> result = new ArrayList<>();
        result.add("All");
        result.addAll(methods);
        availablePaymentMethods.setValue(result);
    }

    private List<SaleDTO> filterSales(List<SaleDTO> sales, FilterState filter) {
        List<SaleDTO> result = new ArrayList<>();
        for (SaleDTO s : sales) {
            String date = s.getSaleDate() != null && s.getSaleDate().length() >= 10
                    ? s.getSaleDate().substring(0, 10) : "";
            if (!filter.startDate.isEmpty() && !date.isEmpty() && date.compareTo(filter.startDate) < 0) continue;
            if (!filter.endDate.isEmpty() && !date.isEmpty() && date.compareTo(filter.endDate) > 0) continue;
            if (!filter.paymentMethod.equals("All") && !filter.paymentMethod.isEmpty()) {
                if (!filter.paymentMethod.equalsIgnoreCase(s.getPaymentMethod())) continue;
            }
            result.add(s);
        }
        return result;
    }

    private void computeAnalytics(List<SaleDTO> sales, FilterState filter) {
        List<SaleDTO> regularSales = new ArrayList<>();
        for (SaleDTO s : sales) {
            if (!Boolean.TRUE.equals(s.getIsRefund())) regularSales.add(s);
        }

        AnalyticsData data = new AnalyticsData();

        // Summary
        BigDecimal totalRevenue = BigDecimal.ZERO;
        int totalItems = 0;
        for (SaleDTO s : regularSales) {
            if (s.getTotalAmount() != null) totalRevenue = totalRevenue.add(s.getTotalAmount());
            if (s.getItems() != null) {
                for (SaleDTO.SaleItemDTO item : s.getItems()) {
                    if (item.getQuantity() != null) totalItems += Math.abs(item.getQuantity());
                }
            }
        }
        data.totalRevenue = totalRevenue;
        data.totalTransactions = regularSales.size();
        data.avgTransaction = data.totalTransactions > 0
                ? totalRevenue.divide(BigDecimal.valueOf(data.totalTransactions), 2, RoundingMode.HALF_UP)
                : BigDecimal.ZERO;
        data.totalItems = totalItems;

        Map<String, BigDecimal> revenueByProduct = new LinkedHashMap<>();
        Map<String, Integer> quantityByProduct = new LinkedHashMap<>();
        Map<String, Integer> paymentCount = new LinkedHashMap<>();
        Map<String, BigDecimal> employeeRevenue = new LinkedHashMap<>();

        for (SaleDTO s : regularSales) {
            String method = s.getPaymentMethod() != null ? s.getPaymentMethod() : "Unknown";
            paymentCount.merge(method, 1, Integer::sum);

            String emp = s.getEmployeeName() != null ? s.getEmployeeName() : "Unknown";
            if (s.getTotalAmount() != null) employeeRevenue.merge(emp, s.getTotalAmount(), BigDecimal::add);

            if (s.getItems() != null) {
                for (SaleDTO.SaleItemDTO item : s.getItems()) {
                    String name = item.getProductName() != null ? item.getProductName() : "Unknown";
                    int qty = item.getQuantity() != null ? Math.abs(item.getQuantity()) : 0;
                    BigDecimal lineTotal = item.getUnitPrice() != null
                            ? item.getUnitPrice().multiply(BigDecimal.valueOf(qty)) : BigDecimal.ZERO;
                    revenueByProduct.merge(name, lineTotal, BigDecimal::add);
                    quantityByProduct.merge(name, qty, Integer::sum);
                }
            }
        }

        int topN = filter.topN > 0 ? filter.topN : 5;

        data.topRevenueProducts = new ArrayList<>(revenueByProduct.entrySet());
        data.topRevenueProducts.sort((a, b) -> b.getValue().compareTo(a.getValue()));
        if (data.topRevenueProducts.size() > topN) data.topRevenueProducts = data.topRevenueProducts.subList(0, topN);

        data.topQuantityProducts = new ArrayList<>(quantityByProduct.entrySet());
        data.topQuantityProducts.sort((a, b) -> b.getValue() - a.getValue());
        if (data.topQuantityProducts.size() > topN) data.topQuantityProducts = data.topQuantityProducts.subList(0, topN);

        data.paymentMethodStats = new ArrayList<>(paymentCount.entrySet());
        data.paymentMethodStats.sort((a, b) -> b.getValue() - a.getValue());

        data.employeePerformance = new ArrayList<>(employeeRevenue.entrySet());
        data.employeePerformance.sort((a, b) -> b.getValue().compareTo(a.getValue()));

        // Daily revenue display to filter date range, max 60 days
        String rangeStart = filter.startDate;
        String rangeEnd = filter.endDate;
        if (rangeStart.isEmpty() && rangeEnd.isEmpty()) {
            rangeEnd = todayString(0);
            rangeStart = todayString(-6);
        } else if (rangeStart.isEmpty()) {
            rangeStart = shiftDate(rangeEnd, -6);
        } else if (rangeEnd.isEmpty()) {
            rangeEnd = todayString(0);
        }

        List<String> dateRange = buildDateRange(rangeStart, rangeEnd, 60);
        Map<String, BigDecimal> dailyMap = new TreeMap<>();
        for (String d : dateRange) dailyMap.put(d, BigDecimal.ZERO);
        for (SaleDTO s : regularSales) {
            if (s.getSaleDate() != null && s.getTotalAmount() != null) {
                String d = s.getSaleDate().length() >= 10 ? s.getSaleDate().substring(0, 10) : s.getSaleDate();
                if (dailyMap.containsKey(d)) dailyMap.merge(d, s.getTotalAmount(), BigDecimal::add);
            }
        }
        data.dailyRevenue = new ArrayList<>(dailyMap.entrySet());
        data.dailyRevenueTitle = buildDailyTitle(filter, rangeStart, rangeEnd);

        analyticsData.setValue(data);
    }

    private String todayString(int offsetDays) {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DAY_OF_YEAR, offsetDays);
        return String.format(Locale.US, "%04d-%02d-%02d",
                c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));
    }

    private String shiftDate(String date, int offsetDays) {
        try {
            String[] p = date.split("-");
            Calendar c = Calendar.getInstance();
            c.set(Integer.parseInt(p[0]), Integer.parseInt(p[1]) - 1, Integer.parseInt(p[2]), 0, 0, 0);
            c.add(Calendar.DAY_OF_YEAR, offsetDays);
            return String.format(Locale.US, "%04d-%02d-%02d",
                    c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));
        } catch (Exception e) {
            return date;
        }
    }

    private List<String> buildDateRange(String start, String end, int maxDays) {
        List<String> dates = new ArrayList<>();
        try {
            String[] sp = start.split("-");
            String[] ep = end.split("-");
            Calendar cur = Calendar.getInstance();
            cur.set(Integer.parseInt(sp[0]), Integer.parseInt(sp[1]) - 1, Integer.parseInt(sp[2]), 0, 0, 0);
            Calendar endCal = Calendar.getInstance();
            endCal.set(Integer.parseInt(ep[0]), Integer.parseInt(ep[1]) - 1, Integer.parseInt(ep[2]), 0, 0, 0);
            int count = 0;
            while (!cur.after(endCal) && count < maxDays) {
                dates.add(String.format(Locale.US, "%04d-%02d-%02d",
                        cur.get(Calendar.YEAR), cur.get(Calendar.MONTH) + 1, cur.get(Calendar.DAY_OF_MONTH)));
                cur.add(Calendar.DAY_OF_YEAR, 1);
                count++;
            }
        } catch (Exception ignored) {}
        return dates;
    }

    private String buildDailyTitle(FilterState filter, String rangeStart, String rangeEnd) {
        if (filter.startDate.isEmpty() && filter.endDate.isEmpty()) return "Daily Revenue (Last 7 Days)";
        String s = rangeStart.length() >= 10 ? rangeStart.substring(5) : rangeStart;
        String e = rangeEnd.length() >= 10 ? rangeEnd.substring(5) : rangeEnd;
        return "Daily Revenue (" + s + " – " + e + ")";
    }

    private <T> void observeOnce(LiveData<Resource<T>> liveData, Observer<Resource<T>> handler) {
        liveData.observeForever(new Observer<Resource<T>>() {
            @Override
            public void onChanged(Resource<T> resource) {
                if (resource == null || resource.status != Resource.Status.LOADING) {
                    liveData.removeObserver(this);
                }
                handler.onChanged(resource);
            }
        });
    }

    public static class FilterState {
        public String startDate = "";
        public String endDate = "";
        public String paymentMethod = "All";
        public int topN = 5;
    }

    public static class AnalyticsData {
        public BigDecimal totalRevenue;
        public int totalTransactions;
        public BigDecimal avgTransaction;
        public int totalItems;
        public List<Map.Entry<String, BigDecimal>> topRevenueProducts;
        public List<Map.Entry<String, Integer>> topQuantityProducts;
        public List<Map.Entry<String, Integer>> paymentMethodStats;
        public List<Map.Entry<String, BigDecimal>> employeePerformance;
        public List<Map.Entry<String, BigDecimal>> dailyRevenue;
        public String dailyRevenueTitle = "Daily Revenue";
    }
}
