package com.example.sms_call_sync;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class SyncManager {
    private static final String TAG = "SyncManager";
    private Context context;
    private OkHttpClient client;
    private Gson gson;
    private ExecutorService executorService;
    // Changed dbHelper from private to protected or default if it needs to be accessed directly by subclasses
    // However, a getter is safer for external classes like SettingsActivity.
    private DatabaseHelper dbHelper; // Keep private, provide getter

    public SyncManager(Context context) {
        this.context = context;
        this.client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
        this.gson = new Gson();
        this.executorService = Executors.newFixedThreadPool(2); // For concurrent tasks
        this.dbHelper = new DatabaseHelper(context); // Initialize DatabaseHelper
    }

    // Public getter for dbHelper
    public DatabaseHelper getDbHelper() {
        return dbHelper;
    }

    public void addBlockedNumber(String number) {
        dbHelper.addBlockedNumber(number);
    }

    public void removeBlockedNumber(String number) {
        dbHelper.removeBlockedNumber(number);
    }

    public Set<String> getBlockedNumbers() {
        return dbHelper.getBlockedNumbers();
    }

    public void syncSms(String serverUrl, String phoneName) {
        executorService.execute(() -> {
            List<Map<String, String>> smsList = getSmsMessages();
            List<Map<String, String>> filteredSmsList = filterBlockedNumbers(smsList, "address");
            if (filteredSmsList.isEmpty()) {
                Log.d(TAG, "No SMS messages to sync after blocking.");
                return;
            }
            sendDataToServer(serverUrl + "/sync_sms.php", filteredSmsList, "SMS");
            
            // Send heartbeat after sync
            sendHeartbeat(serverUrl, phoneName, "active", filteredSmsList.size(), 0);
        });
    }

    public void syncCallHistory(String serverUrl, String phoneName) {
        executorService.execute(() -> {
            List<Map<String, String>> callList = getCallHistory();
            List<Map<String, String>> filteredCallList = filterBlockedNumbers(callList, "number");
            if (filteredCallList.isEmpty()) {
                Log.d(TAG, "No call history records to sync after blocking.");
                return;
            }
            sendDataToServer(serverUrl + "/sync_calls.php", filteredCallList, "Call History");
            
            // Send heartbeat after sync
            sendHeartbeat(serverUrl, phoneName, "active", 0, filteredCallList.size());
        });
    }
    
    public void updateAutoSyncStatus(String serverUrl, String phoneName, boolean enabled, int intervalSeconds) {
        executorService.execute(() -> {
            try {
                Map<String, Object> data = new HashMap<>();
                data.put("phone_name", phoneName);
                data.put("auto_sync_enabled", enabled);
                data.put("sync_interval_seconds", intervalSeconds);
                
                String json = gson.toJson(data);
                MediaType JSON = MediaType.parse("application/json; charset=utf-8");
                RequestBody body = RequestBody.create(json, JSON);
                Request request = new Request.Builder()
                        .url(serverUrl + "/auto_sync_status.php")
                        .post(body)
                        .build();

                try (Response response = client.newCall(request).execute()) {
                    if (response.isSuccessful()) {
                        Log.d(TAG, "Auto sync status updated successfully: " + response.body().string());
                    } else {
                        Log.e(TAG, "Failed to update auto sync status: " + response.code() + " " + response.message());
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Error updating auto sync status: " + e.getMessage());
                }
            } catch (Exception e) {
                Log.e(TAG, "Error in updateAutoSyncStatus: " + e.getMessage());
            }
        });
    }
    
    public void syncLocation(String serverUrl, String phoneName, double latitude, double longitude) {
        executorService.execute(() -> {
            try {
                Map<String, Object> locationData = new HashMap<>();
                locationData.put("phone_name", phoneName);
                locationData.put("latitude", latitude);
                locationData.put("longitude", longitude);
                locationData.put("timestamp", System.currentTimeMillis());
                
                String json = gson.toJson(locationData);
                MediaType JSON = MediaType.parse("application/json; charset=utf-8");
                RequestBody body = RequestBody.create(json, JSON);
                Request request = new Request.Builder()
                        .url(serverUrl + "/update_location.php")
                        .post(body)
                        .build();

                try (Response response = client.newCall(request).execute()) {
                    if (response.isSuccessful()) {
                        Log.d(TAG, "Location sync successful: " + response.body().string());
                    } else {
                        Log.e(TAG, "Location sync failed: " + response.code() + " " + response.message());
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Error sending location data to server: " + e.getMessage());
                }
            } catch (Exception e) {
                Log.e(TAG, "Error in syncLocation: " + e.getMessage());
            }
        });
    }
    
    private void sendHeartbeat(String serverUrl, String phoneName, String status, int smsCount, int callsCount) {
        try {
            Map<String, Object> data = new HashMap<>();
            data.put("phone_name", phoneName);
            data.put("sync_status", status);
            data.put("sms_count", smsCount);
            data.put("calls_count", callsCount);
            
            String json = gson.toJson(data);
            MediaType JSON = MediaType.parse("application/json; charset=utf-8");
            RequestBody body = RequestBody.create(json, JSON);
            Request request = new Request.Builder()
                    .url(serverUrl + "/sync_heartbeat.php")
                    .post(body)
                    .build();

            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    Log.d(TAG, "Heartbeat sent successfully");
                } else {
                    Log.e(TAG, "Failed to send heartbeat: " + response.code());
                }
            } catch (IOException e) {
                Log.e(TAG, "Error sending heartbeat: " + e.getMessage());
            }
        } catch (Exception e) {
            Log.e(TAG, "Error in sendHeartbeat: " + e.getMessage());
        }
    }

    private List<Map<String, String>> getSmsMessages() {
        List<Map<String, String>> smsList = new ArrayList<>();
        Uri uri = Uri.parse("content://sms/");
        String[] projection = new String[]{"_id", "address", "body", "date", "type"};
        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, "date DESC");

        if (cursor != null) {
            try {
                int idColumn = cursor.getColumnIndex("_id");
                int addressColumn = cursor.getColumnIndex("address");
                int bodyColumn = cursor.getColumnIndex("body");
                int dateColumn = cursor.getColumnIndex("date");
                int typeColumn = cursor.getColumnIndex("type");

                while (cursor.moveToNext()) {
                    Map<String, String> sms = new HashMap<>();
                    sms.put("id", cursor.getString(idColumn));
                    sms.put("address", cursor.getString(addressColumn));
                    sms.put("body", cursor.getString(bodyColumn));
                    sms.put("date", cursor.getString(dateColumn));
                    sms.put("type", cursor.getString(typeColumn));
                    sms.put("phone_name", dbHelper.getPhoneName()); // Assuming dbHelper can provide phone name
                    smsList.add(sms);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error reading SMS: " + e.getMessage());
            } finally {
                cursor.close();
            }
        }
        return smsList;
    }

    private List<Map<String, String>> getCallHistory() {
        List<Map<String, String>> callList = new ArrayList<>();
        Uri uri = CallLog.Calls.CONTENT_URI;
        String[] projection = new String[]{
                CallLog.Calls._ID,
                CallLog.Calls.NUMBER,
                CallLog.Calls.DATE,
                CallLog.Calls.DURATION,
                CallLog.Calls.TYPE
        };
        String sortOrder = CallLog.Calls.DATE + " DESC";
        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, sortOrder);

        if (cursor != null) {
            try {
                int idColumn = cursor.getColumnIndex(CallLog.Calls._ID);
                int numberColumn = cursor.getColumnIndex(CallLog.Calls.NUMBER);
                int dateColumn = cursor.getColumnIndex(CallLog.Calls.DATE);
                int durationColumn = cursor.getColumnIndex(CallLog.Calls.DURATION);
                int typeColumn = cursor.getColumnIndex(CallLog.Calls.TYPE);

                while (cursor.moveToNext()) {
                    Map<String, String> call = new HashMap<>();
                    call.put("id", cursor.getString(idColumn));
                    call.put("number", cursor.getString(numberColumn));
                    call.put("date", cursor.getString(dateColumn));
                    call.put("duration", cursor.getString(durationColumn));
                    call.put("type", cursor.getString(typeColumn));
                    call.put("phone_name", dbHelper.getPhoneName()); // Assuming dbHelper can provide phone name
                    callList.add(call);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error reading Call Log: " + e.getMessage());
            } finally {
                cursor.close();
            }
        }
        return callList;
    }

    private List<Map<String, String>> filterBlockedNumbers(List<Map<String, String>> dataList, String numberKey) {
        Set<String> blockedNumbers = dbHelper.getBlockedNumbers();
        List<Map<String, String>> filteredList = new ArrayList<>();
        for (Map<String, String> item : dataList) {
            String number = item.get(numberKey);
            if (number != null && !blockedNumbers.contains(number)) {
                filteredList.add(item);
            } else if (number == null) {
                // Include items with no number (e.g., some system messages)
                filteredList.add(item);
            }
        }
        return filteredList;
    }


    private void sendDataToServer(String url, List<Map<String, String>> data, String dataType) {
        if (data.isEmpty()) {
            Log.d(TAG, "No " + dataType + " data to send.");
            return;
        }

        String json = gson.toJson(data);
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(json, JSON);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                Log.d(TAG, dataType + " sync successful: " + response.body().string());
            } else {
                Log.e(TAG, dataType + " sync failed: " + response.code() + " " + response.message() + " " + response.body().string());
            }
        } catch (IOException e) {
            Log.e(TAG, "Error sending " + dataType + " data to server: " + e.getMessage());
        }
    }
}
