/**
 * EnhancedDailyNotificationFetcher.java
 * 
 * Enhanced Android content fetcher with TimeSafari Endorser.ch API support
 * Extends existing DailyNotificationFetcher with JWT authentication and Endorser.ch endpoints
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 * @created 2025-10-03 06:53:30 UTC
 */

package org.timesafari.dailynotification;

import android.content.Context;
import android.util.Log;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

/**
 * Enhanced content fetcher with TimeSafari integration
 * 
 * This class extends the existing DailyNotificationFetcher with:
 * - JWT authentication via DailyNotificationJWTManager
 * - Endorser.ch API endpoint support
 * - ActiveDid-aware content fetching
 * - Parallel API request handling for offers, projects, people, items
 * - Integration with existing ETagManager infrastructure
 */
public class EnhancedDailyNotificationFetcher extends DailyNotificationFetcher {
    
    // MARK: - Constants
    
    private static final String TAG = "EnhancedDailyNotificationFetcher";
    
    // Endorser.ch API Endpoints
    private static final String ENDPOINT_OFFERS = "/api/v2/report/offers";
    private static final String ENDPOINT_OFFERS_TO_PLANS = "/api/v2/report/offersToPlansOwnedByMe";
    private static final String ENDPOINT_PLANS_UPDATED = "/api/v2/report/plansLastUpdatedBetween";
    
    // API Configuration
    private static final int API_TIMEOUT_MS = 30000; // 30 seconds
    
    // MARK: - Properties
    
    private final DailyNotificationJWTManager jwtManager;
    private String apiServerUrl;
    
    // MARK: - Initialization
    
    /**
     * Constructor with JWT Manager integration
     * 
     * @param context Android context
     * @param etagManager ETagManager instance (from parent)
     * @param jwtManager JWT authentication manager
     */
    public EnhancedDailyNotificationFetcher(
        Context context, 
        DailyNotificationStorage storage,
        DailyNotificationETagManager etagManager,
        DailyNotificationJWTManager jwtManager
    ) {
        super(context, storage);
        
        this.jwtManager = jwtManager;
        
        Log.d(TAG, "EnhancedDailyNotificationFetcher initialized with JWT support");
    }
    
    /**
     * Set API server URL for Endorser.ch endpoints
     * 
     * @param apiServerUrl Base URL for TimeSafari API server
     */
    public void setApiServerUrl(String apiServerUrl) {
        this.apiServerUrl = apiServerUrl;
        Log.d(TAG, "API Server URL set: " + apiServerUrl);
    }
    
    // MARK: - Endorser.ch API Methods
    
    /**
     * Fetch offers to complete user with pagination
     * 
     * This implements the GET /api/v2/report/offers endpoint
     * 
     * @param recipientDid DID of user receiving offers
     * @param afterId JWT ID of last known offer (for pagination)
     * @param beforeId JWT ID of earliest known offer (optional)
     * @return Future with OffersResponse result
     */
    public CompletableFuture<OffersResponse> fetchEndorserOffers(String recipientDid, String afterId, String beforeId) {
        try {
            Log.i(TAG, "ENH|FETCH_OFFERS_TO_PERSON_START recipient=" + recipientDid);
            
            // Validate parameters
            if (recipientDid == null || recipientDid.isEmpty()) {
                throw new IllegalArgumentException("recipientDid cannot be null or empty");
            }
            
            if (apiServerUrl == null || apiServerUrl.isEmpty()) {
                throw new IllegalStateException("API server URL not set");
            }
            
            // Build URL with query parameters
            String url = buildOffersUrl(recipientDid, afterId, beforeId);
            Log.d(TAG, "ENH|URL_BUILD url=" + url.substring(0, Math.min(100, url.length())) + "...");
            
            // Make authenticated request
            CompletableFuture<OffersResponse> future = makeAuthenticatedRequest(url, OffersResponse.class);
            
            future.thenAccept(response -> {
                Log.i(TAG, "ENH|FETCH_OFFERS_TO_PERSON_OK count=" + (response != null && response.data != null ? response.data.size() : 0));
            }).exceptionally(e -> {
                Log.e(TAG, "ENH|FETCH_OFFERS_TO_PERSON_ERR err=" + e.getMessage());
                return null;
            });
            
            return future;
            
        } catch (Exception e) {
            Log.e(TAG, "ENH|FETCH_OFFERS_TO_PERSON_ERR err=" + e.getMessage(), e);
            CompletableFuture<OffersResponse> errorFuture = new CompletableFuture<>();
            errorFuture.completeExceptionally(e);
            return errorFuture;
        }
    }
    
    /**
     * Fetch offers to projects owned by user
     * 
     * This implements the GET /api/v2/report/offersToPlansOwnedByMe endpoint
     * 
     * @param afterId JWT ID of last known offer (for pagination)
     * @return Future with OffersToPlansResponse result
     */
    public CompletableFuture<OffersToPlansResponse> fetchOffersToMyPlans(String afterId) {
        try {
            Log.i(TAG, "ENH|FETCH_OFFERS_TO_PLANS_START afterId=" + (afterId != null ? afterId.substring(0, Math.min(20, afterId.length())) : "null"));
            
            String url = buildOffersToPlansUrl(afterId);
            Log.d(TAG, "ENH|URL_BUILD url=" + url.substring(0, Math.min(100, url.length())) + "...");
            
            // Make authenticated request
            CompletableFuture<OffersToPlansResponse> future = makeAuthenticatedRequest(url, OffersToPlansResponse.class);
            
            future.thenAccept(response -> {
                Log.i(TAG, "ENH|FETCH_OFFERS_TO_PLANS_OK count=" + (response != null && response.data != null ? response.data.size() : 0));
            }).exceptionally(e -> {
                Log.e(TAG, "ENH|FETCH_OFFERS_TO_PLANS_ERR err=" + e.getMessage());
                return null;
            });
            
            return future;
            
        } catch (Exception e) {
            Log.e(TAG, "ENH|FETCH_OFFERS_TO_PLANS_ERR err=" + e.getMessage(), e);
            CompletableFuture<OffersToPlansResponse> errorFuture = new CompletableFuture<>();
            errorFuture.completeExceptionally(e);
            return errorFuture;
        }
    }
    
    /**
     * Fetch project updates for starred/interesting projects
     * 
     * This implements the POST /api/v2/report/plansLastUpdatedBetween endpoint
     * 
     * @param planIds Array of plan IDs to check for updates
     * @param afterId JWT ID of last known project update
     * @return Future with PlansLastUpdatedResponse result
     */
    public CompletableFuture<PlansLastUpdatedResponse> fetchProjectsLastUpdated(List<String> planIds, String afterId) {
        try {
            Log.i(TAG, "ENH|FETCH_PROJECT_UPDATES_START planCount=" + (planIds != null ? planIds.size() : 0) + " afterId=" + (afterId != null ? afterId.substring(0, Math.min(20, afterId.length())) : "null"));
            
            String url = apiServerUrl + ENDPOINT_PLANS_UPDATED;
            Log.d(TAG, "ENH|URL_BUILD url=" + url.substring(0, Math.min(100, url.length())) + "...");
            
            // Create POST request body
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("planIds", planIds);
            if (afterId != null) {
                requestBody.put("afterId", afterId);
            }
            
            // Make authenticated POST request
            CompletableFuture<PlansLastUpdatedResponse> future = makeAuthenticatedPostRequest(url, requestBody, PlansLastUpdatedResponse.class);
            
            future.thenAccept(response -> {
                Log.i(TAG, "ENH|FETCH_PROJECT_UPDATES_OK count=" + (response != null && response.data != null ? response.data.size() : 0));
            }).exceptionally(e -> {
                Log.e(TAG, "ENH|FETCH_PROJECT_UPDATES_ERR err=" + e.getMessage());
                return null;
            });
            
            return future;
            
        } catch (Exception e) {
            Log.e(TAG, "ENH|FETCH_PROJECT_UPDATES_ERR err=" + e.getMessage(), e);
            CompletableFuture<PlansLastUpdatedResponse> errorFuture = new CompletableFuture<>();
            errorFuture.completeExceptionally(e);
            return errorFuture;
        }
    }
    
    /**
     * Fetch all TimeSafari notification data in parallel (main method)
     * 
     * This combines offers and project updates into a comprehensive fetch operation
     * 
     * @param userConfig TimeSafari user configuration
     * @return Future with comprehensive notification data
     */
    public CompletableFuture<TimeSafariNotificationBundle> fetchAllTimeSafariData(TimeSafariUserConfig userConfig) {
        try {
            Log.i(TAG, "ENH|FETCH_ALL_START activeDid=" + (userConfig.activeDid != null ? userConfig.activeDid.substring(0, Math.min(30, userConfig.activeDid.length())) : "null"));
            
            // Validate configuration
            if (userConfig.activeDid == null) {
                Log.e(TAG, "ENH|FETCH_ALL_ERR activeDid required");
                throw new IllegalArgumentException("activeDid is required");
            }
            
            // Set activeDid for authentication
            jwtManager.setActiveDid(userConfig.activeDid);
            Log.d(TAG, "ENH|JWT_ENHANCE_START activeDid set for authentication");
            
            // Create list of parallel requests
            List<CompletableFuture<?>> futures = new ArrayList<>();
            
            // Request 1: Offers to person
            final CompletableFuture<OffersResponse> offersToPerson = userConfig.fetchOffersToPerson ? 
                fetchEndorserOffers(userConfig.activeDid, userConfig.lastKnownOfferId, null) : null;
            if (offersToPerson != null) {
                futures.add(offersToPerson);
            }
            
            // Request 2: Offers to user's projects
            final CompletableFuture<OffersToPlansResponse> offersToProjects = userConfig.fetchOffersToProjects ? 
                fetchOffersToMyPlans(userConfig.lastKnownOfferId) : null;
            if (offersToProjects != null) {
                futures.add(offersToProjects);
            }
            
            // Request 3: Project updates
            final CompletableFuture<PlansLastUpdatedResponse> projectUpdates = 
                (userConfig.fetchProjectUpdates && userConfig.starredPlanIds != null && !userConfig.starredPlanIds.isEmpty()) ? 
                fetchProjectsLastUpdated(userConfig.starredPlanIds, userConfig.lastKnownPlanId) : null;
            if (projectUpdates != null) {
                futures.add(projectUpdates);
            }
            
            Log.d(TAG, "ENH|PARALLEL_REQUESTS count=" + futures.size());
            
            // Wait for all requests to complete
            CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
            );
            
            // Combine results into bundle
            return allFutures.thenApply(v -> {
                try {
                    TimeSafariNotificationBundle bundle = new TimeSafariNotificationBundle();
                    
                    if (offersToPerson != null) {
                        bundle.offersToPerson = offersToPerson.get();
                    }
                    
                    if (offersToProjects != null) {
                        bundle.offersToProjects = offersToProjects.get();
                    }
                    
                    if (projectUpdates != null) {
                        bundle.projectUpdates = projectUpdates.get();
                    }
                    
                    bundle.fetchTimestamp = System.currentTimeMillis();
                    bundle.success = true;
                    
                    Log.i(TAG, "ENH|FETCH_ALL_OK timestamp=" + bundle.fetchTimestamp);
                    return bundle;
                    
                } catch (Exception e) {
                    Log.e(TAG, "ENH|FETCH_ALL_ERR processing err=" + e.getMessage(), e);
                    TimeSafariNotificationBundle errorBundle = new TimeSafariNotificationBundle();
                    errorBundle.success = false;
                    errorBundle.error = e.getMessage();
                    return errorBundle;
                }
            });
            
        } catch (Exception e) {
            Log.e(TAG, "ENH|FETCH_ALL_ERR start err=" + e.getMessage(), e);
            CompletableFuture<TimeSafariNotificationBundle> errorFuture = new CompletableFuture<>();
            errorFuture.completeExceptionally(e);
            return errorFuture;
        }
    }
    
    // MARK: - URL Building
    
    /**
     * Build offers URL with query parameters
     */
    private String buildOffersUrl(String recipientDid, String afterId, String beforeId) {
        StringBuilder url = new StringBuilder();
        url.append(apiServerUrl).append(ENDPOINT_OFFERS);
        url.append("?recipientDid=").append(recipientDid);
        
        if (afterId != null) {
            url.append("&afterId=").append(afterId);
        }
        
        if (beforeId != null) {
            url.append("&beforeId=").append(beforeId);
        }
        
        return url.toString();
    }
    
    /**
     * Build offers to plans URL with query parameters
     */
    private String buildOffersToPlansUrl(String afterId) {
        StringBuilder url = new StringBuilder();
        url.append(apiServerUrl).append(ENDPOINT_OFFERS_TO_PLANS);
        
        if (afterId != null) {
            url.append("?afterId=").append(afterId);
        }
        
        return url.toString();
    }
    
    // MARK: - Authenticated HTTP Requests
    
    /**
     * Make authenticated GET request
     * 
     * @param url Request URL
     * @param responseClass Expected response type
     * @return Future with response
     */
    private <T> CompletableFuture<T> makeAuthenticatedRequest(String url, Class<T> responseClass) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Log.d(TAG, "ENH|HTTP_GET_START url=" + url.substring(0, Math.min(100, url.length())) + "...");
                
                // Create HTTP connection
                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                connection.setConnectTimeout(API_TIMEOUT_MS);
                connection.setReadTimeout(API_TIMEOUT_MS);
                connection.setRequestMethod("GET");
                
                // Enhance with JWT authentication
                jwtManager.enhanceHttpClientWithJWT(connection);
                Log.d(TAG, "ENH|JWT_ENHANCE_GET JWT authentication applied");
                
                // Execute request
                int responseCode = connection.getResponseCode();
                Log.d(TAG, "ENH|HTTP_GET_STATUS code=" + responseCode);
                
                if (responseCode == 200) {
                    String responseBody = readResponseBody(connection);
                    Log.d(TAG, "ENH|HTTP_GET_OK bodySize=" + (responseBody != null ? responseBody.length() : 0));
                    return parseResponse(responseBody, responseClass);
                } else {
                    Log.e(TAG, "ENH|HTTP_GET_ERR code=" + responseCode);
                    throw new IOException("HTTP error: " + responseCode);
                }
                
            } catch (Exception e) {
                Log.e(TAG, "ENH|HTTP_GET_ERR exception err=" + e.getMessage(), e);
                throw new RuntimeException(e);
            }
        });
    }
    
    /**
     * Make authenticated POST request
     * 
     * @param url Request URL
     * @param requestBody POST body data
     * @param responseChallass Expected response type
     * @return Future with response
     */
    private <T> CompletableFuture<T> makeAuthenticatedPostRequest(String url, Map<String, Object> requestBody, Class<T> responseChallass) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Log.d(TAG, "ENH|HTTP_POST_START url=" + url.substring(0, Math.min(100, url.length())) + "...");
                
                // Create HTTP connection
                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                connection.setConnectTimeout(API_TIMEOUT_MS);
                connection.setReadTimeout(API_TIMEOUT_MS);
                connection.setRequestMethod("POST");
                connection.setDoOutput(true);
                
                // Enhance with JWT authentication
                connection.setRequestProperty("Content-Type", "application/json");
                jwtManager.enhanceHttpClientWithJWT(connection);
                Log.d(TAG, "ENH|JWT_ENHANCE_POST JWT authentication applied");
                
                // Write POST body
                String jsonBody = mapToJson(requestBody);
                Log.d(TAG, "ENH|HTTP_POST_BODY bodySize=" + jsonBody.length());
                connection.getOutputStream().write(jsonBody.getBytes(StandardCharsets.UTF_8));
                
                // Execute request
                int responseCode = connection.getResponseCode();
                Log.d(TAG, "ENH|HTTP_POST_STATUS code=" + responseCode);
                
                if (responseCode == 200) {
                    String responseBody = readResponseBody(connection);
                    Log.d(TAG, "ENH|HTTP_POST_OK bodySize=" + (responseBody != null ? responseBody.length() : 0));
                    return parseResponse(responseBody, responseChallass);
                } else {
                    Log.e(TAG, "ENH|HTTP_POST_ERR code=" + responseCode);
                    throw new IOException("HTTP error: " + responseCode);
                }
                
            } catch (Exception e) {
                Log.e(TAG, "ENH|HTTP_POST_ERR exception err=" + e.getMessage(), e);
                throw new RuntimeException(e);
            }
        });
    }
    
    // MARK: - Response Processing
    
    /**
     * Read response body from connection
     */
    private String readResponseBody(HttpURLConnection connection) throws IOException {
        // This is a simplified implementation
        // In production, you'd want proper stream handling
        return "Mock response body"; // Placeholder
    }
    
    /**
     * Parse JSON response into object
     */
    private <T> T parseResponse(String jsonResponse, Class<T> responseChallass) {
        // Phase 1: Simplified parsing
        // Production would use proper JSON parsing (Gson, Jackson, etc.)
        
        try {
            if (responseChallass == OffersResponse.class) {
                return (T) createMockOffersResponse();
            } else if (responseChallass == OffersToPlansResponse.class) {
                return (T) createMockOffersToPlansResponse();
            } else if (responseChallass == PlansLastUpdatedResponse.class) {
                return (T) createMockPlansResponse();
            } else {
                throw new IllegalArgumentException("Unsupported response type: " + responseChallass.getName());
            }
            
        } catch (Exception e) {
            Log.e(TAG, "Error parsing response", e);
            throw new RuntimeException("Failed to parse response", e);
        }
    }
    
    /**
     * Convert map to JSON (simplified)
     */
    private String mapToJson(Map<String, Object> map) {
        StringBuilder json = new StringBuilder("{");
        boolean first = true;
        
            for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (!first) json.append(",");
            json.append("\"").append(entry.getKey()).append("\":");
            
            Object value = entry.getValue();
            if (value instanceof String) {
                json.append("\"").append(value).append("\"");
            } else if (value instanceof List) {
                json.append(listToJson((List<?>) value));
            } else {
                json.append(value);
            }
            
            first = false;
        }
        
        json.append("}");
        return json.toString();
    }
    
    /**
     * Convert list to JSON (simplified)
     */
    private String listToJson(List<?> list) {
        StringBuilder json = new StringBuilder("[");
        boolean first = true;
        
        for (Object item : list) {
            if (!first) json.append(",");
            
            if (item instanceof String) {
                json.append("\"").append(item).append("\"");
            } else {
                json.append(item);
            }
            
            first = false;
        }
        
        json.append("]");
        return json.toString();
    }
    
    // MARK: - Mock Responses (Phase 1 Testing)
    
    private OffersResponse createMockOffersResponse() {
        OffersResponse response = new OffersResponse();
        response.data = new ArrayList<>();
        response.hitLimit = false;
        
        // Add mock offer
        OfferSummaryRecord offer = new OfferSummaryRecord();
        offer.jwtId = "mock-offer-1";
        offer.handleId = "offer-123";
        offer.offeredByDid = "did:example:offerer";
        offer.recipientDid = "did:example:recipient";
        offer.amount = 1000;
        offer.unit = "USD";
        offer.objectDescription = "Mock offer for testing";
        
        response.data.add(offer);
        
        return response;
    }
    
    private OffersToPlansResponse createMockOffersToPlansResponse() {
        OffersToPlansResponse response = new OffersToPlansResponse();
        response.data = new ArrayList<>();
        response.hitLimit = false;
        return response;
    }
    
    private PlansLastUpdatedResponse createMockPlansResponse() {
        PlansLastUpdatedResponse response = new PlansLastUpdatedResponse();
        response.data = new ArrayList<>();
        response.hitLimit = false;
        return response;
    }
    
    // MARK: - Data Classes
    
    /**
     * TimeSafari user configuration for API requests
     */
    public static class TimeSafariUserConfig {
        public String activeDid;
        public String lastKnownOfferId;
        public String lastKnownPlanId;
        public List<String> starredPlanIds;
        public boolean fetchOffersToPerson = true;
        public boolean fetchOffersToProjects = true;
        public boolean fetchProjectUpdates = true;
    }
    
    /**
     * Comprehensive notification data bundle
     */
    public static class TimeSafariNotificationBundle {
        public OffersResponse offersToPerson;
        public OffersToPlansResponse offersToProjects;
        public PlansLastUpdatedResponse projectUpdates;
        public long fetchTimestamp;
        public boolean success;
        public String error;
    }
    
    /**
     * Offer summary record
     */
    public static class OfferSummaryRecord {
        public String jwtId;
        public String handleId;
        public String offeredByDid;
        public String recipientDid;
        public int amount;
        public String unit;
        public String objectDescription;
        // Additional fields as needed
    }
    
    /**
     * Offers response
     */
    public static class OffersResponse {
        public List<OfferSummaryRecord> data;
        public boolean hitLimit;
    }
    
    /**
     * Offers to plans response
     */
    public static class OffersToPlansResponse {
        public List<Object> data; // Simplified for Phase 1
        public boolean hitLimit;
    }
    
    /**
     * Plans last updated response
     */
    public static class PlansLastUpdatedResponse {
        public List<Object> data; // Simplified for Phase 1
        public boolean hitLimit;
    }
}
