<?php
/**
 * Google Calendar Service
 * 
 * Handles OAuth authentication, calendar operations, and two-way sync
 * with Google Calendar API.
 *
 * @package Freelance_Studio
 */

if (!defined('ABSPATH')) {
    exit;
}

class FS_Google_Calendar_Service {

    /**
     * Singleton instance
     */
    private static $instance = null;

    /**
     * Google Calendar API base URL
     */
    const API_BASE = 'https://www.googleapis.com/calendar/v3';
    const OAUTH_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
    const OAUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token';

    /**
     * Calendar scopes needed
     */
    const SCOPES = 'https://www.googleapis.com/auth/calendar';

    /**
     * Get singleton instance
     */
    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor
     */
    private function __construct() {
        // Register hooks for sync triggers
        add_action('fs_project_saved', array($this, 'sync_project_dates'), 10, 2);
        add_action('fs_milestone_saved', array($this, 'sync_milestone'), 10, 2);
        add_action('fs_task_saved', array($this, 'sync_task'), 10, 2);
        add_action('fs_invoice_saved', array($this, 'sync_invoice'), 10, 2);
        add_action('fs_contract_saved', array($this, 'sync_contract'), 10, 2);
        add_action('fs_proposal_saved', array($this, 'sync_proposal'), 10, 2);
        
        // Webhook handler for incoming changes from Google
        add_action('rest_api_init', array($this, 'register_webhook_endpoint'));
        
        // Two-way sync cron hooks
        add_filter('cron_schedules', array(__CLASS__, 'add_cron_schedules'));
        add_action('fs_gcal_inbound_sync', array(__CLASS__, 'process_inbound_sync'));
        add_action('admin_init', array(__CLASS__, 'schedule_inbound_sync'));
    }

    /**
     * Check if Google Calendar is connected
     */
    public static function is_connected() {
        $access_token = FS_Security_Service::get_encrypted_option('fs_gcal_access_token', '');
        $refresh_token = FS_Security_Service::get_encrypted_option('fs_gcal_refresh_token', '');
        return !empty($access_token) && !empty($refresh_token);
    }

    /**
     * Check if integration is enabled
     */
    public static function is_enabled() {
        return get_option('fs_gcal_enabled', '0') === '1' && self::is_connected();
    }

    /**
     * Get OAuth authorization URL
     */
    public static function get_auth_url() {
        $client_id = get_option('fs_gcal_client_id');
        $redirect_uri = admin_url('admin.php?page=fs-settings&tab=calendar&gcal_callback=1');
        
        $params = array(
            'client_id' => $client_id,
            'redirect_uri' => $redirect_uri,
            'response_type' => 'code',
            'scope' => self::SCOPES,
            'access_type' => 'offline',
            'prompt' => 'consent',
            'state' => wp_create_nonce('fs_gcal_oauth'),
        );
        
        return self::OAUTH_AUTH_URL . '?' . http_build_query($params);
    }

    /**
     * Exchange authorization code for tokens
     */
    public static function exchange_code($code) {
        $client_id = get_option('fs_gcal_client_id');
        $client_secret = get_option('fs_gcal_client_secret');
        $redirect_uri = admin_url('admin.php?page=fs-settings&tab=calendar&gcal_callback=1');
        
        error_log('FS Google Calendar: Exchanging authorization code for tokens');
        
        $response = wp_remote_post(self::OAUTH_TOKEN_URL, array(
            'body' => array(
                'code' => $code,
                'client_id' => $client_id,
                'client_secret' => $client_secret,
                'redirect_uri' => $redirect_uri,
                'grant_type' => 'authorization_code',
            ),
            'timeout' => 30,
        ));
        
        if (is_wp_error($response)) {
            error_log('FS Google Calendar: Token exchange failed - ' . $response->get_error_message());
            return false;
        }
        
        $response_code = wp_remote_retrieve_response_code($response);
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        error_log('FS Google Calendar: Token exchange response code: ' . $response_code);
        
        if (isset($body['error'])) {
            error_log('FS Google Calendar: Token exchange error - ' . (isset($body['error_description']) ? $body['error_description'] : $body['error']));
            return false;
        }
        
        if (!isset($body['access_token'])) {
            error_log('FS Google Calendar: No access token in response');
            return false;
        }
        
        // Save tokens with encryption
        FS_Security_Service::update_encrypted_option('fs_gcal_access_token', $body['access_token']);
        update_option('fs_gcal_token_expires', time() + $body['expires_in']);
        
        error_log('FS Google Calendar: Access token saved (encrypted), expires in ' . $body['expires_in'] . ' seconds');
        
        if (isset($body['refresh_token'])) {
            FS_Security_Service::update_encrypted_option('fs_gcal_refresh_token', $body['refresh_token']);
            error_log('FS Google Calendar: Refresh token saved (encrypted)');
        } else {
            error_log('FS Google Calendar: No refresh token in response (may already exist)');
        }
        
        // Get user email for display
        self::fetch_user_info();
        
        error_log('FS Google Calendar: Token exchange completed successfully');
        
        return true;
    }

    /**
     * Refresh access token
     */
    public static function refresh_token() {
        $refresh_token = FS_Security_Service::get_encrypted_option('fs_gcal_refresh_token', '');
        $client_id = get_option('fs_gcal_client_id');
        $client_secret = FS_Security_Service::get_encrypted_option('fs_gcal_client_secret', get_option('fs_gcal_client_secret', ''));
        
        error_log('FS Google Calendar: Attempting token refresh. Refresh token exists: ' . (!empty($refresh_token) ? 'yes' : 'no'));
        
        if (empty($refresh_token)) {
            error_log('FS Google Calendar: No refresh token available');
            return false;
        }
        
        $response = wp_remote_post(self::OAUTH_TOKEN_URL, array(
            'body' => array(
                'refresh_token' => $refresh_token,
                'client_id' => $client_id,
                'client_secret' => $client_secret,
                'grant_type' => 'refresh_token',
            ),
            'timeout' => 30,
        ));
        
        if (is_wp_error($response)) {
            return false;
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (isset($body['access_token'])) {
            FS_Security_Service::update_encrypted_option('fs_gcal_access_token', $body['access_token']);
            update_option('fs_gcal_token_expires', time() + $body['expires_in']);
            return true;
        }
        
        return false;
    }

    /**
     * Get valid access token (refreshing if needed)
     */
    public static function get_access_token() {
        $token = FS_Security_Service::get_encrypted_option('fs_gcal_access_token', '');
        $expires = get_option('fs_gcal_token_expires', 0);
        
        error_log('FS Google Calendar: get_access_token called. Token exists: ' . (!empty($token) ? 'yes' : 'no') . ', Expires: ' . gmdate('Y-m-d H:i:s', $expires) . ', Current: ' . gmdate('Y-m-d H:i:s'));
        
        // Check if we have a token at all
        if (empty($token)) {
            error_log('FS Google Calendar: No access token stored');
            return false;
        }
        
        // Refresh if token expires in less than 5 minutes
        if ($expires < time() + 300) {
            error_log('FS Google Calendar: Token expired or expiring soon, attempting refresh');
            if (!self::refresh_token()) {
                error_log('FS Google Calendar: Token refresh failed');
                return false;
            }
            // Get the refreshed token
            $token = FS_Security_Service::get_encrypted_option('fs_gcal_access_token', '');
        }
        
        return $token;
    }

    /**
     * Fetch user info (email) for display
     */
    private static function fetch_user_info() {
        $token = get_option('fs_gcal_access_token');
        
        $response = wp_remote_get('https://www.googleapis.com/oauth2/v2/userinfo', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
            ),
        ));
        
        if (!is_wp_error($response)) {
            $body = json_decode(wp_remote_retrieve_body($response), true);
            if (isset($body['email'])) {
                update_option('fs_gcal_user_email', $body['email']);
            }
        }
    }

    /**
     * Disconnect from Google Calendar
     */
    public static function disconnect() {
        delete_option('fs_gcal_access_token');
        delete_option('fs_gcal_refresh_token');
        delete_option('fs_gcal_token_expires');
        delete_option('fs_gcal_user_email');
        delete_option('fs_gcal_calendar_id');
        delete_option('fs_gcal_enabled');
    }

    /**
     * Make API request to Google Calendar
     */
    public static function api_request($endpoint, $method = 'GET', $body = null) {
        $token = self::get_access_token();
        
        if (!$token) {
            error_log('FS Google Calendar: api_request failed - no valid access token');
            return new WP_Error('no_token', 'No valid access token');
        }
        
        $args = array(
            'method' => $method,
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
                'Content-Type' => 'application/json',
            ),
            'timeout' => 30,
        );
        
        if ($body) {
            $args['body'] = json_encode($body);
        }
        
        $url = self::API_BASE . $endpoint;
        error_log('FS Google Calendar: API request to ' . $method . ' ' . $endpoint);
        
        $response = wp_remote_request($url, $args);
        
        if (is_wp_error($response)) {
            error_log('FS Google Calendar: wp_remote_request error - ' . $response->get_error_message());
            return $response;
        }
        
        $code = wp_remote_retrieve_response_code($response);
        $response_body = wp_remote_retrieve_body($response);
        $body = json_decode($response_body, true);
        
        error_log('FS Google Calendar: API response code ' . $code);
        
        if ($code >= 400) {
            $error_msg = isset($body['error']['message']) ? $body['error']['message'] : 'Unknown error';
            error_log('FS Google Calendar: API error - ' . $error_msg);
            return new WP_Error('api_error', $error_msg, array('code' => $code));
        }
        
        return $body;
    }

    /**
     * Get list of user's calendars
     */
    public static function get_calendars() {
        $result = self::api_request('/users/me/calendarList');
        
        if (is_wp_error($result)) {
            error_log('FS Google Calendar: get_calendars failed - ' . $result->get_error_message());
            return array();
        }
        
        error_log('FS Google Calendar: get_calendars returned ' . count(isset($result['items']) ? $result['items'] : array()) . ' calendars');
        
        return isset($result['items']) ? $result['items'] : array();
    }

    /**
     * Create Freelance Studio calendar
     */
    public static function create_freelance_calendar() {
        error_log('FS Google Calendar: Attempting to create Freelance Studio calendar');
        
        $result = self::api_request('/calendars', 'POST', array(
            'summary' => 'Freelance Studio',
            'description' => 'Business calendar managed by Freelance Studio WordPress plugin',
            'timeZone' => wp_timezone_string(),
        ));
        
        if (is_wp_error($result)) {
            error_log('FS Google Calendar: create_freelance_calendar failed - ' . $result->get_error_message());
            return false;
        }
        
        if (isset($result['id'])) {
            update_option('fs_gcal_calendar_id', $result['id']);
            return $result['id'];
        }
        
        return false;
    }

    /**
     * Get the active calendar ID
     * Returns empty string if no calendar selected (prevents fetching from 'primary' which shows all calendars)
     */
    public static function get_calendar_id() {
        $calendar_id = get_option('fs_gcal_calendar_id', '');
        // Don't use 'primary' as it can show events from multiple synced calendars
        if ($calendar_id === 'primary') {
            error_log('FS Google Calendar: Warning - calendar_id is set to "primary", this may show events from multiple calendars');
        }
        return !empty($calendar_id) ? $calendar_id : '';
    }

    /**
     * Get color ID for a client (creates consistent colors per client)
     */
    public static function get_client_color_id($client_id) {
        $color_map = get_option('fs_gcal_client_colors', array());
        
        if (!isset($color_map[$client_id])) {
            // Google Calendar has 11 color options (1-11)
            // Assign based on client ID to ensure consistency
            $color_map[$client_id] = ($client_id % 11) + 1;
            update_option('fs_gcal_client_colors', $color_map);
        }
        
        return (string) $color_map[$client_id];
    }

    /**
     * Create or update a calendar event
     */
    public static function upsert_event($event_data, $event_id = null, $with_conference = false) {
        if (!self::is_enabled()) {
            return false;
        }
        
        $calendar_id = self::get_calendar_id();
        if (empty($calendar_id)) {
            error_log('FS Google Calendar: No calendar selected, cannot create event');
            return false;
        }
        
        // Add color if client_id is provided
        if (isset($event_data['client_id'])) {
            $event_data['colorId'] = self::get_client_color_id($event_data['client_id']);
            unset($event_data['client_id']);
        }
        
        // Disable notifications by default (user can enable in Google Calendar if desired)
        if (!isset($event_data['reminders'])) {
            $event_data['reminders'] = array(
                'useDefault' => false,
                'overrides' => array(),
            );
        }
        
        // Add metadata
        if (!isset($event_data['extendedProperties'])) {
            $event_data['extendedProperties'] = array('private' => array());
        }
        $event_data['extendedProperties']['private']['source'] = 'freelance-studio';
        $event_data['extendedProperties']['private']['updated_at'] = current_time('c');
        
        // Build endpoint with conferenceDataVersion if needed
        // IMPORTANT: sendUpdates=none prevents Google from sending automatic invitation emails to attendees
        // Our plugin handles all email notifications to avoid duplicate emails
        $query_params = array(
            'sendUpdates' => 'none',  // Suppress Google Calendar invitation emails
        );
        if ($with_conference && isset($event_data['conferenceData'])) {
            $query_params['conferenceDataVersion'] = 1;
        }
        $query_string = '?' . http_build_query($query_params);
        
        if ($event_id) {
            // Update existing event
            $result = self::api_request(
                '/calendars/' . urlencode($calendar_id) . '/events/' . urlencode($event_id) . $query_string,
                'PUT',
                $event_data
            );
        } else {
            // Create new event
            $result = self::api_request(
                '/calendars/' . urlencode($calendar_id) . '/events' . $query_string,
                'POST',
                $event_data
            );
        }
        
        if (is_wp_error($result)) {
            error_log('FS Google Calendar: Event upsert failed - ' . $result->get_error_message());
            return false;
        }
        
        // If with_conference is true, return the full result so caller can extract meet link
        if ($with_conference) {
            return $result;
        }
        
        return isset($result['id']) ? $result['id'] : false;
    }

    /**
     * Delete a calendar event
     */
    public static function delete_event($event_id) {
        if (!self::is_enabled() || empty($event_id)) {
            return false;
        }
        
        $calendar_id = self::get_calendar_id();
        if (empty($calendar_id)) {
            return false;
        }
        
        // sendUpdates=none prevents Google from sending automatic cancellation emails
        $result = self::api_request(
            '/calendars/' . urlencode($calendar_id) . '/events/' . urlencode($event_id) . '?sendUpdates=none',
            'DELETE'
        );
        
        return !is_wp_error($result);
    }

    /**
     * Get event by ID
     */
    public static function get_event($event_id) {
        if (!self::is_enabled() || empty($event_id)) {
            return null;
        }
        
        $calendar_id = self::get_calendar_id();
        if (empty($calendar_id)) {
            return null;
        }
        
        $result = self::api_request(
            '/calendars/' . urlencode($calendar_id) . '/events/' . urlencode($event_id)
        );
        
        if (is_wp_error($result)) {
            return null;
        }
        
        return $result;
    }

    /**
     * Get events in a date range from Google Calendar
     */
    public static function get_events_in_range($start_date, $end_date) {
        if (!self::is_enabled()) {
            return array();
        }
        
        $calendar_id = self::get_calendar_id();
        if (empty($calendar_id)) {
            // No calendar selected - don't fetch events
            return array();
        }
        
        $timezone = wp_timezone_string();
        
        // Convert to RFC3339 format
        $time_min = $start_date . 'T00:00:00' . self::get_timezone_offset();
        $time_max = $end_date . 'T23:59:59' . self::get_timezone_offset();
        
        $endpoint = '/calendars/' . urlencode($calendar_id) . '/events?' . http_build_query(array(
            'timeMin' => $time_min,
            'timeMax' => $time_max,
            'singleEvents' => 'true',
            'orderBy' => 'startTime',
            'maxResults' => 250,
        ));
        
        $result = self::api_request($endpoint);
        
        if (is_wp_error($result) || !isset($result['items'])) {
            return array();
        }
        
        $events = array();
        foreach ($result['items'] as $item) {
            // Skip birthday events (from Contacts birthday calendar)
            // These have organizer email ending in @group.calendar.google.com with 'birthday' in it
            // or have eventType of 'birthday'
            if (isset($item['eventType']) && $item['eventType'] === 'birthday') {
                continue;
            }
            
            // Skip events from other calendars that are "imported" or shown on this calendar
            // Birthday calendar organizer looks like: addressbook#contacts@group.v.calendar.google.com
            if (isset($item['organizer']['email'])) {
                $organizer_email = $item['organizer']['email'];
                // Skip if from birthday/contacts calendar
                if (strpos($organizer_email, 'addressbook#contacts@group') !== false) {
                    continue;
                }
                // Skip if from holiday calendar
                if (strpos($organizer_email, '#holiday@group') !== false) {
                    continue;
                }
                // Skip if from other google system calendars
                if (strpos($organizer_email, '@group.v.calendar.google.com') !== false 
                    && strpos($organizer_email, '@group.calendar.google.com') === false) {
                    continue;
                }
            }
            
            // Determine if this is a Freelance Studio event
            $is_fs_event = isset($item['extendedProperties']['private']['source']) 
                && $item['extendedProperties']['private']['source'] === 'freelance-studio';
            
            // Get date (handle both all-day and timed events)
            $date = isset($item['start']['date']) 
                ? $item['start']['date'] 
                : substr($item['start']['dateTime'], 0, 10);
            
            $end_date = isset($item['end']['date'])
                ? $item['end']['date']
                : (isset($item['end']['dateTime']) ? substr($item['end']['dateTime'], 0, 10) : $date);
            
            $is_all_day = isset($item['start']['date']);
            
            $start_time = '';
            $end_time = '';
            if (!$is_all_day) {
                // Convert to 12-hour format
                $start_time = isset($item['start']['dateTime']) ? gmdate('g:i A', strtotime($item['start']['dateTime'])) : '';
                $end_time = isset($item['end']['dateTime']) ? gmdate('g:i A', strtotime($item['end']['dateTime'])) : '';
            }
            
            $events[] = array(
                'id' => $item['id'],
                'title' => $item['summary'] ?? '(No title)',
                'description' => $item['description'] ?? '',
                'date' => $date,
                'end_date' => $end_date,
                'start_time' => $start_time,
                'end_time' => $end_time,
                'all_day' => $is_all_day,
                'color' => isset($item['colorId']) ? self::get_color_hex($item['colorId']) : '#4285f4',
                'is_fs_event' => $is_fs_event,
                'fs_type' => $is_fs_event ? ($item['extendedProperties']['private']['fs_type'] ?? 'custom') : 'google',
                // Editable if: not a FS event at all, OR is a custom FS event (created through calendar UI)
                'editable' => !$is_fs_event || ($item['extendedProperties']['private']['fs_type'] ?? '') === '' || ($item['extendedProperties']['private']['fs_type'] ?? '') === 'custom',
            );
        }
        
        return $events;
    }

    /**
     * Get timezone offset in format +HH:MM or -HH:MM
     */
    private static function get_timezone_offset() {
        $tz = new DateTimeZone(wp_timezone_string());
        $now = new DateTime('now', $tz);
        $offset_seconds = $tz->getOffset($now);
        $offset_hours = intval($offset_seconds / 3600);
        $offset_minutes = abs(($offset_seconds % 3600) / 60);
        return sprintf('%+03d:%02d', $offset_hours, $offset_minutes);
    }

    /**
     * Convert Google Calendar color ID to hex
     */
    private static function get_color_hex($color_id) {
        // Google Calendar color palette
        $colors = array(
            '1' => '#7986cb',  // Lavender
            '2' => '#33b679',  // Sage
            '3' => '#8e24aa',  // Grape
            '4' => '#e67c73',  // Flamingo
            '5' => '#f6c026',  // Banana
            '6' => '#f5511d',  // Tangerine
            '7' => '#039be5',  // Peacock
            '8' => '#616161',  // Graphite
            '9' => '#3f51b5',  // Blueberry
            '10' => '#0b8043', // Basil
            '11' => '#d60000', // Tomato
        );
        
        return isset($colors[$color_id]) ? $colors[$color_id] : '#4285f4';
    }

    /**
     * Sync project start/end dates to calendar
     */
    public function sync_project_dates($project_id, $project = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_projects', '1')) {
            return;
        }
        
        if (!$project) {
            $project = FS_Project::get($project_id);
        }
        
        if (!$project) {
            return;
        }
        
        $client = FS_Client::get($project->client_id);
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        // Sync start date
        if (!empty($project->start_date)) {
            $event_key = 'fs_gcal_event_project_start_' . $project_id;
            $existing_event_id = get_option($event_key);
            
            $event_data = array(
                'summary' => '🚀 ' . $project->name . ' - Project Start',
                'description' => self::build_project_description($project, $client_name),
                'start' => array('date' => $project->start_date),
                'end' => array('date' => $project->start_date),
                'client_id' => $project->client_id,
                'extendedProperties' => array(
                    'private' => array(
                        'fs_type' => 'project_start',
                        'fs_project_id' => $project_id,
                    ),
                ),
            );
            
            $new_event_id = self::upsert_event($event_data, $existing_event_id);
            if ($new_event_id && $new_event_id !== $existing_event_id) {
                update_option($event_key, $new_event_id);
            }
        }
        
        // Sync end/due date
        if (!empty($project->due_date)) {
            $event_key = 'fs_gcal_event_project_end_' . $project_id;
            $existing_event_id = get_option($event_key);
            
            $event_data = array(
                'summary' => '🏁 ' . $project->name . ' - Project Deadline',
                'description' => self::build_project_description($project, $client_name),
                'start' => array('date' => $project->due_date),
                'end' => array('date' => $project->due_date),
                'client_id' => $project->client_id,
                'extendedProperties' => array(
                    'private' => array(
                        'fs_type' => 'project_end',
                        'fs_project_id' => $project_id,
                    ),
                ),
            );
            
            $new_event_id = self::upsert_event($event_data, $existing_event_id);
            if ($new_event_id && $new_event_id !== $existing_event_id) {
                update_option($event_key, $new_event_id);
            }
        }
    }

    /**
     * Build project description with link
     */
    private static function build_project_description($project, $client_name) {
        $link = admin_url('admin.php?page=fs-projects&action=view&id=' . $project->id);
        return "Client: {$client_name}\n\nView in Freelance Studio: {$link}";
    }

    /**
     * Sync milestone to calendar
     */
    public function sync_milestone($milestone_id, $milestone = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_milestones', '1')) {
            return;
        }
        
        if (!$milestone) {
            $milestone = FS_Milestone::get($milestone_id);
        }
        
        if (!$milestone || empty($milestone->due_date)) {
            return;
        }
        
        $project = FS_Project::get($milestone->project_id);
        $client = $project ? FS_Client::get($project->client_id) : null;
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        $event_key = 'fs_gcal_event_milestone_' . $milestone_id;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-projects&action=view&id=' . $milestone->project_id . '#milestones');
        
        $event_data = array(
            'summary' => '🎯 ' . $milestone->name,
            'description' => "Project: {$milestone->project_name}\nClient: {$client_name}\n\nView in Freelance Studio: {$link}",
            'start' => array('date' => $milestone->due_date),
            'end' => array('date' => $milestone->due_date),
            'client_id' => $project ? $project->client_id : 0,
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'milestone',
                    'fs_milestone_id' => $milestone_id,
                    'fs_project_id' => $milestone->project_id,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync task deadline to calendar
     */
    public function sync_task($task_id, $task = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_tasks', '1')) {
            return;
        }
        
        if (!$task) {
            $task = FS_Task::get($task_id);
        }
        
        if (!$task || empty($task->due_date)) {
            return;
        }
        
        $project = FS_Project::get($task->project_id);
        $client = $project ? FS_Client::get($project->client_id) : null;
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        $event_key = 'fs_gcal_event_task_' . $task_id;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-projects&action=view&id=' . $task->project_id . '#tasks');
        
        // Priority emoji
        $priority_emoji = '';
        switch ($task->priority) {
            case 'urgent': $priority_emoji = '🔴 '; break;
            case 'high': $priority_emoji = '🟠 '; break;
            case 'medium': $priority_emoji = '🟡 '; break;
            default: $priority_emoji = ''; break;
        }
        
        $event_data = array(
            'summary' => $priority_emoji . '✅ ' . $task->title,
            'description' => "Project: {$task->project_name}\nClient: {$client_name}\n\nView in Freelance Studio: {$link}",
            'start' => array('date' => $task->due_date),
            'end' => array('date' => $task->due_date),
            'client_id' => $project ? $project->client_id : 0,
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'task',
                    'fs_task_id' => $task_id,
                    'fs_project_id' => $task->project_id,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync invoice due date to calendar
     */
    public function sync_invoice($invoice_id, $invoice = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_invoices', '1')) {
            return;
        }
        
        if (!$invoice) {
            $invoice = FS_Invoice::get($invoice_id);
        }
        
        if (!$invoice || empty($invoice->due_date)) {
            return;
        }
        
        // Don't sync paid or cancelled invoices
        if (in_array($invoice->status, array('paid', 'cancelled'))) {
            // Remove event if exists
            $event_key = 'fs_gcal_event_invoice_' . $invoice_id;
            $event_id = get_option($event_key);
            if ($event_id) {
                self::delete_event($event_id);
                delete_option($event_key);
            }
            return;
        }
        
        $client = FS_Client::get($invoice->client_id);
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        $event_key = 'fs_gcal_event_invoice_' . $invoice_id;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $invoice_id);
        $amount = number_format($invoice->balance_due, 2);
        
        $status_emoji = $invoice->status === 'overdue' ? '⚠️ ' : '💰 ';
        
        $event_data = array(
            'summary' => $status_emoji . 'Invoice #' . $invoice->invoice_number . ' Due - $' . $amount,
            'description' => "Client: {$client_name}\nAmount Due: \${$amount}\n\nView Invoice: {$link}",
            'start' => array('date' => $invoice->due_date),
            'end' => array('date' => $invoice->due_date),
            'client_id' => $invoice->client_id,
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'invoice',
                    'fs_invoice_id' => $invoice_id,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync contract/proposal expiration to calendar
     */
    public function sync_contract($contract_id, $contract = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_contracts', '1')) {
            return;
        }
        
        if (!$contract) {
            $contract = FS_Contract::get($contract_id);
        }
        
        if (!$contract || empty($contract->valid_until)) {
            return;
        }
        
        // Don't sync signed, expired, or cancelled contracts
        if (in_array($contract->status, array('signed', 'completed', 'expired', 'cancelled', 'declined'))) {
            $event_key = 'fs_gcal_event_contract_' . $contract_id;
            $event_id = get_option($event_key);
            if ($event_id) {
                self::delete_event($event_id);
                delete_option($event_key);
            }
            return;
        }
        
        $client = FS_Client::get($contract->client_id);
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        $event_key = 'fs_gcal_event_contract_' . $contract_id;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-contracts&action=view&id=' . $contract_id);
        
        $event_data = array(
            'summary' => '📝 Contract Expires: ' . $contract->title,
            'description' => "Client: {$client_name}\n\nView Contract: {$link}",
            'start' => array('date' => $contract->valid_until),
            'end' => array('date' => $contract->valid_until),
            'client_id' => $contract->client_id,
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'contract',
                    'fs_contract_id' => $contract_id,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync proposal expiration to calendar
     */
    public function sync_proposal($proposal_id, $proposal = null) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_proposals', '1')) {
            return;
        }
        
        if (!$proposal) {
            $proposal = FS_Proposal::get($proposal_id);
        }
        
        if (!$proposal || empty($proposal->valid_until)) {
            return;
        }
        
        // Don't sync approved, expired, or declined proposals
        if (in_array($proposal->status, array('approved', 'expired', 'declined'))) {
            $event_key = 'fs_gcal_event_proposal_' . $proposal_id;
            $event_id = get_option($event_key);
            if ($event_id) {
                self::delete_event($event_id);
                delete_option($event_key);
            }
            return;
        }
        
        $client = FS_Client::get($proposal->client_id);
        $client_name = $client ? ($client->company_name ?: $client->name) : 'Unknown Client';
        
        $event_key = 'fs_gcal_event_proposal_' . $proposal_id;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-proposals&action=view&id=' . $proposal_id);
        
        $event_data = array(
            'summary' => '📋 Proposal Expires: ' . $proposal->title,
            'description' => "Client: {$client_name}\n\nView Proposal: {$link}",
            'start' => array('date' => $proposal->valid_until),
            'end' => array('date' => $proposal->valid_until),
            'client_id' => $proposal->client_id,
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'proposal',
                    'fs_proposal_id' => $proposal_id,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync quarterly tax payment deadline
     */
    public static function sync_quarterly_tax($quarter, $year, $due_date, $estimated_amount = 0) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_tax', '1')) {
            return;
        }
        
        $event_key = 'fs_gcal_event_tax_q' . $quarter . '_' . $year;
        $existing_event_id = get_option($event_key);
        
        $quarter_names = array(1 => 'Q1', 2 => 'Q2', 3 => 'Q3', 4 => 'Q4');
        $amount_text = $estimated_amount > 0 ? ' - Est. $' . number_format($estimated_amount, 2) : '';
        
        $link = admin_url('admin.php?page=fs-finances&tab=tax');
        
        $event_data = array(
            'summary' => '🏛️ Estimated Tax Payment Due - ' . $quarter_names[$quarter] . ' ' . $year . $amount_text,
            'description' => "Quarterly estimated tax payment deadline.\n\nView Tax Settings: {$link}\n\n⚠️ This is an estimate only. Consult a tax professional for accurate tax advice.",
            'start' => array('date' => $due_date),
            'end' => array('date' => $due_date),
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'quarterly_tax',
                    'fs_quarter' => $quarter,
                    'fs_year' => $year,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Sync annual tax filing deadline
     */
    public static function sync_annual_tax_deadline($year, $due_date) {
        if (!self::is_enabled() || !get_option('fs_gcal_sync_tax', '1')) {
            return;
        }
        
        $event_key = 'fs_gcal_event_tax_annual_' . $year;
        $existing_event_id = get_option($event_key);
        
        $link = admin_url('admin.php?page=fs-finances&tab=tax');
        
        $event_data = array(
            'summary' => '🏛️ Tax Filing Deadline - ' . $year,
            'description' => "Annual tax filing deadline for tax year {$year}.\n\nView Tax Settings: {$link}\n\n⚠️ Consult a tax professional for accurate tax advice.",
            'start' => array('date' => $due_date),
            'end' => array('date' => $due_date),
            'extendedProperties' => array(
                'private' => array(
                    'fs_type' => 'annual_tax',
                    'fs_year' => $year,
                ),
            ),
        );
        
        $new_event_id = self::upsert_event($event_data, $existing_event_id);
        if ($new_event_id && $new_event_id !== $existing_event_id) {
            update_option($event_key, $new_event_id);
        }
    }

    /**
     * Generate weekly digest content
     */
    public static function generate_weekly_digest() {
        $digest = array(
            'projects' => array(),
            'invoices' => array(),
            'tasks' => array(),
            'milestones' => array(),
            'proposals' => array(),
            'contracts' => array(),
        );
        
        $start_date = gmdate('Y-m-d');
        $end_date = gmdate('Y-m-d', strtotime('+7 days'));
        
        // Get projects with deadlines this week
        global $wpdb;
        
        // Projects ending this week
        $projects = $wpdb->get_results($wpdb->prepare(
            "SELECT p.*, c.name as client_name, c.company_name 
             FROM {$wpdb->prefix}fs_projects p
             LEFT JOIN {$wpdb->prefix}fs_clients c ON p.client_id = c.id
             WHERE p.due_date BETWEEN %s AND %s
             AND p.status NOT IN ('completed', 'cancelled')
             ORDER BY p.due_date ASC",
            $start_date, $end_date
        ));
        $digest['projects'] = $projects;
        
        // Invoices due this week
        $invoices = $wpdb->get_results($wpdb->prepare(
            "SELECT i.*, c.name as client_name, c.company_name 
             FROM {$wpdb->prefix}fs_invoices i
             LEFT JOIN {$wpdb->prefix}fs_clients c ON i.client_id = c.id
             WHERE i.due_date BETWEEN %s AND %s
             AND i.status NOT IN ('paid', 'cancelled')
             ORDER BY i.due_date ASC",
            $start_date, $end_date
        ));
        $digest['invoices'] = $invoices;
        
        // Tasks due this week
        $tasks = $wpdb->get_results($wpdb->prepare(
            "SELECT t.*, p.name as project_name
             FROM {$wpdb->prefix}fs_tasks t
             LEFT JOIN {$wpdb->prefix}fs_projects p ON t.project_id = p.id
             WHERE t.due_date BETWEEN %s AND %s
             AND t.status NOT IN ('completed', 'cancelled')
             ORDER BY t.due_date ASC, t.priority DESC",
            $start_date, $end_date
        ));
        $digest['tasks'] = $tasks;
        
        // Milestones due this week
        $milestones = $wpdb->get_results($wpdb->prepare(
            "SELECT m.*, p.name as project_name
             FROM {$wpdb->prefix}fs_milestones m
             LEFT JOIN {$wpdb->prefix}fs_projects p ON m.project_id = p.id
             WHERE m.due_date BETWEEN %s AND %s
             AND m.status NOT IN ('completed', 'cancelled')
             ORDER BY m.due_date ASC",
            $start_date, $end_date
        ));
        $digest['milestones'] = $milestones;
        
        // Proposals expiring this week
        $proposals = $wpdb->get_results($wpdb->prepare(
            "SELECT pr.*, c.name as client_name, c.company_name 
             FROM {$wpdb->prefix}fs_proposals pr
             LEFT JOIN {$wpdb->prefix}fs_clients c ON pr.client_id = c.id
             WHERE pr.valid_until BETWEEN %s AND %s
             AND pr.status NOT IN ('approved', 'declined', 'expired')
             ORDER BY pr.valid_until ASC",
            $start_date, $end_date
        ));
        $digest['proposals'] = $proposals;
        
        // Contracts expiring this week
        $contracts = $wpdb->get_results($wpdb->prepare(
            "SELECT co.*, c.name as client_name, c.company_name 
             FROM {$wpdb->prefix}fs_contracts co
             LEFT JOIN {$wpdb->prefix}fs_clients c ON co.client_id = c.id
             WHERE co.valid_until BETWEEN %s AND %s
             AND co.status NOT IN ('signed', 'completed', 'declined', 'expired', 'cancelled')
             ORDER BY co.valid_until ASC",
            $start_date, $end_date
        ));
        $digest['contracts'] = $contracts;
        
        return $digest;
    }

    /**
     * Send weekly digest email
     */
    public static function send_weekly_digest() {
        if (!get_option('fs_gcal_weekly_digest_enabled', '0')) {
            return;
        }
        
        $digest = self::generate_weekly_digest();
        $business_email = get_option('fs_business_email');
        
        if (empty($business_email)) {
            return;
        }
        
        // Check if there's anything to report
        $has_content = false;
        foreach ($digest as $items) {
            if (!empty($items)) {
                $has_content = true;
                break;
            }
        }
        
        if (!$has_content) {
            // Still send but with "all clear" message
        }
        
        $subject = 'Your Week Ahead - Freelance Studio Digest';
        $message = self::build_digest_email_html($digest);
        
        $headers = array(
            'Content-Type: text/html; charset=UTF-8',
        );
        
        $business_name = get_option('fs_business_name', 'Freelance Studio');
        if (!empty($business_email)) {
            $headers[] = 'From: ' . $business_name . ' <' . $business_email . '>';
        }
        
        wp_mail($business_email, $subject, $message, $headers);
    }

    /**
     * Build weekly digest email HTML
     */
    private static function build_digest_email_html($digest) {
        $settings = array(
            'business_name' => get_option('fs_business_name', 'Freelance Studio'),
        );
        
        $start_date = gmdate('l, F j');
        $end_date = gmdate('l, F j', strtotime('+7 days'));
        
        // Randomize the headline for variety
        $headlines = array(
            __('Ready to have a great week?', 'freelance-studio'),
            __("Here's what's on deck", 'freelance-studio'),
            __('Your week at a glance', 'freelance-studio'),
            __('Time to crush it this week', 'freelance-studio'),
            __("Here's what's coming up", 'freelance-studio'),
            __('Your week, mapped out', 'freelance-studio'),
            __("What's on your plate this week", 'freelance-studio'),
        );
        $headline = $headlines[array_rand($headlines)];
        
        ob_start();
        ?>
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
        <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; color: #374151; max-width: 600px; margin: 0 auto; padding: 20px;">
            <div style="background: linear-gradient(90deg, #20bba6, #77d5c9 37.9%, #77d5c9 67%, #d2e767 100%); padding: 30px; border-radius: 8px 8px 0 0; text-align: center;">
                <h1 style="color: #374151; margin: 0; font-size: 24px;"><?php echo esc_html($headline); ?></h1>
                <p style="color: #374151; opacity: 0.7; margin: 10px 0 0 0;"><?php echo  esc_html( $start_date ); ?> - <?php echo  esc_html( $end_date ); ?></p>
            </div>
            
            <div style="background: #fff; border: 1px solid #E2E6EC; border-top: none; padding: 30px; border-radius: 0 0 8px 8px;">
                
                <?php if (empty($digest['projects']) && empty($digest['invoices']) && empty($digest['tasks']) && empty($digest['milestones']) && empty($digest['proposals']) && empty($digest['contracts'])): ?>
                    <div style="text-align: center; padding: 40px 0;">
                        <h2 style="color: #1FBBA6; margin: 0 0 10px 0;">All Clear!</h2>
                        <p style="color: #6B7280;">No deadlines or due dates this week. Enjoy your time!</p>
                    </div>
                <?php else: ?>
                
                    <?php if (!empty($digest['invoices'])): ?>
                    <div style="margin-bottom: 25px;">
                        <h2 style="color: #374151; font-size: 18px; border-bottom: 2px solid #77D5C9; padding-bottom: 10px; margin-bottom: 15px;">
                            Invoices Due (<?php echo count($digest['invoices']); ?>)
                        </h2>
                        <?php foreach ($digest['invoices'] as $invoice): ?>
                            <div style="background: #FAFBFC; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid #FFA26E;">
                                <strong style="color: #374151;">Invoice #<?php echo esc_html($invoice->invoice_number); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($invoice->due_date)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($invoice->company_name ?: $invoice->client_name); ?></span>
                                <span style="color: #1FBBA6; font-weight: 600; float: right;">$<?php echo number_format($invoice->balance_due, 2); ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    
                    <?php if (!empty($digest['projects'])): ?>
                    <div style="margin-bottom: 25px;">
                        <h2 style="color: #374151; font-size: 18px; border-bottom: 2px solid #77D5C9; padding-bottom: 10px; margin-bottom: 15px;">
                            Project Deadlines (<?php echo count($digest['projects']); ?>)
                        </h2>
                        <?php foreach ($digest['projects'] as $project): ?>
                            <div style="background: #FAFBFC; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid #81A5DF;">
                                <strong style="color: #374151;"><?php echo esc_html($project->name); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($project->due_date)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($project->company_name ?: $project->client_name); ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    
                    <?php if (!empty($digest['milestones'])): ?>
                    <div style="margin-bottom: 25px;">
                        <h2 style="color: #374151; font-size: 18px; border-bottom: 2px solid #77D5C9; padding-bottom: 10px; margin-bottom: 15px;">
                            Milestones Due (<?php echo count($digest['milestones']); ?>)
                        </h2>
                        <?php foreach ($digest['milestones'] as $milestone): ?>
                            <div style="background: #FAFBFC; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid #D2E768;">
                                <strong style="color: #374151;"><?php echo esc_html($milestone->name); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($milestone->due_date)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($milestone->project_name); ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    
                    <?php if (!empty($digest['tasks'])): ?>
                    <div style="margin-bottom: 25px;">
                        <h2 style="color: #374151; font-size: 18px; border-bottom: 2px solid #77D5C9; padding-bottom: 10px; margin-bottom: 15px;">
                            Tasks Due (<?php echo count($digest['tasks']); ?>)
                        </h2>
                        <?php foreach ($digest['tasks'] as $task): 
                            $priority_color = '#9CA3AF';
                            if ($task->priority === 'urgent') $priority_color = '#E85A5A';
                            elseif ($task->priority === 'high') $priority_color = '#FFA26E';
                            
                            $status_label = ucfirst(str_replace('_', ' ', $task->status));
                            $status_color = '#81A5DF';
                            if ($task->status === 'todo') $status_color = '#9CA3AF';
                            elseif ($task->status === 'in_progress') $status_color = '#81A5DF';
                        ?>
                            <div style="background: #FAFBFC; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid <?php echo  esc_attr( $priority_color ); ?>;">
                                <strong style="color: #374151;"><?php echo esc_html($task->title); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($task->due_date)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($task->project_name); ?></span>
                                <span style="display: inline-block; background: <?php echo  esc_attr( $status_color ); ?>; color: #fff; font-size: 11px; padding: 2px 8px; border-radius: 10px; margin-left: 8px; vertical-align: middle;"><?php echo esc_html($status_label); ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    
                    <?php if (!empty($digest['proposals']) || !empty($digest['contracts'])): ?>
                    <div style="margin-bottom: 25px;">
                        <h2 style="color: #374151; font-size: 18px; border-bottom: 2px solid #77D5C9; padding-bottom: 10px; margin-bottom: 15px;">
                            Expiring Soon
                        </h2>
                        <?php foreach ($digest['proposals'] as $proposal): ?>
                            <div style="background: #FFF0F0; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid #E85A5A;">
                                <strong style="color: #374151;">Proposal: <?php echo esc_html($proposal->title); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($proposal->valid_until)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($proposal->company_name ?: $proposal->client_name); ?></span>
                            </div>
                        <?php endforeach; ?>
                        <?php foreach ($digest['contracts'] as $contract): ?>
                            <div style="background: #FFF0F0; padding: 12px 15px; border-radius: 6px; margin-bottom: 10px; border-left: 4px solid #E85A5A;">
                                <strong style="color: #374151;">Contract: <?php echo esc_html($contract->title); ?></strong>
                                <span style="color: #6B7280; float: right;"><?php echo  esc_html( gmdate('M j', strtotime($contract->valid_until)) ); ?></span>
                                <br>
                                <span style="color: #6B7280; font-size: 14px;"><?php echo esc_html($contract->company_name ?: $contract->client_name); ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>
                    <?php endif; ?>
                    
                <?php endif; ?>
                
                <div style="text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #EEF1F5;">
                    <a href="<?php echo  esc_url( admin_url('admin.php?page=fs-dashboard') ); ?>" style="display: inline-block; background: linear-gradient(135deg, #1FBBA6, #2BA891); color: #fff; padding: 12px 30px; border-radius: 8px; text-decoration: none; font-weight: 600;">
                        Open Dashboard
                    </a>
                </div>
            </div>
            
            <div style="text-align: center; padding: 20px; color: #9CA3AF; font-size: 12px;">
                <p>This digest was sent by Freelance Studio<br>
                <a href="<?php echo  esc_url( admin_url('admin.php?page=fs-settings&tab=calendar') ); ?>" style="color: #1FBBA6;">Manage notification settings</a></p>
            </div>
        </body>
        </html>
        <?php
        return ob_get_clean();
    }

    /**
     * Generate demo/preview digest content
     */
    public static function get_demo_digest() {
        return array(
            'projects' => array(
                (object) array(
                    'name' => 'Website Redesign',
                    'due_date' => gmdate('Y-m-d', strtotime('+2 days')),
                    'client_name' => 'Acme Corp',
                    'company_name' => 'Acme Corporation',
                ),
                (object) array(
                    'name' => 'Brand Identity Package',
                    'due_date' => gmdate('Y-m-d', strtotime('+5 days')),
                    'client_name' => 'StartUp Inc',
                    'company_name' => '',
                ),
            ),
            'invoices' => array(
                (object) array(
                    'invoice_number' => 'INV-2025-001',
                    'due_date' => gmdate('Y-m-d', strtotime('+1 day')),
                    'balance_due' => 2500.00,
                    'client_name' => 'Tech Solutions',
                    'company_name' => 'Tech Solutions LLC',
                ),
                (object) array(
                    'invoice_number' => 'INV-2025-002',
                    'due_date' => gmdate('Y-m-d', strtotime('+4 days')),
                    'balance_due' => 1200.00,
                    'client_name' => 'Creative Agency',
                    'company_name' => '',
                ),
            ),
            'tasks' => array(
                (object) array(
                    'title' => 'Final design revisions',
                    'due_date' => gmdate('Y-m-d', strtotime('+1 day')),
                    'project_name' => 'Website Redesign',
                    'priority' => 'urgent',
                    'status' => 'in_progress',
                ),
                (object) array(
                    'title' => 'Prepare presentation deck',
                    'due_date' => gmdate('Y-m-d', strtotime('+3 days')),
                    'project_name' => 'Brand Identity Package',
                    'priority' => 'high',
                    'status' => 'todo',
                ),
                (object) array(
                    'title' => 'Export final assets',
                    'due_date' => gmdate('Y-m-d', strtotime('+5 days')),
                    'project_name' => 'Website Redesign',
                    'priority' => 'medium',
                    'status' => 'in_progress',
                ),
            ),
            'milestones' => array(
                (object) array(
                    'name' => 'Design Phase Complete',
                    'due_date' => gmdate('Y-m-d', strtotime('+3 days')),
                    'project_name' => 'Website Redesign',
                ),
            ),
            'proposals' => array(
                (object) array(
                    'title' => 'E-commerce Platform Development',
                    'valid_until' => gmdate('Y-m-d', strtotime('+2 days')),
                    'client_name' => 'Retail Plus',
                    'company_name' => 'Retail Plus Inc',
                ),
            ),
            'contracts' => array(
                (object) array(
                    'title' => 'Annual Retainer Agreement',
                    'valid_until' => gmdate('Y-m-d', strtotime('+6 days')),
                    'client_name' => 'Marketing Co',
                    'company_name' => '',
                ),
            ),
        );
    }

    /**
     * Register webhook endpoint for Google Calendar push notifications
     */
    public function register_webhook_endpoint() {
        register_rest_route('freelance-studio/v1', '/gcal-webhook', array(
            'methods' => 'POST',
            'callback' => array($this, 'handle_webhook'),
            'permission_callback' => '__return_true',
        ));
    }

    /**
     * Handle incoming webhook from Google Calendar
     */
    public function handle_webhook($request) {
        $channel_id = $request->get_header('X-Goog-Channel-ID');
        $resource_state = $request->get_header('X-Goog-Resource-State');
        
        // Verify channel
        $stored_channel = get_option('fs_gcal_webhook_channel');
        if ($channel_id !== $stored_channel) {
            return new WP_REST_Response(array('error' => 'Invalid channel'), 403);
        }
        
        // Handle sync notification
        if ($resource_state === 'sync') {
            return new WP_REST_Response(array('status' => 'ok'), 200);
        }
        
        // Handle change notification
        if ($resource_state === 'exists') {
            // Queue sync job
            wp_schedule_single_event(time(), 'fs_gcal_sync_from_google');
        }
        
        return new WP_REST_Response(array('status' => 'ok'), 200);
    }

    /**
     * Full sync - push all existing dates to calendar
     */
    public static function full_sync() {
        if (!self::is_enabled()) {
            return array('success' => false, 'message' => 'Google Calendar not connected');
        }
        
        $synced = array(
            'projects' => 0,
            'milestones' => 0,
            'tasks' => 0,
            'invoices' => 0,
            'contracts' => 0,
            'proposals' => 0,
        );
        
        $service = self::get_instance();
        
        // Sync all active projects
        $projects = FS_Project::get_all(array(
            'status' => array('pending', 'active', 'on_hold'),
        ));
        foreach ($projects as $project) {
            $service->sync_project_dates($project->id, $project);
            $synced['projects']++;
        }
        
        // Sync milestones
        $milestones = FS_Milestone::get_all(array(
            'status' => array('pending', 'in_progress'),
        ));
        foreach ($milestones as $milestone) {
            $service->sync_milestone($milestone->id, $milestone);
            $synced['milestones']++;
        }
        
        // Sync tasks with due dates
        $tasks = FS_Task::get_all(array(
            'status' => array('pending', 'in_progress'),
        ));
        foreach ($tasks as $task) {
            if (!empty($task->due_date)) {
                $service->sync_task($task->id, $task);
                $synced['tasks']++;
            }
        }
        
        // Sync unpaid invoices
        $invoices = FS_Invoice::get_all(array(
            'status' => array('draft', 'sent', 'viewed', 'partial', 'overdue'),
        ));
        foreach ($invoices as $invoice) {
            $service->sync_invoice($invoice->id, $invoice);
            $synced['invoices']++;
        }
        
        // Sync active contracts
        $contracts = FS_Contract::get_all(array(
            'status' => array('draft', 'sent', 'viewed'),
        ));
        foreach ($contracts as $contract) {
            $service->sync_contract($contract->id, $contract);
            $synced['contracts']++;
        }
        
        // Sync active proposals
        $proposals = FS_Proposal::get_all(array(
            'status' => array('draft', 'sent', 'viewed'),
        ));
        foreach ($proposals as $proposal) {
            $service->sync_proposal($proposal->id, $proposal);
            $synced['proposals']++;
        }
        
        return array(
            'success' => true,
            'synced' => $synced,
        );
    }

    /**
     * ====================================================================
     * TWO-WAY SYNC METHODS (Google Calendar → Freelance Studio)
     * ====================================================================
     */

    /**
     * Check if two-way sync is enabled
     */
    public static function is_two_way_sync_enabled() {
        return get_option('fs_gcal_two_way_sync', '0') === '1' && self::is_enabled();
    }

    /**
     * Get the last sync timestamp
     */
    public static function get_last_inbound_sync() {
        return get_option('fs_gcal_last_inbound_sync', '');
    }

    /**
     * Fetch events that have been updated since the last sync
     * Uses Google's updatedMin parameter for efficient syncing
     */
    public static function fetch_updated_events($since = null) {
        if (!self::is_enabled()) {
            return array();
        }
        
        $calendar_id = self::get_calendar_id();
        if (empty($calendar_id)) {
            return array();
        }
        
        // If no since time provided, use last sync time or default to 1 hour ago
        if (empty($since)) {
            $since = self::get_last_inbound_sync();
            if (empty($since)) {
                $since = gmdate('c', strtotime('-1 hour'));
            }
        }
        
        // Build query parameters
        $params = array(
            'updatedMin' => $since,
            'singleEvents' => 'true',
            'maxResults' => 250,
            'showDeleted' => 'true', // Include cancelled/deleted events
        );
        
        $endpoint = '/calendars/' . urlencode($calendar_id) . '/events?' . http_build_query($params);
        
        $result = self::api_request($endpoint);
        
        if (is_wp_error($result) || !isset($result['items'])) {
            error_log('FS Google Calendar: fetch_updated_events failed - ' . (is_wp_error($result) ? $result->get_error_message() : 'No items'));
            return array();
        }
        
        // Filter to only Freelance Studio events
        $fs_events = array();
        foreach ($result['items'] as $item) {
            // Only process events created by Freelance Studio
            if (isset($item['extendedProperties']['private']['source']) 
                && $item['extendedProperties']['private']['source'] === 'freelance-studio') {
                $fs_events[] = $item;
            }
        }
        
        error_log('FS Google Calendar: Found ' . count($fs_events) . ' updated Freelance Studio events since ' . $since);
        
        return $fs_events;
    }

    /**
     * Process inbound sync - update plugin records based on Google Calendar changes
     * This is the main two-way sync method called by the cron job
     */
    public static function process_inbound_sync() {
        if (!self::is_two_way_sync_enabled()) {
            return array('success' => false, 'message' => 'Two-way sync not enabled');
        }
        
        $updated_events = self::fetch_updated_events();
        
        $results = array(
            'processed' => 0,
            'updated' => 0,
            'cancelled' => 0,
            'skipped' => 0,
            'errors' => 0,
        );
        
        foreach ($updated_events as $event) {
            $results['processed']++;
            
            $fs_type = $event['extendedProperties']['private']['fs_type'] ?? '';
            $is_cancelled = ($event['status'] ?? '') === 'cancelled';
            
            try {
                switch ($fs_type) {
                    case 'booking':
                        $result = self::sync_booking_from_google($event, $is_cancelled);
                        break;
                    case 'task':
                        $result = self::sync_task_from_google($event, $is_cancelled);
                        break;
                    case 'milestone':
                        $result = self::sync_milestone_from_google($event, $is_cancelled);
                        break;
                    case 'invoice':
                        $result = self::sync_invoice_from_google($event, $is_cancelled);
                        break;
                    case 'project_start':
                    case 'project_end':
                        $result = self::sync_project_from_google($event, $fs_type, $is_cancelled);
                        break;
                    case 'contract':
                    case 'proposal':
                        // These are typically not editable from calendar
                        $result = 'skipped';
                        break;
                    default:
                        $result = 'skipped';
                }
                
                if ($result === 'updated') {
                    $results['updated']++;
                } elseif ($result === 'cancelled') {
                    $results['cancelled']++;
                } else {
                    $results['skipped']++;
                }
            } catch (Exception $e) {
                error_log('FS Google Calendar: Error processing event ' . $event['id'] . ' - ' . $e->getMessage());
                $results['errors']++;
            }
        }
        
        // Update last sync timestamp
        update_option('fs_gcal_last_inbound_sync', gmdate('c'));
        
        error_log('FS Google Calendar: Inbound sync complete - ' . json_encode($results));
        
        return array('success' => true, 'results' => $results);
    }

    /**
     * Sync a booking from Google Calendar changes
     */
    private static function sync_booking_from_google($event, $is_cancelled) {
        $booking_id = $event['extendedProperties']['private']['fs_booking_id'] ?? null;
        
        if (!$booking_id) {
            // Try to find by gcal_event_id
            $booking = FS_Booking::get_by_gcal_event($event['id']);
            if ($booking) {
                $booking_id = $booking->id;
            }
        }
        
        if (!$booking_id) {
            return 'skipped';
        }
        
        $booking = FS_Booking::get($booking_id);
        if (!$booking) {
            return 'skipped';
        }
        
        // Handle cancellation
        if ($is_cancelled && $booking->status !== 'cancelled') {
            // Event was deleted/cancelled in Google Calendar
            // Use direct DB update to avoid triggering sync_to_gcal
            global $wpdb;
            $table = $wpdb->prefix . 'fs_bookings';
            $wpdb->update($table, array(
                'status' => 'cancelled',
                'gcal_event_id' => null, // Clear to prevent future sync attempts
            ), array('id' => $booking_id));
            error_log('FS Google Calendar: Booking #' . $booking_id . ' marked as cancelled from Google Calendar');
            return 'cancelled';
        }
        
        // Skip if booking is already cancelled - don't sync time changes
        if ($booking->status === 'cancelled') {
            return 'skipped';
        }
        
        // Handle time/date changes
        // Bookings table uses start_datetime and end_datetime (combined datetime columns)
        $changes = array();
        
        if (isset($event['start']['dateTime'])) {
            // Timed event - extract full datetime
            $new_start = gmdate('Y-m-d H:i:s', strtotime($event['start']['dateTime']));
            $new_end = gmdate('Y-m-d H:i:s', strtotime($event['end']['dateTime']));
            
            if ($booking->start_datetime !== $new_start) {
                $changes['start_datetime'] = $new_start;
            }
            if ($booking->end_datetime !== $new_end) {
                $changes['end_datetime'] = $new_end;
            }
        } elseif (isset($event['start']['date'])) {
            // All-day event - just update the date portion, keep existing times
            $event_date = $event['start']['date'];
            $existing_start_time = gmdate('H:i:s', strtotime($booking->start_datetime));
            $existing_end_time = gmdate('H:i:s', strtotime($booking->end_datetime));
            
            $new_start = $event_date . ' ' . $existing_start_time;
            $new_end = $event_date . ' ' . $existing_end_time;
            
            if ($booking->start_datetime !== $new_start) {
                $changes['start_datetime'] = $new_start;
                $changes['end_datetime'] = $new_end;
            }
        }
        
        if (!empty($changes)) {
            FS_Booking::update($booking_id, $changes);
            error_log('FS Google Calendar: Booking #' . $booking_id . ' updated from Google Calendar - ' . json_encode($changes));
            return 'updated';
        }
        
        return 'skipped';
    }

    /**
     * Sync a task from Google Calendar changes
     */
    private static function sync_task_from_google($event, $is_cancelled) {
        $task_id = $event['extendedProperties']['private']['fs_task_id'] ?? null;
        
        if (!$task_id) {
            return 'skipped';
        }
        
        $task = FS_Task::get($task_id);
        if (!$task) {
            return 'skipped';
        }
        
        // Handle deletion (mark task as cancelled/done)
        if ($is_cancelled && !in_array($task->status, array('completed', 'cancelled'))) {
            // Deleting a task in Google Calendar marks it as completed
            FS_Task::update($task_id, array('status' => 'completed'));
            error_log('FS Google Calendar: Task #' . $task_id . ' marked as completed from Google Calendar deletion');
            return 'cancelled';
        }
        
        // Handle date changes
        $event_date = isset($event['start']['date']) 
            ? $event['start']['date'] 
            : substr($event['start']['dateTime'], 0, 10);
        
        if ($task->due_date !== $event_date) {
            FS_Task::update($task_id, array('due_date' => $event_date));
            error_log('FS Google Calendar: Task #' . $task_id . ' due date updated from Google Calendar to ' . $event_date);
            return 'updated';
        }
        
        return 'skipped';
    }

    /**
     * Sync a milestone from Google Calendar changes
     */
    private static function sync_milestone_from_google($event, $is_cancelled) {
        $milestone_id = $event['extendedProperties']['private']['fs_milestone_id'] ?? null;
        
        if (!$milestone_id) {
            return 'skipped';
        }
        
        $milestone = FS_Milestone::get($milestone_id);
        if (!$milestone) {
            return 'skipped';
        }
        
        // Handle deletion (mark milestone as completed)
        if ($is_cancelled && $milestone->status !== 'completed') {
            FS_Milestone::update($milestone_id, array('status' => 'completed'));
            error_log('FS Google Calendar: Milestone #' . $milestone_id . ' marked as completed from Google Calendar deletion');
            return 'cancelled';
        }
        
        // Handle date changes
        $event_date = isset($event['start']['date']) 
            ? $event['start']['date'] 
            : substr($event['start']['dateTime'], 0, 10);
        
        if ($milestone->due_date !== $event_date) {
            FS_Milestone::update($milestone_id, array('due_date' => $event_date));
            error_log('FS Google Calendar: Milestone #' . $milestone_id . ' due date updated from Google Calendar to ' . $event_date);
            return 'updated';
        }
        
        return 'skipped';
    }

    /**
     * Sync an invoice from Google Calendar changes
     */
    private static function sync_invoice_from_google($event, $is_cancelled) {
        $invoice_id = $event['extendedProperties']['private']['fs_invoice_id'] ?? null;
        
        if (!$invoice_id) {
            return 'skipped';
        }
        
        $invoice = FS_Invoice::get($invoice_id);
        if (!$invoice) {
            return 'skipped';
        }
        
        // Don't change paid invoices
        if ($invoice->status === 'paid') {
            return 'skipped';
        }
        
        // Handle date changes (due date)
        $event_date = isset($event['start']['date']) 
            ? $event['start']['date'] 
            : substr($event['start']['dateTime'], 0, 10);
        
        if ($invoice->due_date !== $event_date) {
            FS_Invoice::update($invoice_id, array('due_date' => $event_date));
            error_log('FS Google Calendar: Invoice #' . $invoice_id . ' due date updated from Google Calendar to ' . $event_date);
            return 'updated';
        }
        
        return 'skipped';
    }

    /**
     * Sync project dates from Google Calendar changes
     */
    private static function sync_project_from_google($event, $fs_type, $is_cancelled) {
        $project_id = $event['extendedProperties']['private']['fs_project_id'] ?? null;
        
        if (!$project_id) {
            return 'skipped';
        }
        
        $project = FS_Project::get($project_id);
        if (!$project) {
            return 'skipped';
        }
        
        // Don't update completed projects
        if ($project->status === 'completed') {
            return 'skipped';
        }
        
        $event_date = isset($event['start']['date']) 
            ? $event['start']['date'] 
            : substr($event['start']['dateTime'], 0, 10);
        
        // Use correct field names: start_date and due_date (not end_date)
        $field = ($fs_type === 'project_start') ? 'start_date' : 'due_date';
        $current_value = ($fs_type === 'project_start') ? $project->start_date : $project->due_date;
        
        if ($current_value !== $event_date) {
            // Use partial update to only change the specific date field
            FS_Project::update_date_field($project_id, $field, $event_date);
            error_log('FS Google Calendar: Project #' . $project_id . ' ' . $field . ' updated from Google Calendar to ' . $event_date);
            return 'updated';
        }
        
        return 'skipped';
    }

    /**
     * Schedule the two-way sync cron job
     */
    public static function schedule_inbound_sync() {
        if (!self::is_two_way_sync_enabled()) {
            // Unschedule if disabled
            $timestamp = wp_next_scheduled('fs_gcal_inbound_sync');
            if ($timestamp) {
                wp_unschedule_event($timestamp, 'fs_gcal_inbound_sync');
            }
            return;
        }
        
        // Get sync frequency (default: every 15 minutes)
        $frequency = get_option('fs_gcal_sync_frequency', 'fifteen_minutes');
        
        // Check if already scheduled
        if (!wp_next_scheduled('fs_gcal_inbound_sync')) {
            wp_schedule_event(time(), $frequency, 'fs_gcal_inbound_sync');
            error_log('FS Google Calendar: Scheduled inbound sync with frequency: ' . $frequency);
        }
    }

    /**
     * Register custom cron schedules for sync frequency options
     */
    public static function add_cron_schedules($schedules) {
        $schedules['fifteen_minutes'] = array(
            'interval' => 900, // 15 minutes
            'display' => __('Every 15 minutes', 'freelance-studio'),
        );
        $schedules['thirty_minutes'] = array(
            'interval' => 1800, // 30 minutes
            'display' => __('Every 30 minutes', 'freelance-studio'),
        );
        return $schedules;
    }

    /**
     * Run manual inbound sync (triggered from settings or AJAX)
     */
    public static function manual_inbound_sync() {
        if (!self::is_enabled()) {
            return array('success' => false, 'message' => 'Google Calendar not connected');
        }
        
        // Run the sync even if two-way sync is disabled (for manual trigger)
        $updated_events = self::fetch_updated_events(gmdate('c', strtotime('-24 hours')));
        
        $results = array(
            'processed' => 0,
            'updated' => 0,
            'cancelled' => 0,
            'skipped' => 0,
            'errors' => 0,
        );
        
        foreach ($updated_events as $event) {
            $results['processed']++;
            
            $fs_type = $event['extendedProperties']['private']['fs_type'] ?? '';
            $is_cancelled = ($event['status'] ?? '') === 'cancelled';
            
            try {
                switch ($fs_type) {
                    case 'booking':
                        $result = self::sync_booking_from_google($event, $is_cancelled);
                        break;
                    case 'task':
                        $result = self::sync_task_from_google($event, $is_cancelled);
                        break;
                    case 'milestone':
                        $result = self::sync_milestone_from_google($event, $is_cancelled);
                        break;
                    case 'invoice':
                        $result = self::sync_invoice_from_google($event, $is_cancelled);
                        break;
                    case 'project_start':
                    case 'project_end':
                        $result = self::sync_project_from_google($event, $fs_type, $is_cancelled);
                        break;
                    default:
                        $result = 'skipped';
                }
                
                if ($result === 'updated') {
                    $results['updated']++;
                } elseif ($result === 'cancelled') {
                    $results['cancelled']++;
                } else {
                    $results['skipped']++;
                }
            } catch (Exception $e) {
                $results['errors']++;
            }
        }
        
        update_option('fs_gcal_last_inbound_sync', gmdate('c'));
        
        return array('success' => true, 'results' => $results);
    }
}
