package com.example.bdkipoc;

import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;
import android.widget.TextView;
import android.widget.LinearLayout;
import android.content.Intent;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Calendar;
import java.util.Collections;
import java.util.TimeZone;

import android.app.DatePickerDialog;
import android.widget.DatePicker;

public class TransactionActivity extends AppCompatActivity implements TransactionAdapter.OnPrintClickListener {
    private RecyclerView recyclerView;
    private TransactionAdapter adapter;
    private List<Transaction> transactionList;
    private List<Transaction> filteredList;
    private ProgressBar progressBar;
    private EditText searchEditText;
    private ImageButton searchButton;
    
    // ✅ PAGINATION UI ELEMENTS
    private LinearLayout infoBar;
    private TextView textTotalRecords;
    private TextView textPageInfo;
    private LinearLayout paginationControls;
    private LinearLayout pageNumbersContainer;
    private ImageButton btnFirstPage, btnPrevPage, btnNextPage, btnLastPage;
    
    // ✅ FRONTEND DEDUPLICATION: Local caching and tracking
    private Map<String, Transaction> transactionCache = new HashMap<>();
    private Set<String> processedReferences = new HashSet<>();
    private SharedPreferences prefs;
    
    // ✅ DATE FILTER VARIABLES
    private String fromDate = "";
    private String toDate = "";
    private TextView filterButtonText;
    
    // ✅ UPDATED PAGINATION VARIABLES
    private int currentPage = 1; // Start from page 1 instead of 0
    private final int itemsPerPage = 15; // ✅ 15 items per page as requested
    private int totalRecords = 0;
    private int totalPages = 0;
    private boolean isLoading = false;
    private String currentSearchQuery = "";
    private boolean isRefreshing = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transaction);

        // ✅ Initialize SharedPreferences for local tracking
        prefs = getSharedPreferences("transaction_prefs", MODE_PRIVATE);

        // Initialize views
        initViews();
        
        // Setup toolbar
        setupToolbar();
        
        // Setup RecyclerView
        setupRecyclerView();
        
        // Setup search functionality
        setupSearch();
        
        // ✅ Setup pagination controls
        setupPaginationControls();
        
        // Load initial data
        loadTransactions(1); // Start from page 1
    }

    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);
        progressBar = findViewById(R.id.progressBar);
        searchEditText = findViewById(R.id.searchEditText);
        
        // ✅ APPLY PROGRAMMATIC STYLING
        LinearLayout searchContainer = findViewById(R.id.searchContainer);
        LinearLayout filterButton = findViewById(R.id.filterButton);
        
        StyleHelper.applySearchInputStyle(searchContainer, this);
        StyleHelper.applyFilterButtonStyle(filterButton, this);
        
        // ✅ PAGINATION UI ELEMENTS
        paginationControls = findViewById(R.id.paginationControls);
        pageNumbersContainer = findViewById(R.id.pageNumbersContainer);
        btnFirstPage = findViewById(R.id.btnFirstPage);
        btnPrevPage = findViewById(R.id.btnPrevPage);
        btnNextPage = findViewById(R.id.btnNextPage);
        btnLastPage = findViewById(R.id.btnLastPage);
        
        // Apply pagination button styling (updated sizes)
        StyleHelper.applyPaginationButtonStyle(btnFirstPage, this, false);
        StyleHelper.applyPaginationButtonStyle(btnPrevPage, this, false);
        StyleHelper.applyPaginationButtonStyle(btnNextPage, this, false);
        StyleHelper.applyPaginationButtonStyle(btnLastPage, this, false);
    }

    private void setupToolbar() {
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        
        // Hide default title since we're using custom layout
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayShowTitleEnabled(false);
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
        }
        
        // Setup custom back button
        ImageView backButton = findViewById(R.id.backButton);
        backButton.setOnClickListener(v -> finish());
    }

    private void setupRecyclerView() {
        transactionList = new ArrayList<>();
        filteredList = new ArrayList<>();
        
        adapter = new TransactionAdapter(filteredList);
        adapter.setPrintClickListener(this);
        
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

        // ✅ REMOVED: Auto-pagination scroll listener since we use manual pagination now
        // Manual pagination is better for user control and performance
    }

    private void setupSearch() {
        // Search on text change
        searchEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                currentSearchQuery = s.toString().trim();
                // ✅ RESET TO PAGE 1 when searching
                currentPage = 1;
                filterTransactions(currentSearchQuery);
            }

            @Override
            public void afterTextChanged(Editable s) {}
        });
        
        // Filter button click
        LinearLayout filterButton = findViewById(R.id.filterButton);
        filterButtonText = (TextView) filterButton.findViewById(R.id.filterButtonText);
        
        filterButton.setOnClickListener(v -> {
            showDateRangePickerDialog();
        });
        
        // Long press to clear filter
        filterButton.setOnLongClickListener(v -> {
            if (!fromDate.isEmpty() || !toDate.isEmpty()) {
                clearDateFilter();
                Toast.makeText(this, "Date filter cleared", Toast.LENGTH_SHORT).show();
            }
            return true;
        });
    }

    // ✅ NEW METHOD: Setup pagination controls
    private void setupPaginationControls() {
        btnFirstPage.setOnClickListener(v -> {
            if (currentPage > 1) {
                goToPage(1);
            }
        });
        
        btnPrevPage.setOnClickListener(v -> {
            if (currentPage > 1) {
                goToPage(currentPage - 1);
            }
        });
        
        btnNextPage.setOnClickListener(v -> {
            if (currentPage < totalPages) {
                goToPage(currentPage + 1);
            }
        });
        
        btnLastPage.setOnClickListener(v -> {
            if (currentPage < totalPages) {
                goToPage(totalPages);
            }
        });
    }

    // ✅ NEW METHOD: Show date range picker dialog
    private void showDateRangePickerDialog() {
        Calendar calendar = Calendar.getInstance();
        
        // First, pick FROM date
        DatePickerDialog fromDatePicker = new DatePickerDialog(this,
            (view, year, month, dayOfMonth) -> {
                Calendar fromCal = Calendar.getInstance();
                fromCal.set(year, month, dayOfMonth);
                
                // Format for API (yyyy-MM-dd)
                SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
                fromDate = apiFormat.format(fromCal.getTime());
                
                // Now pick TO date
                showToDatePicker(fromCal);
            },
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        );
        
        fromDatePicker.setTitle("Select From Date");
        fromDatePicker.show();
    }
    
    // ✅ NEW METHOD: Show TO date picker
    private void showToDatePicker(Calendar fromCal) {
        Calendar calendar = Calendar.getInstance();
        
        DatePickerDialog toDatePicker = new DatePickerDialog(this,
            (view, year, month, dayOfMonth) -> {
                Calendar toCal = Calendar.getInstance();
                toCal.set(year, month, dayOfMonth);
                
                // Validate date range
                if (toCal.before(fromCal)) {
                    Toast.makeText(this, "To date cannot be before from date", Toast.LENGTH_SHORT).show();
                    return;
                }
                
                // Format for API (yyyy-MM-dd)
                SimpleDateFormat apiFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
                toDate = apiFormat.format(toCal.getTime());
                
                // Format for display (compact format)
                SimpleDateFormat displayFormat = new SimpleDateFormat("dd/MM", Locale.getDefault());
                String fromDisplay = displayFormat.format(fromCal.getTime());
                String toDisplay = displayFormat.format(toCal.getTime());
                
                // Add year if different years
                SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
                String fromYear = yearFormat.format(fromCal.getTime());
                String toYear = yearFormat.format(toCal.getTime());
                
                String dateRangeText;
                if (fromYear.equals(toYear)) {
                    // Same year: "01/04 - 30/04"
                    dateRangeText = fromDisplay + " - " + toDisplay;
                } else {
                    // Different years: "01/04/24 - 30/04/25"
                    SimpleDateFormat shortYearFormat = new SimpleDateFormat("dd/MM/yy", Locale.getDefault());
                    String fromDisplayWithYear = shortYearFormat.format(fromCal.getTime());
                    String toDisplayWithYear = shortYearFormat.format(toCal.getTime());
                    dateRangeText = fromDisplayWithYear + " - " + toDisplayWithYear;
                }
                
                // Update filter button text
                updateFilterButtonText(dateRangeText);
                
                // Apply filter
                applyDateFilter();
            },
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        );
        
        // Set minimum date to fromDate
        toDatePicker.getDatePicker().setMinDate(fromCal.getTimeInMillis());
        toDatePicker.setTitle("Select To Date");
        toDatePicker.show();
    }
    
    // ✅ NEW METHOD: Update filter button text with smart formatting
    private void updateFilterButtonText(String dateRangeText) {
        if (filterButtonText != null) {
            // Smart text formatting for better display
            String displayText;
            
            if (dateRangeText.length() > 15) {
                // For long date ranges, use compact format
                String[] dates = dateRangeText.split(" - ");
                if (dates.length == 2) {
                    // Convert "01/04/2025 - 30/04/2025" to "01/04\n30/04"
                    String fromShort = dates[0].substring(0, 5); // Get "01/04"
                    String toShort = dates[1].substring(0, 5);   // Get "30/04"
                    displayText = fromShort + "\n" + toShort;
                } else {
                    displayText = dateRangeText;
                }
            } else {
                displayText = dateRangeText;
            }
            
            filterButtonText.setText(displayText);
            filterButtonText.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
            filterButtonText.setTextSize(12); // Smaller text when filter is active
            
            Log.d("TransactionActivity", "🎨 Filter button updated: " + displayText);
        }
    }
    
    // ✅ NEW METHOD: Apply date filter
    private void applyDateFilter() {
        Log.d("TransactionActivity", "🗓️ Applying date filter: " + fromDate + " to " + toDate);
        
        // Reset to first page and reload data
        currentPage = 1;
        refreshTransactions();
    }
    
    // ✅ NEW METHOD: Clear date filter
    private void clearDateFilter() {
        fromDate = "";
        toDate = "";
        
        if (filterButtonText != null) {
            filterButtonText.setText("Filter");
            filterButtonText.setTextColor(getResources().getColor(android.R.color.darker_gray));
            filterButtonText.setTextSize(14); // Reset to normal size
        }
        
        Log.d("TransactionActivity", "🗓️ Date filter cleared");
        
        // Reload data without date filter
        currentPage = 1;
        refreshTransactions();
    }
    private void goToPage(int page) {
        if (page < 1 || page > totalPages || page == currentPage || isLoading) {
            return;
        }
        
        Log.d("TransactionActivity", "🔄 Navigating to page " + page);
        
        if (currentSearchQuery.isEmpty()) {
            // Load from API
            loadTransactions(page);
        } else {
            // Search mode - just update current page and refresh display
            currentPage = page;
            updatePaginationDisplay();
            displayCurrentPageData();
        }
    }

    // ✅ NEW METHOD: Display current page data for search results
    private void displayCurrentPageData() {
        if (currentSearchQuery.isEmpty()) {
            return; // This method is only for search results
        }
        
        int startIndex = (currentPage - 1) * itemsPerPage;
        int endIndex = Math.min(startIndex + itemsPerPage, filteredList.size());
        
        List<Transaction> pageData = new ArrayList<>();
        for (int i = startIndex; i < endIndex; i++) {
            pageData.add(filteredList.get(i));
        }
        
        // Update adapter with current page data
        adapter.updateData(pageData, startIndex); // Pass startIndex for numbering
        
        // Scroll to top
        recyclerView.scrollToPosition(0);
        
        Log.d("TransactionActivity", "📄 Displaying search results page " + currentPage + 
            " (items " + (startIndex + 1) + "-" + endIndex + " of " + filteredList.size() + ")");
    }

    private void refreshTransactions() {
        if (isRefreshing) return;
        
        isRefreshing = true;
        // ✅ CLEAR LOCAL CACHE on refresh
        transactionCache.clear();
        processedReferences.clear();
        
        // Clear search when refreshing
        searchEditText.setText("");
        currentSearchQuery = "";
        currentPage = 1; // ✅ Reset to page 1
        transactionList.clear();
        filteredList.clear();
        adapter.notifyDataSetChanged();
        loadTransactions(1);
    }

    private void performSearch() {
        String query = searchEditText.getText().toString().trim();
        currentSearchQuery = query;
        currentPage = 1; // ✅ Reset to page 1 when searching
        filterTransactions(query);
        
        // Hide keyboard
        searchEditText.clearFocus();
    }

    private void filterTransactions(String query) {
        filteredList.clear();
        
        if (query.isEmpty()) {
            // ✅ NO SEARCH: Show current API page data (maintain sorting from API)
            filteredList.addAll(transactionList);
            totalRecords = totalRecords; // Use API total
            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
            
            // ✅ PASTIKAN TIDAK PERLU SORT LAGI karena sudah sorted dari API response
            Log.d("TransactionActivity", "📋 FILTERED LIST ORDER (no search - maintaining API order):");
            for (int i = 0; i < Math.min(5, filteredList.size()); i++) {
                Transaction tx = filteredList.get(i);
                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
            }
        } else {
            // ✅ SEARCH MODE: Filter all available data
            for (Transaction transaction : transactionList) {
                if (transaction.referenceId.toLowerCase().contains(query.toLowerCase()) ||
                    transaction.amount.contains(query)) {
                    filteredList.add(transaction);
                }
            }
            
            // ✅ SORT SEARCH RESULTS by date (maintaining newest first)
            Collections.sort(filteredList, (t1, t2) -> {
                try {
                    Date date1 = parseCreatedAtDate(t1.createdAt);
                    Date date2 = parseCreatedAtDate(t2.createdAt);
                    if (date1 != null && date2 != null) {
                        return date2.compareTo(date1); // Newest first
                    }
                } catch (Exception e) {
                    // Fallback
                }
                return Integer.compare(t2.id, t1.id);
            });
            
            // ✅ SEARCH PAGINATION: Calculate pages for filtered results
            totalRecords = filteredList.size();
            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
            
            // ✅ Display current page of search results
            displayCurrentPageData();
            updatePaginationDisplay();
            return; // Early return for search
        }
        
        // ✅ For non-search, just update adapter normally
        adapter.updateData(filteredList, (currentPage - 1) * itemsPerPage);
        updatePaginationDisplay();
        
        // Scroll to top after filtering
        if (!filteredList.isEmpty()) {
            recyclerView.scrollToPosition(0);
        }
    }

    // ✅ NEW METHOD: Update pagination display
    private void updatePaginationDisplay() {
        // Update page info - remove total records display for cleaner look
        String pageText = "Halaman " + currentPage + " dari " + Math.max(1, totalPages);
        
        // Show/hide pagination controls based on data availability
        if (totalRecords > 0) {
            if (totalPages > 1) {
                paginationControls.setVisibility(View.VISIBLE);
                updatePageButtons();
                createPageNumbers();
            } else {
                paginationControls.setVisibility(View.GONE);
            }
        } else {
            paginationControls.setVisibility(View.GONE);
        }
        
        Log.d("TransactionActivity", "📊 Pagination updated: " + 
            "Page " + currentPage + "/" + totalPages + ", Total: " + totalRecords);
    }

    // ✅ NEW METHOD: Update pagination button states
    private void updatePageButtons() {
        // Enable/disable buttons based on current page
        btnFirstPage.setEnabled(currentPage > 1);
        btnPrevPage.setEnabled(currentPage > 1);
        btnNextPage.setEnabled(currentPage < totalPages);
        btnLastPage.setEnabled(currentPage < totalPages);
        
        // Update button opacity for visual feedback
        float enabledAlpha = 1.0f;
        float disabledAlpha = 0.3f;
        
        btnFirstPage.setAlpha(btnFirstPage.isEnabled() ? enabledAlpha : disabledAlpha);
        btnPrevPage.setAlpha(btnPrevPage.isEnabled() ? enabledAlpha : disabledAlpha);
        btnNextPage.setAlpha(btnNextPage.isEnabled() ? enabledAlpha : disabledAlpha);
        btnLastPage.setAlpha(btnLastPage.isEnabled() ? enabledAlpha : disabledAlpha);
    }

    // ✅ NEW METHOD: Create page number buttons
    private void createPageNumbers() {
        pageNumbersContainer.removeAllViews();
        
        // Calculate which page numbers to show (max 5 numbers)
        int maxPageButtons = 5;
        int startPage = Math.max(1, currentPage - 2);
        int endPage = Math.min(totalPages, startPage + maxPageButtons - 1);
        
        // Adjust start if we're near the end
        if (endPage - startPage < maxPageButtons - 1) {
            startPage = Math.max(1, endPage - maxPageButtons + 1);
        }
        
        // ✅ CONSISTENT BUTTON SIZE for all devices (more modern size)
        int buttonSize = (int) (44 * getResources().getDisplayMetrics().density); // 44dp (iOS standard)
        
        for (int i = startPage; i <= endPage; i++) {
            final int pageNumber = i;
            
            TextView pageButton = new TextView(this);
            pageButton.setText(String.valueOf(pageNumber));
            pageButton.setTextSize(16); // Slightly larger text
            pageButton.setClickable(true);
            pageButton.setFocusable(true);
            pageButton.setGravity(android.view.Gravity.CENTER);
            
            // ✅ USE STYLE HELPER
            boolean isActive = (pageNumber == currentPage);
            StyleHelper.applyPaginationButtonStyle(pageButton, this, isActive);
            
            pageButton.setOnClickListener(v -> goToPage(pageNumber));
            
            // ✅ IMPROVED: Better spacing like in the image
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                buttonSize, // Consistent width
                buttonSize  // Consistent height
            );
            params.setMargins(6, 0, 6, 0); // 6dp margin for better spacing
            pageButton.setLayoutParams(params);
            
            pageNumbersContainer.addView(pageButton);
        }
        
        Log.d("TransactionActivity", "🔢 Page buttons created: " + startPage + " to " + endPage + 
            " with size: " + buttonSize + "px");
    }

    private void loadTransactions(int pageToLoad) {
        isLoading = true;
        progressBar.setVisibility(View.VISIBLE);
        new FetchTransactionsTask(pageToLoad).execute();
    }

    private class FetchTransactionsTask extends AsyncTask<Void, Void, List<Transaction>> {
        private int pageToLoad;
        private boolean error = false;
        private int apiTotal = 0;

        FetchTransactionsTask(int page) {
            this.pageToLoad = page;
        }

        @Override
        protected List<Transaction> doInBackground(Void... voids) {
            List<Transaction> result = new ArrayList<>();
            try {
                // ✅ PAGINATION API CALL: Use page-based API call with date filter
                int apiPage = pageToLoad - 1; // API uses 0-based indexing
                
                String urlString = "https://be-edc.msvc.app/transactions?page=" + apiPage + 
                    "&limit=" + itemsPerPage + "&sortOrder=DESC&from_date=" + fromDate + "&to_date=" + toDate + "&location_id=0&merchant_id=0&tid=73001500&mid=71000026521&sortColumn=created_at";
                    
                Log.d("TransactionActivity", "🔍 Fetching transactions page " + pageToLoad + 
                    " (API page " + apiPage + ") with limit " + itemsPerPage + " - SORT: DESC by created_at" +
                    " - Date Filter: " + fromDate + " to " + toDate);
                
                URI uri = new URI(urlString);
                URL url = uri.toURL();
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("User-Agent", "BDKIPOCApp/1.0");
                conn.setConnectTimeout(15000);
                conn.setReadTimeout(15000);
                
                int responseCode = conn.getResponseCode();
                
                if (responseCode == 200) {
                    BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = in.readLine()) != null) {
                        response.append(line);
                    }
                    in.close();
                    
                    JSONObject jsonObject = new JSONObject(response.toString());
                    JSONObject results = jsonObject.getJSONObject("results");
                    apiTotal = results.getInt("total");
                    JSONArray data = results.getJSONArray("data");
                    
                    Log.d("TransactionActivity", "📊 API response: " + data.length() + 
                        " records, total: " + apiTotal);
                    
                    // ✅ STEP 1: Parse all transactions from API
                    List<Transaction> rawTransactions = new ArrayList<>();
                    for (int i = 0; i < data.length(); i++) {
                        JSONObject t = data.getJSONObject(i);
                        Transaction tx = new Transaction(
                                t.getInt("id"),
                                t.getString("type"),
                                t.getString("channel_category"),
                                t.getString("channel_code"),
                                t.getString("reference_id"),
                                t.getString("amount"),
                                t.getString("cashflow"),
                                t.getString("status"),
                                t.getString("created_at"),
                                t.getString("merchant_name")
                        );
                        rawTransactions.add(tx);
                    }
                    
                    // ✅ STEP 2: Apply intelligent deduplication
                    result = applyAdvancedDeduplication(rawTransactions);
                    
                    Log.d("TransactionActivity", "✅ After advanced deduplication: " + result.size() + " unique transactions");
                    
                } else {
                    Log.e("TransactionActivity", "❌ HTTP Error: " + responseCode);
                    error = true;
                }
            } catch (IOException | JSONException | URISyntaxException e) {
                Log.e("TransactionActivity", "❌ Exception: " + e.getMessage(), e);
                error = true;
            }
            return result;
        }

        @Override
        protected void onPostExecute(List<Transaction> transactions) {
            isLoading = false;
            isRefreshing = false;
            progressBar.setVisibility(View.GONE);
            
            if (error) {
                Toast.makeText(TransactionActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
                updatePaginationDisplay();
                return;
            }
            
            // ✅ UPDATE PAGINATION DATA
            currentPage = pageToLoad;
            totalRecords = apiTotal;
            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
            
            // ✅ PERBAIKAN SORTING: Sort transactions SEBELUM menambahkan ke list
            Collections.sort(transactions, (t1, t2) -> {
                try {
                    Date date1 = parseCreatedAtDate(t1.createdAt);
                    Date date2 = parseCreatedAtDate(t2.createdAt);
                    
                    if (date1 != null && date2 != null) {
                        int comparison = date2.compareTo(date1); // Newest first
                        Log.d("TransactionActivity", "🔄 Sorting: " + t2.createdAt + " vs " + t1.createdAt + " = " + comparison);
                        return comparison;
                    }
                } catch (Exception e) {
                    Log.w("TransactionActivity", "Date comparison error: " + e.getMessage());
                }
                return Integer.compare(t2.id, t1.id); // Fallback by ID (higher ID = newer)
            });
            
            // ✅ UPDATE TRANSACTION LIST dengan data yang sudah tersortir
            transactionList.clear();
            transactionList.addAll(transactions);
            
            Log.d("TransactionActivity", "📋 Page " + currentPage + " loaded and sorted: " + 
                transactions.size() + " transactions. Total: " + totalRecords + "/" + totalPages + " pages");
            
            // ✅ VERIFIKASI SORTING ORDER
            Log.d("TransactionActivity", "📋 SORTED ORDER VERIFICATION:");
            for (int i = 0; i < Math.min(5, transactionList.size()); i++) {
                Transaction tx = transactionList.get(i);
                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
            }
            
            // Update filtered list based on current search
            filterTransactions(currentSearchQuery);
            
            // Scroll to top
            if (!filteredList.isEmpty()) {
                recyclerView.scrollToPosition(0);
            }
        }
    }

    /**
     * ✅ ENHANCED DATE PARSING: Handle multiple date formats from API
     */
    private Date parseCreatedAtDate(String rawDate) {
        if (rawDate == null || rawDate.isEmpty()) {
            return null;
        }
        
        // List of possible date formats from API (urutan dari yang paling spesifik)
        String[] possibleFormats = {
            "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", // ISO format with microseconds (6 digits)
            "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",    // ISO format with milliseconds (3 digits)
            "yyyy-MM-dd'T'HH:mm:ss'Z'",        // ISO format without milliseconds
            "yyyy-MM-dd HH:mm:ss.SSSSSS",      // Standard format with microseconds
            "yyyy-MM-dd HH:mm:ss.SSS",         // Standard format with milliseconds
            "yyyy-MM-dd HH:mm:ss"              // Standard format
        };
        
        for (String format : possibleFormats) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
                sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // Handle timezone properly
                Date parsed = sdf.parse(rawDate);
                Log.d("TransactionActivity", "✅ Date parsed successfully: " + rawDate + " -> " + parsed + " using format: " + format);
                return parsed;
            } catch (Exception e) {
                // Continue to next format
            }
        }
        
        // Manual parsing fallback for complex formats
        try {
            String cleanedDate = rawDate.replace("T", " ").replace("Z", "");
            
            // Remove microseconds/milliseconds if present
            if (cleanedDate.contains(".")) {
                cleanedDate = cleanedDate.substring(0, cleanedDate.indexOf("."));
            }
            
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
            Date parsed = sdf.parse(cleanedDate);
            Log.d("TransactionActivity", "✅ Date parsed with fallback: " + rawDate + " -> " + parsed);
            return parsed;
        } catch (Exception e) {
            Log.w("TransactionActivity", "❌ Could not parse date: " + rawDate + " - Error: " + e.getMessage());
            return null;
        }
    }

    /**
     * ✅ ADVANCED DEDUPLICATION: Enhanced algorithm with multiple strategies
     */
    private List<Transaction> applyAdvancedDeduplication(List<Transaction> rawTransactions) {
        Log.d("TransactionActivity", "🧠 Starting advanced deduplication...");
        Log.d("TransactionActivity", "📥 Input transactions order (first 5):");
        for (int i = 0; i < Math.min(5, rawTransactions.size()); i++) {
            Transaction tx = rawTransactions.get(i);
            Log.d("TransactionActivity", "   " + (i+1) + ". ID:" + tx.id + " Date:" + tx.createdAt + " Ref:" + tx.referenceId);
        }
        
        // Strategy 1: Group by reference_id
        Map<String, List<Transaction>> groupedByRef = new HashMap<>();
        
        for (Transaction tx : rawTransactions) {
            String refId = tx.referenceId;
            List<Transaction> list = groupedByRef.get(refId);
            if (list == null) {
                list = new ArrayList<>();
                groupedByRef.put(refId, list);
            }
            list.add(tx);

        }
        
        List<Transaction> deduplicatedList = new ArrayList<>();
        int duplicatesRemoved = 0;
        
        // Strategy 2: For each group, select the best representative
        for (Map.Entry<String, List<Transaction>> entry : groupedByRef.entrySet()) {
            String referenceId = entry.getKey();
            List<Transaction> group = entry.getValue();
            
            if (group.size() == 1) {
                // No duplicates for this reference
                deduplicatedList.add(group.get(0));
                Log.d("TransactionActivity", "✅ Unique transaction: " + referenceId);
            } else {
                // Multiple transactions with same reference_id - sort group by date first
                Collections.sort(group, (t1, t2) -> {
                    try {
                        Date date1 = parseCreatedAtDate(t1.createdAt);
                        Date date2 = parseCreatedAtDate(t2.createdAt);
                        if (date1 != null && date2 != null) {
                            return date2.compareTo(date1); // Newest first in group
                        }
                    } catch (Exception e) {
                        // Fallback to ID
                    }
                    return Integer.compare(t2.id, t1.id);
                });
                
                Transaction bestTransaction = selectBestTransactionAdvanced(group, referenceId);
                deduplicatedList.add(bestTransaction);
                duplicatesRemoved += (group.size() - 1);
                
                Log.d("TransactionActivity", "🔄 Deduplicated " + group.size() + " → 1 for ref: " + referenceId + 
                    " (kept ID: " + bestTransaction.id + ", status: " + bestTransaction.status + ", date: " + bestTransaction.createdAt + ")");
            }
        }
        
        Log.d("TransactionActivity", "✅ Advanced deduplication complete:");
        Log.d("TransactionActivity", "   📥 Input: " + rawTransactions.size() + " transactions");
        Log.d("TransactionActivity", "   📤 Output: " + deduplicatedList.size() + " unique transactions");
        Log.d("TransactionActivity", "   🗑️ Removed: " + duplicatesRemoved + " duplicates");
        
        return deduplicatedList;
    }

    /**
     * ✅ ENHANCED SELECTION: Advanced algorithm to pick the best transaction
     */
    private Transaction selectBestTransactionAdvanced(List<Transaction> duplicates, String referenceId) {
        Log.d("TransactionActivity", "🎯 Selecting best from " + duplicates.size() + " duplicates for: " + referenceId);
        
        Transaction bestTransaction = duplicates.get(0);
        int bestPriority = getStatusPriority(bestTransaction.status);
        Date bestDate = parseCreatedAtDate(bestTransaction.createdAt);
        
        // Detailed analysis of each candidate
        for (Transaction tx : duplicates) {
            int currentPriority = getStatusPriority(tx.status);
            Date currentDate = parseCreatedAtDate(tx.createdAt);
            
            Log.d("TransactionActivity", "   📊 Candidate: ID=" + tx.id + 
                ", Status=" + tx.status + " (priority=" + currentPriority + ")" +
                ", Created=" + tx.createdAt);
            
            boolean shouldSelect = false;
            String reason = "";
            
            // Rule 1: Higher status priority always wins
            if (currentPriority > bestPriority) {
                shouldSelect = true;
                reason = "better status (" + tx.status + " > " + bestTransaction.status + ")";
            }
            // Rule 2: Same priority, choose newer timestamp
            else if (currentPriority == bestPriority) {
                if (currentDate != null && bestDate != null && currentDate.after(bestDate)) {
                    shouldSelect = true;
                    reason = "newer timestamp";
                }
                // Rule 3: Same priority and time, choose higher ID
                else if ((currentDate == null && bestDate == null) || 
                         (currentDate != null && bestDate != null && currentDate.equals(bestDate))) {
                    if (tx.id > bestTransaction.id) {
                        shouldSelect = true;
                        reason = "higher ID";
                    }
                }
            }
            
            if (shouldSelect) {
                bestTransaction = tx;
                bestPriority = currentPriority;
                bestDate = currentDate;
                Log.d("TransactionActivity", "   ⭐ NEW BEST selected: " + reason);
            }
        }
        
        Log.d("TransactionActivity", "🏆 FINAL SELECTION: ID=" + bestTransaction.id + 
            ", Status=" + bestTransaction.status + ", Created=" + bestTransaction.createdAt);
        
        return bestTransaction;
    }

    /**
     * ✅ TIMESTAMP COMPARISON: Smart date comparison using enhanced parsing
     */
    private boolean isNewerTransaction(Transaction tx1, Transaction tx2) {
        try {
            Date date1 = parseCreatedAtDate(tx1.createdAt);
            Date date2 = parseCreatedAtDate(tx2.createdAt);
            
            if (date1 != null && date2 != null) {
                return date1.after(date2);
            }
        } catch (Exception e) {
            Log.w("TransactionActivity", "Date comparison error, falling back to ID comparison");
        }
        
        // Fallback: higher ID usually means newer
        return tx1.id > tx2.id;
    }

    /**
     * ✅ COMPARISON HELPER: Check if one transaction is better than another using enhanced parsing
     */
    private boolean isBetterTransaction(Transaction newTx, Transaction existingTx) {
        int newPriority = getStatusPriority(newTx.status);
        int existingPriority = getStatusPriority(existingTx.status);
        
        if (newPriority > existingPriority) {
            return true; // Better status
        } else if (newPriority == existingPriority) {
            return isNewerTransaction(newTx, existingTx); // Same status, check if newer
        }
        
        return false; // Existing transaction is better
    }

    /**
     * ✅ STATUS PRIORITY SYSTEM: Comprehensive status ranking
     */
    private int getStatusPriority(String status) {
        if (status == null) return 0;
        
        String statusLower = status.toLowerCase().trim();
        switch (statusLower) {
            // Tier 1: Confirmed payments (highest priority)
            case "paid":
            case "settlement":
            case "capture":
            case "success":
            case "completed":
                return 100;
                
            // Tier 2: Processing states
            case "pending":
            case "processing":
            case "authorized":
                return 50;
                
            // Tier 3: Initial states
            case "init":
            case "initialized":
            case "created":
                return 20;
                
            // Tier 4: Failed/cancelled states (lowest priority)
            case "failed":
            case "failure":
            case "error":
            case "declined":
            case "expire":
            case "expired":
            case "cancel":
            case "cancelled":
            case "timeout":
                return 10;
                
            // Tier 5: Unknown status
            default:
                Log.w("TransactionActivity", "Unknown status encountered: " + status);
                return 1;
        }
    }

    // ✅ REST OF THE CLASS: Existing methods remain the same
    @Override
    public void onPrintClick(Transaction transaction) {
        // Open ReceiptActivity with transaction data
        Intent intent = new Intent(this, ReceiptActivity.class);
        
        // Add calling activity information for proper back navigation
        intent.putExtra("calling_activity", "TransactionActivity");
        
        // Extract and send raw amount properly
        String rawAmount = extractRawAmount(transaction.amount);
        
        Log.d("TransactionActivity", "Opening receipt for transaction: " + transaction.referenceId + 
            ", channel: " + transaction.channelCode + ", original amount: '" + transaction.amount + "'");
        
        // Send transaction data to ReceiptActivity
        intent.putExtra("transaction_id", transaction.referenceId);
        intent.putExtra("reference_id", transaction.referenceId); // Nomor Transaksi
        intent.putExtra("transaction_amount", rawAmount); // Total transaksi
        intent.putExtra("gross_amount", rawAmount); // Consistent with transaction_amount
        intent.putExtra("created_at", transaction.createdAt); // Tanggal transaksi (will be formatted)
        intent.putExtra("transaction_date", formatDate(transaction.createdAt)); // Backup formatted date
        intent.putExtra("payment_method", getPaymentMethodName(transaction.channelCode, transaction.channelCategory));
        intent.putExtra("channel_code", transaction.channelCode); // Metode Pembayaran
        intent.putExtra("channel_category", transaction.channelCategory);
        intent.putExtra("card_type", transaction.channelCategory);
        intent.putExtra("merchant_name", transaction.merchantName);
        intent.putExtra("merchant_location", "Jakarta, Indonesia");
        
        // Add MID and TID (default values since not available in Transaction model)
        intent.putExtra("mid", "71000026521"); // MID
        intent.putExtra("tid", "73001500");   // TID
        
        // ✅ ENHANCED: Use improved acquirer determination
        String acquirer = getRealAcquirerForQris(transaction.referenceId, transaction.channelCode);
        intent.putExtra("acquirer", acquirer); // Jenis Kartu
        
        Log.d("TransactionActivity", "🎯 Determined acquirer: " + acquirer + " for channel: " + transaction.channelCode);
        
        startActivity(intent);
    }
    
    /**
     * ✅ ENHANCED: Dynamic acquirer determination instead of defaulting to GoPay
     */
    private String determineAcquirerFromChannelCode(String channelCode) {
        if (channelCode == null) return "unknown"; // ✅ CHANGED: unknown instead of gopay
        
        switch (channelCode.toLowerCase()) {
            case "qris":
                // ✅ IMPROVED: For QRIS, try to get real acquirer or return generic
                return "qris"; // Will be processed by receipt activity to find real acquirer
            case "bca":
                return "bca";
            case "mandiri":
                return "mandiri";
            case "bni":
                return "bni";
            case "bri":
                return "bri";
            case "permata":
                return "permata";
            case "cimb":
                return "cimb";
            case "danamon":
                return "danamon";
            case "bsi":
                return "bsi";
            case "debit":
                return "visa"; // Default for debit cards
            case "credit":
                return "mastercard"; // Default for credit cards
            default:
                return "unknown"; // ✅ CHANGED: unknown instead of gopay for unknown channels
        }
    }

    /**
     * ✅ NEW METHOD: Try to get real acquirer for QRIS transactions from current transaction data
     * This method can be enhanced to query webhook API for real acquirer
     */
    private String getRealAcquirerForQris(String referenceId, String channelCode) {
        // If not QRIS, return channel code
        if (!"QRIS".equalsIgnoreCase(channelCode)) {
            return determineAcquirerFromChannelCode(channelCode);
        }
        
        // For QRIS, we could implement real-time acquirer lookup here
        // For now, return "qris" and let ReceiptActivity handle the detection
        Log.d("TransactionActivity", "🔍 QRIS transaction detected, deferring acquirer detection to ReceiptActivity");
        return "qris";
    }

    private String extractRawAmount(String formattedAmount) {
        if (formattedAmount == null || formattedAmount.isEmpty()) {
            return "0";
        }
        
        String cleaned = formattedAmount
                .replace("Rp. ", "")
                .replace("Rp ", "")
                .replace("IDR ", "")
                .replace(" ", "")
                .trim();
        
        if (cleaned.contains(".")) {
            String[] parts = cleaned.split("\\.");
            if (parts.length == 2) {
                String beforeDot = parts[0];
                String afterDot = parts[1];
                
                if (afterDot.equals("00") || afterDot.equals("0")) {
                    cleaned = beforeDot;
                } else if (afterDot.length() == 3) {
                    cleaned = beforeDot + afterDot;
                } else {
                    if (beforeDot.length() <= 3 && afterDot.length() == 3) {
                        cleaned = beforeDot + afterDot;
                    } else {
                        cleaned = beforeDot;
                    }
                }
            } else if (parts.length > 2) {
                cleaned = String.join("", parts);
            }
        }
        
        cleaned = cleaned.replace(",", "");
        
        try {
            Long.parseLong(cleaned);
            return cleaned;
        } catch (NumberFormatException e) {
            Log.e("TransactionActivity", "Invalid amount: " + formattedAmount);
            return "0";
        }
    }

    private String getPaymentMethodName(String channelCode, String channelCategory) {
        if (channelCode == null) return "Unknown";
        
        switch (channelCode.toUpperCase()) {
            case "QRIS": return "QRIS";
            case "DEBIT": return "Kartu Debit";
            case "CREDIT": return "Kartu Kredit";
            case "BCA":
            case "MANDIRI":
            case "BNI":
            case "BRI": return "Kartu " + channelCode.toUpperCase();
            case "CASH": return "Tunai";
            case "EDC": return "EDC";
            default:
                if (channelCategory != null && !channelCategory.isEmpty()) {
                    return channelCategory.toUpperCase();
                }
                return channelCode.toUpperCase();
        }
    }

    private String formatDate(String rawDate) {
        try {
            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
            SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
            Date date = inputFormat.parse(rawDate);
            return outputFormat.format(date);
        } catch (Exception e) {
            return rawDate;
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            finish();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    // Transaction model class
    static class Transaction {
        int id;
        String type;
        String channelCategory;
        String channelCode;
        String referenceId;
        String amount;
        String cashflow;
        String status;
        String createdAt;
        String merchantName;

        Transaction(int id, String type, String channelCategory, String channelCode, String referenceId, String amount, String cashflow, String status, String createdAt, String merchantName) {
            this.id = id;
            this.type = type;
            this.channelCategory = channelCategory;
            this.channelCode = channelCode;
            this.referenceId = referenceId;
            this.amount = amount;
            this.cashflow = cashflow;
            this.status = status;
            this.createdAt = createdAt;
            this.merchantName = merchantName;
        }
    }
}