<?php
/**
 * The admin-specific functionality of the plugin.
 *
 * @package Freelance_Studio
 */

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


class FS_Admin {

    /**
     * The ID of this plugin.
     *
     * @var string
     */
    private $plugin_name;

    /**
     * The version of this plugin.
     *
     * @var string
     */
    private $version;

    /**
     * Initialize the class and set its properties.
     *
     * @param string $plugin_name The name of this plugin.
     * @param string $version     The version of this plugin.
     */
    public function __construct($plugin_name, $version) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
        
        // Handle exports early before any output
        add_action('admin_init', array($this, 'handle_exports'));
        
        // Handle Google Calendar OAuth callback early before any output
        add_action('admin_init', array($this, 'handle_gcal_oauth_callback'), 1);
        
        // Handle popup timer early before WP admin chrome is output
        add_action('admin_init', array($this, 'handle_popup_timer'), 1);
        
        // Start output buffering early for all FS pages to allow safe redirects (priority 1 to run first)
        add_action('admin_init', array($this, 'handle_early_redirects'), 1);
    }
    
    /**
     * Handle Google Calendar OAuth callback early, before any output
     * Google redirects back as a GET request with ?code=...&state=...
     */
    public function handle_gcal_oauth_callback() {
        $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        $tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : '';
        
        if ($page !== 'fs-settings' || $tab !== 'calendar' || !isset($_GET['gcal_callback']) || !isset($_GET['code'])) {
            return;
        }
        
        // Verify state nonce
        if (!isset($_GET['state']) || !wp_verify_nonce($_GET['state'], 'fs_gcal_oauth')) {
            wp_die(esc_html__('Security check failed', 'freelance-studio'));
        }
        
        $code = sanitize_text_field($_GET['code']);
        
        if (FS_Google_Calendar_Service::exchange_code($code)) {
            // Create a dedicated calendar if none selected
            $calendar_id = get_option('fs_gcal_calendar_id');
            if (empty($calendar_id)) {
                FS_Google_Calendar_Service::create_freelance_calendar();
            }
            
            wp_safe_redirect(admin_url('admin.php?page=fs-settings&tab=calendar&message=connected'));
            exit;
        } else {
            wp_safe_redirect(admin_url('admin.php?page=fs-settings&tab=calendar&error=auth_failed'));
            exit;
        }
    }

    /**
     * Handle popup timer request early, before WP admin chrome is output
     * This renders a standalone lightweight timer window
     */
    public function handle_popup_timer() {
        $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '';
        
        if ($page !== 'fs-time-tracking' || $action !== 'popup_timer') {
            return;
        }
        
        if (!current_user_can('manage_options') && !current_user_can('fs_access')) {
            wp_die(esc_html__('You do not have permission to access this page.', 'freelance-studio'));
        }
        
        // Timer is a pro feature
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            wp_die(esc_html__('The live timer is available with Freelance Studio Pro.', 'freelance-studio'));
        }
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/time-tracking/popup-timer.php';
        // popup-timer.php ends with exit;
    }

    public function handle_early_redirects() {
        // Only handle our plugin's pages
        $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        if (strpos($page, 'fs-') !== 0 && $page !== 'freelance-studio') {
            return;
        }
        
        // Redirect old financial page URLs to new Finances hub
        $redirect_map = array(
            'fs-invoices'           => 'invoices',
            'fs-recurring-invoices' => 'recurring',
            'fs-expenses'           => 'expenses',
            'fs-reporting'          => 'reporting',
        );
        
        if (isset($redirect_map[$page])) {
            $tab = $redirect_map[$page];
            $new_url = admin_url('admin.php?page=fs-finances&tab=' . $tab);
            
            // Preserve other query params (action, id, status, etc.)
            $params = $_GET;
            unset($params['page']);
            
            // For expenses, rename 'tab' to 'expense_tab' to avoid collision
            if ($page === 'fs-expenses' && isset($params['tab'])) {
                $params['expense_tab'] = $params['tab'];
                unset($params['tab']);
            }
            
            // Special handling for invoices income actions
            if ($page === 'fs-invoices' && isset($params['action'])) {
                $action = $params['action'];
                if (in_array($action, array('income', 'add_income', 'edit_income', 'duplicate_income'))) {
                    $tab = 'income';
                    $action_map = array(
                        'income' => '',
                        'add_income' => 'add',
                        'edit_income' => 'edit',
                        'duplicate_income' => 'duplicate',
                    );
                    if (!empty($action_map[$action])) {
                        $params['action'] = $action_map[$action];
                    } else {
                        unset($params['action']);
                    }
                    $new_url = admin_url('admin.php?page=fs-finances&tab=' . $tab);
                }
            }
            
            if (!empty($params)) {
                $new_url = add_query_arg($params, $new_url);
            }
            
            wp_safe_redirect($new_url, 301);
            exit;
        }
        
        // Start output buffering for ALL FS pages (GET and POST)
        // This ensures safe_redirect() can always clean buffers before redirecting,
        // preventing "headers already sent" errors on actions like archive, duplicate, etc.
        ob_start();
        
        // For POST requests, route to early handlers that process before any output
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            // Settings
            if ($page === 'fs-settings' && isset($_POST['fs_settings_nonce'])) {
                $this->early_save_settings();
            }
            
            // Clients
            if ($page === 'fs-clients' && isset($_POST['fs_client_nonce'])) {
                $this->early_save_client();
            }
            
            // Projects
            if ($page === 'fs-projects' && isset($_POST['fs_project_nonce'])) {
                $this->early_save_project();
            }
        }
        
        // Buffer stays open for GET-based actions (archive, duplicate, delete, etc.)
        // that run inside controllers and call safe_redirect()
    }
    
    /**
     * Safe redirect helper - cleans all output buffers before redirecting
     * This should be used instead of wp_redirect() to avoid "headers already sent" errors
     *
     * @param string $url The URL to redirect to
     * @param int $status HTTP status code (default 302)
     */
    public static function safe_redirect($url, $status = 302) {
        // Clean all output buffers
        while (ob_get_level()) {
            ob_end_clean();
        }
        wp_safe_redirect($url, $status);
        exit;
    }
    
    /**
     * Early save settings (runs in admin_init before any output)
     */
    private function early_save_settings() {
        if (!wp_verify_nonce($_POST['fs_settings_nonce'], 'fs_save_settings')) {
            ob_end_clean();
            return;
        }
        
        $active_tab = isset($_POST['active_tab']) ? sanitize_text_field(wp_unslash($_POST['active_tab'])) : 'business';
        
        // Business Information
        if (isset($_POST['business_name'])) {
            update_option('fs_business_name', sanitize_text_field(wp_unslash($_POST['business_name'])));
        }
        if (isset($_POST['business_email'])) {
            update_option('fs_business_email', sanitize_email(wp_unslash($_POST['business_email'])));
        }
        if (isset($_POST['business_phone'])) {
            update_option('fs_business_phone', sanitize_text_field(wp_unslash($_POST['business_phone'])));
        }
        if (isset($_POST['business_website'])) {
            update_option('fs_business_website', esc_url_raw($_POST['business_website']));
        }
        if (isset($_POST['booking_type'])) {
            update_option('fs_booking_type', sanitize_text_field(wp_unslash($_POST['booking_type'])));
        }
        if (isset($_POST['booking_url'])) {
            update_option('fs_booking_url', esc_url_raw($_POST['booking_url']));
        }
        if (isset($_POST['my_role'])) {
            $valid_roles = array('freelancer', 'designer', 'developer', 'project_manager', 'consultant', 'agency');
            $role = sanitize_text_field(wp_unslash($_POST['my_role']));
            if (in_array($role, $valid_roles)) {
                update_option('fs_my_role', $role);
            }
        }
        
        // Internal Project Tracking
        if ($active_tab === 'business') {
            $internal_tracking_enabled = isset($_POST['internal_tracking_enabled']) ? '1' : '0';
            $was_enabled = get_option('fs_internal_tracking_enabled', '0');
            update_option('fs_internal_tracking_enabled', $internal_tracking_enabled);
            
            if ($internal_tracking_enabled === '1' && $was_enabled !== '1') {
                $internal_client_id = FS_Client::create_internal_client();
            }
            
            // High Contrast Mode
            update_option('fs_high_contrast_mode', isset($_POST['high_contrast_mode']) ? '1' : '0');
        }
        
        // Business Address
        if (isset($_POST['business_address'])) {
            update_option('fs_business_address', sanitize_textarea_field(wp_unslash($_POST['business_address'])));
        }
        if (isset($_POST['business_city'])) {
            update_option('fs_business_city', sanitize_text_field(wp_unslash($_POST['business_city'])));
        }
        if (isset($_POST['business_state'])) {
            update_option('fs_business_state', sanitize_text_field(wp_unslash($_POST['business_state'])));
        }
        if (isset($_POST['business_zip'])) {
            update_option('fs_business_zip', sanitize_text_field(wp_unslash($_POST['business_zip'])));
        }
        if (isset($_POST['business_country'])) {
            update_option('fs_business_country', sanitize_text_field(wp_unslash($_POST['business_country'])));
        }
        
        // Branding
        if (isset($_POST['business_logo'])) {
            update_option('fs_business_logo', esc_url_raw($_POST['business_logo']));
        }
        if (isset($_POST['business_logo_light'])) {
            update_option('fs_business_logo_light', esc_url_raw($_POST['business_logo_light']));
        }
        if (isset($_POST['invoice_primary_color'])) {
            update_option('fs_invoice_primary_color', sanitize_hex_color($_POST['invoice_primary_color']));
        }
        if (isset($_POST['invoice_primary_color_mode'])) {
            $mode = sanitize_text_field($_POST['invoice_primary_color_mode']);
            if (in_array($mode, array('solid', 'gradient'))) {
                update_option('fs_invoice_primary_color_mode', $mode);
            }
        }
        if (isset($_POST['invoice_primary_color_2'])) {
            update_option('fs_invoice_primary_color_2', sanitize_hex_color($_POST['invoice_primary_color_2']));
        }
        if (isset($_POST['invoice_gradient_direction'])) {
            update_option('fs_invoice_gradient_direction', intval($_POST['invoice_gradient_direction']));
        }
        
        // Contractor Signature
        if (isset($_POST['contractor_signature'])) {
            $signature = wp_unslash($_POST['contractor_signature']);
            if (empty($signature) || (is_string($signature) && strpos($signature, 'data:image/') === 0) || filter_var($signature, FILTER_VALIDATE_URL)) {
                update_option('fs_contractor_signature', $signature);
            }
        }
        if (isset($_POST['contractor_signer_name'])) {
            update_option('fs_contractor_signer_name', sanitize_text_field(wp_unslash($_POST['contractor_signer_name'])));
        }
        if (isset($_POST['contractor_signer_title'])) {
            update_option('fs_contractor_signer_title', sanitize_text_field(wp_unslash($_POST['contractor_signer_title'])));
        }
        
        // Default Rates
        if (isset($_POST['default_hourly_rate'])) {
            update_option('fs_default_hourly_rate', floatval($_POST['default_hourly_rate']));
        }
        if (isset($_POST['default_payment_terms'])) {
            update_option('fs_default_payment_terms', intval($_POST['default_payment_terms']));
        }
        if (isset($_POST['default_deposit_percentage'])) {
            update_option('fs_default_deposit_percentage', floatval($_POST['default_deposit_percentage']));
        }
        
        // Invoice Settings
        if (isset($_POST['invoice_prefix'])) {
            update_option('fs_invoice_prefix', sanitize_text_field(wp_unslash($_POST['invoice_prefix'])));
        }
        if (isset($_POST['invoice_notes'])) {
            update_option('fs_invoice_notes', sanitize_textarea_field(wp_unslash($_POST['invoice_notes'])));
        }
        if (isset($_POST['invoice_footer'])) {
            update_option('fs_invoice_footer', sanitize_textarea_field(wp_unslash($_POST['invoice_footer'])));
        }
        if (isset($_POST['late_fee_enabled'])) {
            update_option('fs_late_fee_enabled', 1);
            update_option('fs_late_fee_percentage', floatval($_POST['late_fee_percentage'] ?? 0));
            update_option('fs_late_fee_grace_days', intval($_POST['late_fee_grace_days'] ?? 0));
        } elseif ($active_tab === 'invoices') {
            update_option('fs_late_fee_enabled', 0);
        }
        
        // Payment Methods
        if (isset($_POST['payment_methods'])) {
            $methods = array_map('sanitize_text_field', $_POST['payment_methods']);
            update_option('fs_payment_methods', implode(',', $methods));
        } elseif ($active_tab === 'invoices') {
            update_option('fs_payment_methods', '');
        }
        
        // Payment Instructions
        if ($active_tab === 'invoices') {
            if (isset($_POST['payment_instructions'])) {
                update_option('fs_payment_instructions', wp_kses_post(wp_unslash($_POST['payment_instructions'])));
            }
        }
        if (isset($_POST['payment_url'])) {
            update_option('fs_payment_url', esc_url_raw($_POST['payment_url']));
        }
        
        // Proposal Settings
        if (isset($_POST['proposal_prefix'])) {
            update_option('fs_proposal_prefix', sanitize_text_field(wp_unslash($_POST['proposal_prefix'])));
        }
        if (isset($_POST['proposal_validity_days'])) {
            update_option('fs_proposal_validity_days', intval($_POST['proposal_validity_days']));
        }
        if (isset($_POST['proposal_notes'])) {
            update_option('fs_proposal_notes', sanitize_textarea_field(wp_unslash($_POST['proposal_notes'])));
        }
        
        // Contract Settings
        if (isset($_POST['contract_prefix'])) {
            update_option('fs_contract_prefix', sanitize_text_field(wp_unslash($_POST['contract_prefix'])));
        }
        if (isset($_POST['contract_expiry_days'])) {
            update_option('fs_contract_expiry_days', intval($_POST['contract_expiry_days']));
        }
        if (isset($_POST['contract_jurisdiction'])) {
            update_option('fs_contract_jurisdiction', sanitize_text_field(wp_unslash($_POST['contract_jurisdiction'])));
        }
        if (isset($_POST['contract_review_days'])) {
            update_option('fs_contract_review_days', intval($_POST['contract_review_days']));
        }
        if (isset($_POST['contract_notice_days'])) {
            update_option('fs_contract_notice_days', intval($_POST['contract_notice_days']));
        }
        if (isset($_POST['contract_cure_days'])) {
            update_option('fs_contract_cure_days', intval($_POST['contract_cure_days']));
        }
        if (isset($_POST['contract_suspension_days'])) {
            update_option('fs_contract_suspension_days', intval($_POST['contract_suspension_days']));
        }
        if (isset($_POST['contract_late_fee_percent'])) {
            update_option('fs_contract_late_fee_percent', floatval($_POST['contract_late_fee_percent']));
        }
        
        // Time Tracking Settings
        if (isset($_POST['user_timezone'])) {
            update_option('fs_user_timezone', sanitize_text_field(wp_unslash($_POST['user_timezone'])));
        }
        if (isset($_POST['time_rounding'])) {
            update_option('fs_time_rounding', intval($_POST['time_rounding']));
        }
        if (isset($_POST['minimum_billable'])) {
            update_option('fs_minimum_billable', intval($_POST['minimum_billable']));
        }
        if (isset($_POST['weekly_goal'])) {
            update_option('fs_weekly_goal', floatval($_POST['weekly_goal']));
        }
        if (isset($_POST['daily_goal'])) {
            update_option('fs_daily_goal', floatval($_POST['daily_goal']));
        }
        if (isset($_POST['work_days'])) {
            $work_days = array_map('sanitize_text_field', $_POST['work_days']);
            update_option('fs_work_days', $work_days);
        } elseif ($active_tab === 'time') {
            update_option('fs_work_days', array());
        }
        if (isset($_POST['default_activity'])) {
            update_option('fs_default_activity', sanitize_text_field(wp_unslash($_POST['default_activity'])));
        }
        if (isset($_POST['auto_archive_completed_tasks'])) {
            update_option('fs_auto_archive_completed_tasks', intval($_POST['auto_archive_completed_tasks']));
        }
        if (isset($_POST['custom_activities'])) {
            $custom_activities = array_filter(array_map('sanitize_text_field', $_POST['custom_activities']));
            update_option('fs_custom_activities', array_values($custom_activities));
        } elseif ($active_tab === 'time') {
            update_option('fs_custom_activities', array());
        }
        if ($active_tab === 'time') {
            update_option('fs_timer_warning_enabled', isset($_POST['timer_warning_enabled']) ? '1' : '0');
        }
        if (isset($_POST['timer_warning_hours'])) {
            update_option('fs_timer_warning_hours', intval($_POST['timer_warning_hours']));
        }
        if ($active_tab === 'time') {
            update_option('fs_keyboard_shortcuts', isset($_POST['keyboard_shortcuts']) ? '1' : '0');
            update_option('fs_browser_notifications', isset($_POST['browser_notifications']) ? '1' : '0');
        }
        
        // Tax Settings
        if (isset($_POST['tax_state'])) {
            update_option('fs_tax_state', sanitize_text_field(wp_unslash($_POST['tax_state'])));
        }
        if (isset($_POST['federal_tax_bracket'])) {
            update_option('fs_federal_tax_bracket', intval($_POST['federal_tax_bracket']));
        }
        if (isset($_POST['default_sales_tax_rate'])) {
            update_option('fs_default_sales_tax_rate', floatval($_POST['default_sales_tax_rate']));
        }
        if ($active_tab === 'invoices' || $active_tab === 'tax') {
            update_option('fs_auto_apply_sales_tax', isset($_POST['auto_apply_sales_tax']) ? '1' : '0');
        }
        
        // Payment Reminder Settings
        if ($active_tab === 'invoices') {
            update_option('fs_reminder_enabled', isset($_POST['reminder_enabled']) ? '1' : '0');
        }
        if (isset($_POST['reminder_before_due'])) {
            update_option('fs_reminder_before_due', intval($_POST['reminder_before_due']));
        }
        if (isset($_POST['reminder_after_due_1'])) {
            update_option('fs_reminder_after_due_1', intval($_POST['reminder_after_due_1']));
        }
        if (isset($_POST['reminder_after_due_2'])) {
            update_option('fs_reminder_after_due_2', intval($_POST['reminder_after_due_2']));
        }
        if (isset($_POST['reminder_after_due_3'])) {
            update_option('fs_reminder_after_due_3', intval($_POST['reminder_after_due_3']));
        }
        if (isset($_POST['reminder_subject_before'])) {
            update_option('fs_reminder_subject_before', sanitize_text_field(wp_unslash($_POST['reminder_subject_before'])));
        }
        if (isset($_POST['reminder_subject_overdue'])) {
            update_option('fs_reminder_subject_overdue', sanitize_text_field(wp_unslash($_POST['reminder_subject_overdue'])));
        }
        
        // Clean buffer and redirect
        ob_end_clean();
        FS_Admin::safe_redirect(admin_url('admin.php?page=fs-settings&tab=' . $active_tab . '&message=saved'));
        exit;
    }
    
    /**
     * Early save client (runs in admin_init before any output)
     */
    private function early_save_client() {
        if (!wp_verify_nonce($_POST['fs_client_nonce'], 'fs_save_client')) {
            ob_end_clean();
            return;
        }
        
        $action = isset($_POST['action']) ? sanitize_text_field(wp_unslash($_POST['action'])) : '';
        $id = isset($_POST['client_id']) ? intval($_POST['client_id']) : 0;

        $data = array(
            'company_name' => isset($_POST['company_name']) ? sanitize_text_field(wp_unslash($_POST['company_name'])) : '',
            'name' => isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '',
            'email' => isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '',
            'phone' => isset($_POST['phone']) ? sanitize_text_field(wp_unslash($_POST['phone'])) : '',
            'website' => isset($_POST['website']) ? esc_url_raw($_POST['website']) : '',
            'address' => isset($_POST['address']) ? sanitize_text_field(wp_unslash($_POST['address'])) : '',
            'address_2' => isset($_POST['address_2']) ? sanitize_text_field(wp_unslash($_POST['address_2'])) : '',
            'city' => isset($_POST['city']) ? sanitize_text_field(wp_unslash($_POST['city'])) : '',
            'state' => isset($_POST['state']) ? sanitize_text_field(wp_unslash($_POST['state'])) : '',
            'zip' => isset($_POST['zip']) ? sanitize_text_field(wp_unslash($_POST['zip'])) : '',
            'country' => isset($_POST['country']) ? sanitize_text_field(wp_unslash($_POST['country'])) : '',
            'notes' => isset($_POST['notes']) ? sanitize_textarea_field(wp_unslash($_POST['notes'])) : '',
            'logo_id' => isset($_POST['logo_id']) && $_POST['logo_id'] ? intval($_POST['logo_id']) : null,
            'logo_url' => isset($_POST['logo_url']) && $_POST['logo_url'] ? esc_url_raw($_POST['logo_url']) : null,
            'default_hourly_rate' => isset($_POST['default_hourly_rate']) ? floatval($_POST['default_hourly_rate']) : null,
            'default_payment_terms' => isset($_POST['default_payment_terms']) ? intval($_POST['default_payment_terms']) : 30,
            'portal_enabled' => isset($_POST['portal_enabled']) ? 1 : 0,
            'is_internal' => isset($_POST['is_internal']) ? 1 : 0,
            'status' => isset($_POST['status']) ? sanitize_text_field(wp_unslash($_POST['status'])) : 'active',
        );

        ob_end_clean();
        
        if ($action === 'update' && $id) {
            $result = FS_Client::update($id, $data);
            $message = $result !== false ? 'updated' : 'error';
            
            // Handle contacts
            $this->early_save_contacts($id);
            
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-clients&action=edit&id=' . $id . '&message=' . $message));
        } else {
            $client_id = FS_Client::create($data);
            if ($client_id) {
                $this->early_save_contacts($client_id);
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-clients&action=edit&id=' . $client_id . '&message=created'));
            } else {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-clients&action=add&message=error'));
            }
        }
        exit;
    }
    
    /**
     * Early save contacts helper
     */
    private function early_save_contacts($client_id) {
        if (!isset($_POST['contacts']) || !is_array($_POST['contacts'])) {
            return;
        }
        
        $existing_contact_ids = array();
        
        foreach ($_POST['contacts'] as $contact_data) {
            if (empty($contact_data['name']) && empty($contact_data['email'])) {
                continue;
            }
            
            $data = array(
                'client_id' => $client_id,
                'name' => sanitize_text_field($contact_data['name'] ?? ''),
                'email' => sanitize_email($contact_data['email'] ?? ''),
                'phone' => sanitize_text_field($contact_data['phone'] ?? ''),
                'role' => sanitize_text_field($contact_data['role'] ?? ''),
                'is_primary' => !empty($contact_data['is_primary']) && $contact_data['is_primary'] !== '0' ? 1 : 0,
                'is_billing_contact' => !empty($contact_data['is_billing_contact']) ? 1 : 0,
                'is_project_contact' => !empty($contact_data['is_project_contact']) ? 1 : 0,
            );
            
            $contact_id = isset($contact_data['id']) ? intval($contact_data['id']) : 0;
            
            if ($contact_id) {
                FS_Client_Contact::update($contact_id, $data);
                $existing_contact_ids[] = $contact_id;
            } else {
                $new_id = FS_Client_Contact::create($data);
                if ($new_id) {
                    $existing_contact_ids[] = $new_id;
                }
            }
        }
        
        // Delete contacts that were removed
        $all_contacts = FS_Client_Contact::get_by_client($client_id);
        foreach ($all_contacts as $contact) {
            if (!in_array(intval($contact->id), $existing_contact_ids)) {
                FS_Client_Contact::delete($contact->id);
            }
        }
    }
    
    /**
     * Early save project (runs in admin_init before any output)
     */
    private function early_save_project() {
        if (!wp_verify_nonce($_POST['fs_project_nonce'], 'fs_save_project')) {
            ob_end_clean();
            return;
        }
        
        $action = isset($_POST['action']) ? sanitize_text_field(wp_unslash($_POST['action'])) : '';
        $id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;

        // Process services (v3.9.0: include retainer fields)
        $services = array();
        if (!empty($_POST['services']) && is_array($_POST['services'])) {
            foreach ($_POST['services'] as $svc) {
                if (!empty($svc['name'])) {
                    $service_data = array(
                        'name' => sanitize_text_field($svc['name']),
                        'description' => sanitize_textarea_field($svc['description'] ?? ''),
                        'pricing_type' => sanitize_text_field($svc['pricing_type'] ?? 'fixed'),
                        'hourly_source' => sanitize_text_field($svc['hourly_source'] ?? 'estimated'),
                        'rate' => floatval($svc['rate'] ?? 0),
                        'quantity' => floatval($svc['quantity'] ?? 1),
                        'amount' => floatval($svc['amount'] ?? 0),
                    );
                    
                    // v3.9.0: Include retainer-specific fields if pricing type is retainer
                    if ($service_data['pricing_type'] === 'retainer') {
                        $service_data['retainer_amount'] = floatval($svc['retainer_amount'] ?? 0);
                        $service_data['retainer_hours'] = floatval($svc['retainer_hours'] ?? 0);
                        // Checkbox: if not set, it means unchecked (0), if set it means checked (1)
                        $service_data['retainer_rollover'] = isset($svc['retainer_rollover']) ? 1 : 0;
                        $service_data['retainer_overage_rate'] = floatval($svc['retainer_overage_rate'] ?? 0);
                    }
                    
                    $services[] = $service_data;
                }
            }
        }

        // Handle deposit settings
        $deposit_required = isset($_POST['deposit_required']) && $_POST['deposit_required'];
        $deposit_type = sanitize_text_field(wp_unslash($_POST['deposit_type'] ?? 'percentage'));
        $deposit_percentage = 0;
        $deposit_amount = 0;
        
        if ($deposit_required) {
            if ($deposit_type === 'percentage') {
                $deposit_percentage = floatval($_POST['deposit_percentage'] ?? 0);
                $total = floatval($_POST['fixed_price'] ?? 0);
                $deposit_amount = $total * ($deposit_percentage / 100);
            } else {
                $deposit_amount = floatval($_POST['deposit_fixed_amount'] ?? 0);
            }
        }

        // v3.9.0: Extract retainer settings from services for backwards compatibility
        $is_retainer = 0;
        $retainer_amount = null;
        $retainer_hours = null;
        $retainer_rollover = 1;
        
        foreach ($services as $service) {
            if (isset($service['pricing_type']) && $service['pricing_type'] === 'retainer') {
                $is_retainer = 1;
                $retainer_amount = $service['retainer_amount'] ?? null;
                $retainer_hours = $service['retainer_hours'] ?? null;
                $retainer_rollover = $service['retainer_rollover'] ?? 1;
                break;
            }
        }

        $data = array(
            'client_id' => intval($_POST['client_id']),
            'name' => sanitize_text_field(wp_unslash($_POST['name'])),
            'description' => sanitize_textarea_field(wp_unslash($_POST['description'])),
            'status' => sanitize_text_field(wp_unslash($_POST['status'])),
            'billing_type' => sanitize_text_field(wp_unslash($_POST['billing_type'])),
            'estimated_hours' => isset($_POST['estimated_hours']) ? floatval($_POST['estimated_hours']) : null,
            'fixed_price' => isset($_POST['fixed_price']) ? floatval($_POST['fixed_price']) : null,
            'budget' => isset($_POST['budget']) ? floatval($_POST['budget']) : null,
            'deposit_type' => $deposit_type,
            'deposit_percentage' => $deposit_percentage,
            'deposit_amount' => $deposit_amount,
            'start_date' => !empty($_POST['start_date']) ? sanitize_text_field(wp_unslash($_POST['start_date'])) : null,
            'due_date' => !empty($_POST['due_date']) ? sanitize_text_field(wp_unslash($_POST['due_date'])) : null,
            'is_retainer' => $is_retainer,
            'retainer_amount' => $retainer_amount,
            'retainer_hours' => $retainer_hours,
            'retainer_rollover' => $retainer_rollover,
            'notes' => sanitize_textarea_field(wp_unslash($_POST['notes'])),
            'services' => $services,
            'billing_frequency' => sanitize_text_field(wp_unslash($_POST['billing_frequency'] ?? 'once')),
            'first_invoice_date' => !empty($_POST['first_invoice_date']) ? sanitize_text_field(wp_unslash($_POST['first_invoice_date'])) : null,
        );

        ob_end_clean();
        
        if ($action === 'update' && $id) {
            $result = FS_Project::update($id, $data);
            $message = $result !== false ? 'updated' : 'error';
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-projects&action=edit&id=' . $id . '&message=' . $message));
        } else {
            $project_id = FS_Project::create($data);
            if ($project_id) {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-projects&action=edit&id=' . $project_id . '&message=created'));
            } else {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-projects&action=add&message=error'));
            }
        }
        exit;
    }
    
    /**
    /**
     * Render a premium feature upgrade notice page
     * 
     * @param string $feature Feature key from fs_studio_upgrade_notice registry
     */
    public function render_premium_required( $feature = '' ) {
        $messages = array(
            'booking'    => array( 'title' => __( 'Client Booking & Scheduling', 'freelance-studio' ), 'desc' => __( 'Let clients book meetings directly from your website with Google Calendar sync and automatic Google Meet links.', 'freelance-studio' ), 'icon' => '📅' ),
            'recurring'  => array( 'title' => __( 'Recurring Invoices', 'freelance-studio' ), 'desc' => __( 'Automate your billing with recurring invoice schedules for retainer clients and ongoing projects.', 'freelance-studio' ), 'icon' => '🔄' ),
            'templates'  => array( 'title' => __( 'Project Templates', 'freelance-studio' ), 'desc' => __( 'Create reusable project templates with pre-configured tasks, services, and settings to speed up your workflow.', 'freelance-studio' ), 'icon' => '📋' ),
            'gcal'       => array( 'title' => __( 'Google Calendar Integration', 'freelance-studio' ), 'desc' => __( 'Two-way sync your project dates, invoice due dates, and bookings with Google Calendar.', 'freelance-studio' ), 'icon' => '📆' ),
        );
        $info = $messages[ $feature ] ?? array( 'title' => __( 'Pro Feature', 'freelance-studio' ), 'desc' => __( 'This feature is available with Freelance Studio Pro.', 'freelance-studio' ), 'icon' => '⭐' );
        $upgrade_url = function_exists( 'freelance_studio_fs' ) ? freelance_studio_fs()->get_upgrade_url() : '#';
        ?>
        <div class="wrap fs-wrap">
            <div class="fs-premium-required">
                <div class="fs-premium-required-icon"><?php echo  esc_html( $info['icon'] ); ?></div>
                <h1><?php echo esc_html( $info['title'] ); ?></h1>
                <p><?php echo esc_html( $info['desc'] ); ?></p>
                <a href="<?php echo esc_url( $upgrade_url ); ?>" class="fs-btn fs-btn-primary" style="margin-top:16px;">
                    <?php esc_html_e( 'Upgrade to Pro', 'freelance-studio' ); ?>
                </a>
                <p class="fs-premium-help">
                    <?php esc_html_e( 'All Pro features include a 14-day free trial — no credit card required.', 'freelance-studio' ); ?>
                </p>
            </div>
        </div>
        <?php
    }

    /**
     * Handle file exports before any output
     */
    public function handle_exports() {
        // Handle PDF generation for proposals, contracts, and invoices EARLY before any output
        // This prevents "headers already sent" errors
        $page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : '';
        $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
        
        if ($action === 'pdf' && $id > 0) {
            // Clean ALL output buffers before generating PDF
            while (ob_get_level()) {
                ob_end_clean();
            }
            
            switch ($page) {
                case 'fs-proposals':
                    $this->early_generate_proposal_pdf($id);
                    break;
                case 'fs-contracts':
                    $this->early_generate_contract_pdf($id);
                    break;
                case 'fs-invoices':
                case 'fs-finances':
                    $this->early_generate_invoice_pdf($id);
                    break;
            }
        }
        
        // Tax deduction export (from Expenses page, Reporting page, or Finances page)
        if (isset($_GET['export']) && $_GET['export'] === 'tax_deductions') {
            if ($page === 'fs-expenses' || $page === 'fs-reporting' || $page === 'fs-finances') {
                $format = isset($_GET['format']) ? $_GET['format'] : 'csv';
                if ($format === 'pdf') {
                    $this->export_tax_deductions_pdf();
                } else {
                    $this->export_tax_deductions_csv();
                }
            }
        }
    }
    
    /**
     * Generate proposal PDF early (before any output)
     */
    private function early_generate_proposal_pdf($id) {
        $proposal = FS_Proposal::get($id);
        
        if (!$proposal) {
            wp_die(esc_html__('Proposal not found', 'freelance-studio'));
        }
        
        $client = FS_Client::get($proposal->client_id);
        
        FS_PDF_Service::generate_proposal($proposal, $client);
        exit; // Ensure we stop after PDF output
    }
    
    /**
     * Generate contract PDF early (before any output)
     */
    private function early_generate_contract_pdf($id) {
        $contract = FS_Contract::get($id);
        
        if (!$contract) {
            wp_die(esc_html__('Contract not found', 'freelance-studio'));
        }
        
        $client = FS_Client::get($contract->client_id);
        
        FS_PDF_Service::generate_contract($contract, $client);
        exit; // Ensure we stop after PDF output
    }
    
    /**
     * Generate invoice PDF early (before any output)
     */
    private function early_generate_invoice_pdf($id) {
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            wp_die(esc_html__('Invoice not found', 'freelance-studio'));
        }
        
        $client = FS_Client::get($invoice->client_id);
        $project = $invoice->project_id ? FS_Project::get($invoice->project_id) : null;
        
        FS_PDF_Service::generate_invoice($invoice, $client, $project);
        exit; // Ensure we stop after PDF output
    }

    /**
     * Export tax deductions as CSV
     */
    private function export_tax_deductions_csv() {
        $year = isset($_GET['year']) ? intval($_GET['year']) : gmdate('Y');
        
        // Get expense categories
        $expense_categories = FS_Expense::get_by_category(
            $year . '-01-01',
            $year . '-12-31'
        );
        
        // Get annual deductions
        $annual_deductions = FS_Annual_Deduction::get_by_category($year);
        
        // Get totals
        $total_tracked_expenses = 0;
        foreach ($expense_categories as $cat) {
            $total_tracked_expenses += floatval($cat->total);
        }
        
        $total_annual_deductions = FS_Annual_Deduction::get_total($year);
        
        // Set headers for CSV download
        $filename = 'tax-deduction-summary-' . $year . '.csv';
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Pragma: no-cache');
        header('Expires: 0');
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Using PHP stream wrapper for in-memory CSV generation.
        $output = fopen('php://output', 'w');
        
        // Business info header
        $business_name = get_option('fs_business_name', get_bloginfo('name'));
        fputcsv($output, array('TAX DEDUCTION SUMMARY'));
        fputcsv($output, array('Business:', $business_name));
        fputcsv($output, array('Tax Year:', $year));
        fputcsv($output, array('Generated:', gmdate('F j, Y')));
        fputcsv($output, array('')); // Empty row
        
        // Disclaimer
        fputcsv($output, array('IMPORTANT DISCLAIMER:'));
        fputcsv($output, array('This report is for informational purposes only and does not constitute tax advice.'));
        fputcsv($output, array('These figures are estimates based on data entered into the system.'));
        fputcsv($output, array('Please consult with a qualified tax professional for accurate tax preparation.'));
        fputcsv($output, array('')); // Empty row
        
        // Section 1: Tracked Business Expenses
        fputcsv($output, array(''));
        fputcsv($output, array('SECTION 1: TRACKED BUSINESS EXPENSES'));
        fputcsv($output, array('Category', 'Amount', 'Number of Transactions'));
        
        foreach ($expense_categories as $cat) {
            fputcsv($output, array(
                $cat->category,
                '$' . number_format($cat->total, 2),
                $cat->count
            ));
        }
        
        fputcsv($output, array(''));
        fputcsv($output, array('Subtotal - Tracked Expenses:', '$' . number_format($total_tracked_expenses, 2)));
        fputcsv($output, array('')); // Empty row
        
        // Section 2: Annual Estimated Deductions
        fputcsv($output, array(''));
        fputcsv($output, array('SECTION 2: ANNUAL ESTIMATED DEDUCTIONS'));
        fputcsv($output, array('Category', 'Amount', 'Number of Items'));
        
        if (!empty($annual_deductions)) {
            foreach ($annual_deductions as $deduction) {
                fputcsv($output, array(
                    $deduction->category,
                    '$' . number_format($deduction->total, 2),
                    $deduction->count
                ));
            }
        } else {
            fputcsv($output, array('No annual deductions recorded', '', ''));
        }
        
        fputcsv($output, array(''));
        fputcsv($output, array('Subtotal - Annual Deductions:', '$' . number_format($total_annual_deductions, 2)));
        fputcsv($output, array('')); // Empty row
        
        // Section 3: Summary
        fputcsv($output, array(''));
        fputcsv($output, array('SUMMARY'));
        fputcsv($output, array('Tracked Business Expenses:', '$' . number_format($total_tracked_expenses, 2)));
        fputcsv($output, array('Annual Estimated Deductions:', '$' . number_format($total_annual_deductions, 2)));
        fputcsv($output, array(''));
        fputcsv($output, array('TOTAL POTENTIAL DEDUCTIONS:', '$' . number_format($total_tracked_expenses + $total_annual_deductions, 2)));
        
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closing stream/file handle after CSV generation.
        fclose($output);
        exit;
    }

    /**
     * Export tax deductions as PDF
     */
    private function export_tax_deductions_pdf() {
        $year = isset($_GET['year']) ? intval($_GET['year']) : gmdate('Y');
        
        // Get expense categories
        $expense_categories = FS_Expense::get_by_category(
            $year . '-01-01',
            $year . '-12-31'
        );
        
        // Get annual deductions
        $annual_deductions = FS_Annual_Deduction::get_by_category($year);
        
        // Get totals
        $total_tracked_expenses = 0;
        foreach ($expense_categories as $cat) {
            $total_tracked_expenses += floatval($cat->total);
        }
        
        $total_annual_deductions = FS_Annual_Deduction::get_total($year);
        
        $business_name = get_option('fs_business_name', get_bloginfo('name'));
        
        // Build HTML for PDF
        $html = '
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <style>
                body { font-family: Arial, sans-serif; font-size: 12px; color: #333; margin: 40px; }
                h1 { color: #1e3a5f; font-size: 24px; margin-bottom: 5px; }
                h2 { color: #2c5282; font-size: 16px; margin-top: 30px; border-bottom: 2px solid #2c5282; padding-bottom: 5px; }
                .header-info { margin-bottom: 20px; }
                .header-info p { margin: 3px 0; }
                .disclaimer { background: #fef3c7; border: 1px solid #f59e0b; padding: 15px; margin: 20px 0; border-radius: 5px; }
                .disclaimer strong { color: #92400e; }
                table { width: 100%; border-collapse: collapse; margin: 15px 0; }
                th { background: #f1f5f9; text-align: left; padding: 10px; border-bottom: 2px solid #cbd5e1; }
                td { padding: 8px 10px; border-bottom: 1px solid #e2e8f0; }
                .text-right { text-align: right; }
                .subtotal { background: #f8fafc; font-weight: bold; }
                .summary-box { background: #ecfdf5; border: 1px solid #10b981; padding: 15px; margin-top: 30px; border-radius: 5px; }
                .summary-box h3 { margin: 0 0 10px 0; color: #065f46; }
                .total-line { font-size: 16px; font-weight: bold; color: #065f46; margin-top: 10px; padding-top: 10px; border-top: 2px solid #10b981; }
            </style>
        </head>
        <body>
            <h1>Tax Deduction Summary</h1>
            <div class="header-info">
                <p><strong>Business:</strong> ' . esc_html($business_name) . '</p>
                <p><strong>Tax Year:</strong> ' . $year . '</p>
                <p><strong>Generated:</strong> ' . gmdate('F j, Y') . '</p>
            </div>
            
            <div class="disclaimer">
                <strong>Important Disclaimer:</strong> This report is for informational purposes only and does not constitute tax advice. These figures are estimates based on data entered into the system. Please consult with a qualified tax professional for accurate tax preparation.
            </div>
            
            <h2>Section 1: Tracked Business Expenses</h2>
            <table>
                <thead>
                    <tr>
                        <th>Category</th>
                        <th class="text-right">Amount</th>
                        <th class="text-right">Transactions</th>
                    </tr>
                </thead>
                <tbody>';
        
        foreach ($expense_categories as $cat) {
            $html .= '<tr>
                <td>' . esc_html($cat->category) . '</td>
                <td class="text-right">$' . number_format($cat->total, 2) . '</td>
                <td class="text-right">' . $cat->count . '</td>
            </tr>';
        }
        
        $html .= '<tr class="subtotal">
                <td>Subtotal - Tracked Expenses</td>
                <td class="text-right">$' . number_format($total_tracked_expenses, 2) . '</td>
                <td></td>
            </tr>
            </tbody>
            </table>
            
            <h2>Section 2: Annual Estimated Deductions</h2>
            <table>
                <thead>
                    <tr>
                        <th>Category</th>
                        <th class="text-right">Amount</th>
                        <th class="text-right">Items</th>
                    </tr>
                </thead>
                <tbody>';
        
        if (!empty($annual_deductions)) {
            foreach ($annual_deductions as $deduction) {
                $html .= '<tr>
                    <td>' . esc_html($deduction->category) . '</td>
                    <td class="text-right">$' . number_format($deduction->total, 2) . '</td>
                    <td class="text-right">' . $deduction->count . '</td>
                </tr>';
            }
        } else {
            $html .= '<tr><td colspan="3" style="color: #6b7280; font-style: italic;">No annual deductions recorded</td></tr>';
        }
        
        $html .= '<tr class="subtotal">
                <td>Subtotal - Annual Deductions</td>
                <td class="text-right">$' . number_format($total_annual_deductions, 2) . '</td>
                <td></td>
            </tr>
            </tbody>
            </table>
            
            <div class="summary-box">
                <h3>Summary</h3>
                <p>Tracked Business Expenses: <strong>$' . number_format($total_tracked_expenses, 2) . '</strong></p>
                <p>Annual Estimated Deductions: <strong>$' . number_format($total_annual_deductions, 2) . '</strong></p>
                <p class="total-line">Total Potential Deductions: $' . number_format($total_tracked_expenses + $total_annual_deductions, 2) . '</p>
            </div>
        </body>
        </html>';
        
        // Generate PDF using FS_PDF
        if (class_exists('FS_PDF')) {
            $pdf = new FS_PDF('P', 'mm', 'A4');
            $pdf->SetMargins(15, 15, 15);
            $pdf->SetAutoPageBreak(true, 15);
            $pdf->SetTitle('Tax Deduction Summary - ' . $year);
            $pdf->AddPage();
            $pdf->WriteHTML($html);
            $pdf->Output('tax-deduction-summary-' . $year . '.pdf', 'D');
            exit;
        }
        
        // Fallback: Output as downloadable HTML that opens print dialog
        $filename = 'tax-deduction-summary-' . $year . '.html';
        header('Content-Type: text/html; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Internally-built HTML document
        echo '<script>window.onload = function() { window.print(); }</script>';
        exit;
    }

    /**
     * Register the admin menu.
     */
    public function add_admin_menu() {
        // Main menu
        add_menu_page(
            __('Freelance Studio', 'freelance-studio'),
            __('Freelance Studio', 'freelance-studio'),
            'manage_options',
            'freelance-studio',
            array($this, 'render_dashboard'),
            'dashicons-portfolio',
            30
        );

        // Dashboard submenu (same as main)
        add_submenu_page(
            'freelance-studio',
            __('Dashboard', 'freelance-studio'),
            __('Dashboard', 'freelance-studio'),
            'manage_options',
            'freelance-studio',
            array($this, 'render_dashboard')
        );

        // Clients
        add_submenu_page(
            'freelance-studio',
            __('Clients', 'freelance-studio'),
            __('Clients', 'freelance-studio'),
            'manage_options',
            'fs-clients',
            array($this, 'render_clients')
        );

        // Projects
        add_submenu_page(
            'freelance-studio',
            __('Projects', 'freelance-studio'),
            __('Projects', 'freelance-studio'),
            'manage_options',
            'fs-projects',
            array($this, 'render_projects')
        );

        // Tasks
        add_submenu_page(
            'freelance-studio',
            __('Tasks', 'freelance-studio'),
            __('Tasks', 'freelance-studio'),
            'manage_options',
            'fs-tasks',
            array($this, 'render_tasks')
        );

        // Proposals
        add_submenu_page(
            'freelance-studio',
            __('Proposals', 'freelance-studio'),
            __('Proposals', 'freelance-studio'),
            'manage_options',
            'fs-proposals',
            array($this, 'render_proposals')
        );

        // Contracts
        add_submenu_page(
            'freelance-studio',
            __('Contracts', 'freelance-studio'),
            __('Contracts', 'freelance-studio'),
            'manage_options',
            'fs-contracts',
            array($this, 'render_contracts')
        );

        // Finances (Invoices, Recurring, Other Income, Expenses, Reporting, Tax)
        add_submenu_page(
            'freelance-studio',
            __('Finances', 'freelance-studio'),
            __('Finances', 'freelance-studio'),
            'manage_options',
            'fs-finances',
            array($this, 'render_finances')
        );

        // Time Tracking
        add_submenu_page(
            'freelance-studio',
            __('Time Tracking', 'freelance-studio'),
            __('Time Tracking', 'freelance-studio'),
            'manage_options',
            'fs-time-tracking',
            array($this, 'render_time_tracking')
        );

        // Bookings
        $bookings_label = __('Bookings', 'freelance-studio');
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            $bookings_label .= ' <span class="fs-menu-pro-badge">PRO</span>';
        }
        add_submenu_page(
            'freelance-studio',
            __('Bookings', 'freelance-studio'),
            $bookings_label,
            'manage_options',
            'fs-bookings',
            array($this, 'render_bookings')
        );

        // Forms
        $forms_label = __('Forms', 'freelance-studio');
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            $forms_label .= ' <span class="fs-menu-pro-badge">PRO</span>';
        }
        add_submenu_page(
            'freelance-studio',
            __('Forms', 'freelance-studio'),
            $forms_label,
            'manage_options',
            'fs-forms',
            array($this, 'render_forms')
        );

        // Calendar
        add_submenu_page(
            'freelance-studio',
            __('Calendar', 'freelance-studio'),
            __('Calendar', 'freelance-studio'),
            'manage_options',
            'fs-calendar',
            array($this, 'render_calendar')
        );

        // Settings (includes Templates, Contract Sections, License as tabs)
        add_submenu_page(
            'freelance-studio',
            __('Settings', 'freelance-studio'),
            __('Settings', 'freelance-studio'),
            'manage_options',
            'fs-settings',
            array($this, 'render_settings')
        );
    }

    /**
     * Register the stylesheets for the admin area.
     */
    /**
     * Add body classes for plugin features like high contrast mode
     */
    public function add_body_classes($classes) {
        if (get_option('fs_high_contrast_mode', '0') === '1') {
            $classes .= ' fs-high-contrast';
        }
        return $classes;
    }

    public function enqueue_styles($hook) {
        // Only load on our plugin pages
        if (strpos($hook, 'freelance-studio') === false && strpos($hook, 'fs-') === false) {
            return;
        }

        wp_enqueue_style(
            $this->plugin_name,
            JEEB_FS_PLUGIN_URL . 'admin/css/freelance-studio.css',
            array(),
            $this->version,
            'all'
        );

        // Datepicker CSS
        wp_enqueue_style('jquery-ui-datepicker-style', // phpcs:ignore PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent
            'https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css', // phpcs:ignore PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent
            array(),
            '1.13.2'
        );
    }

    /**
     * Register the JavaScript for the admin area.
     */
    public function enqueue_scripts($hook) {
        // Only load on our plugin pages
        if (strpos($hook, 'freelance-studio') === false && strpos($hook, 'fs-') === false) {
            return;
        }

        // jQuery UI for datepicker and sortable
        wp_enqueue_script('jquery-ui-datepicker');
        wp_enqueue_script('jquery-ui-sortable');

        // Main admin script
        wp_enqueue_script(
            $this->plugin_name,
            JEEB_FS_PLUGIN_URL . 'admin/js/freelance-studio-admin.js',
            array('jquery', 'jquery-ui-datepicker', 'jquery-ui-sortable'),
            $this->version,
            true
        );

        // Unified task modal script
        wp_enqueue_script(
            'fs-task-modal',
            JEEB_FS_PLUGIN_URL . 'admin/js/fs-task-modal.js',
            array('jquery'),
            $this->version,
            true
        );

        // Localize script with AJAX URL and nonce
        wp_localize_script(
            $this->plugin_name,
            'fs_ajax',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('fs_ajax_nonce'),
                'admin_url' => admin_url(),
                'currency_symbol' => '$',
                'date_format' => get_option('date_format', 'Y-m-d'),
                'strings' => array(
                    'confirm_delete' => __('Are you sure you want to delete this item? This cannot be undone.', 'freelance-studio'),
                    'deleting' => __('Deleting...', 'freelance-studio'),
                    'saving' => __('Saving...', 'freelance-studio'),
                    'error' => __('An error occurred. Please try again.', 'freelance-studio'),
                    'success' => __('Success!', 'freelance-studio'),
                    'send_email' => __('Send Email', 'freelance-studio'),
                    'send_invoice' => __('Send Invoice', 'freelance-studio'),
                    'send_proposal' => __('Send Proposal', 'freelance-studio'),
                    'sending' => __('Sending...', 'freelance-studio'),
                    'cancel' => __('Cancel', 'freelance-studio'),
                    'custom_message_label' => __('Add a personal message (optional)', 'freelance-studio'),
                    'custom_message_placeholder' => __('Write a personal message to include with this email...', 'freelance-studio'),
                    'send_without_message' => __('Send without message', 'freelance-studio'),
                    'no_expenses' => __('No billable expenses available for this project.', 'freelance-studio'),
                )
            )
        );
        
        // Settings for notifications and shortcuts
        wp_localize_script(
            $this->plugin_name,
            'fs_settings',
            array(
                'keyboard_shortcuts' => get_option('fs_keyboard_shortcuts', '1') === '1',
                'browser_notifications' => get_option('fs_browser_notifications', '1') === '1',
                'timer_warning_hours' => intval(get_option('fs_timer_warning_hours', 8)),
                'timer_warning_enabled' => get_option('fs_timer_warning_enabled', '1') === '1',
                'plugin_url' => plugins_url('', dirname(__FILE__)),
            )
        );

        // Media uploader for receipts
        if (strpos($hook, 'fs-expenses') !== false) {
            wp_enqueue_media();
        }
        
        // Media uploader for client logos
        if (strpos($hook, 'fs-clients') !== false) {
            wp_enqueue_media();
        }
        
        // Media uploader for business logo and signature
        if (strpos($hook, 'fs-settings') !== false) {
            wp_enqueue_media();
        }
        
        // Editor for proposals custom sections
        if (strpos($hook, 'fs-proposals') !== false) {
            wp_enqueue_editor();
        }
        
        // Editor for project notes tab
        if (strpos($hook, 'fs-projects') !== false) {
            wp_enqueue_editor();
        }
    }

    /**
     * Render Dashboard page
     */
    public function render_dashboard() {
        $controller = new FS_Dashboard_Controller();
        $controller->index();
    }

    /**
     * Render Clients page
     */
    public function render_clients() {
        $controller = new FS_Clients_Controller();
        $controller->handle_request();
    }

    /**
     * Render Projects page
     */
    public function render_projects() {
        $controller = new FS_Projects_Controller();
        $controller->handle_request();
    }

    /**
     * Render Tasks page
     */
    public function render_tasks() {
        $view = isset($_GET['view']) ? sanitize_text_field($_GET['view']) : '';
        
        // Remember the user's view preference
        if ($view) {
            update_user_meta(get_current_user_id(), 'fs_tasks_view', $view);
        } else {
            $view = get_user_meta(get_current_user_id(), 'fs_tasks_view', true);
            if (!$view) {
                $view = 'kanban';
            }
        }
        
        if ($view === 'list') {
            include JEEB_FS_PLUGIN_DIR . 'includes/views/tasks/list.php';
        } else {
            include JEEB_FS_PLUGIN_DIR . 'includes/views/tasks/kanban.php';
        }
    }

    /**
     * Render Proposals page
     */
    public function render_proposals() {
        $controller = new FS_Proposals_Controller();
        $controller->handle_request();
    }

    /**
     * Render Contracts page
     */
    public function render_contracts() {
        $controller = new FS_Contracts_Controller();
        $controller->handle_request();
    }

    /**
     * Render Finances page (unified financial hub)
     */
    public function render_finances() {
        $controller = new FS_Finances_Controller();
        $controller->handle_request();
    }

    /**
     * Render Invoices page
     */
    public function render_invoices() {
        $controller = new FS_Invoices_Controller();
        $controller->handle_request();
    }

    /**
     * Render Recurring Invoices page
     */
    public function render_recurring_invoices() {
        // Recurring invoices is a premium-only feature
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            $this->render_premium_required( 'recurring' );
            return;
        }
        $controller = new FS_Recurring_Invoices_Controller();
        $controller->handle_request();
    }

    /**
     * Render Expenses page
     */
    public function render_expenses() {
        $controller = new FS_Expenses_Controller();
        $controller->handle_request();
    }

    /**
     * Render Time Tracking page
     */
    public function render_time_tracking() {
        $controller = new FS_Time_Tracking_Controller();
        $controller->handle_request();
    }

    /**
     * Render Bookings page
     */
    public function render_bookings() {
        // Bookings is a premium-only feature
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            $this->render_premium_required( 'booking' );
            return;
        }
        $controller = new FS_Booking_Controller();
        $controller->handle_request();
    }

    /**
     * Render Calendar page
     */
    public function render_calendar() {
        $controller = new FS_Calendar_Controller();
        $controller->handle_request();
    }

    /**
     * Render Reporting page
     */
    public function render_reporting() {
        $controller = new FS_Reporting_Controller();
        $controller->index();
    }

    /**
     * Render Project Templates page
     */
    public function render_project_templates() {
        // Project templates is a premium-only feature
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            $this->render_premium_required( 'templates' );
            return;
        }
        $controller = new FS_Project_Templates_Controller();
        $controller->handle_request();
    }

    /**
     * Render Settings page
     */
    public function render_settings() {
        $controller = new FS_Settings_Controller();
        $controller->handle_request();
    }
    
    /**
     * Render Forms page
     */
    public function render_forms() {
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            include JEEB_FS_PLUGIN_DIR . 'includes/views/partials/premium-required-page.php';
            return;
        }
        $controller = new FS_Forms_Controller();
        $controller->handle_request();
    }
    
    /**
     * Render Contract Sections page
     */
    public function render_contract_sections() {
        // Handle form submissions
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $this->handle_contract_sections_save();
        }
        
        $sections = FS_Contract_Section::get_all();
        $show_success = isset($_GET['message']) && $_GET['message'] === 'saved';
        $editing_id = isset($_GET['edit']) ? intval($_GET['edit']) : 0;
        $editing_section = $editing_id ? FS_Contract_Section::get($editing_id) : null;
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/settings/contract-sections.php';
    }
    
    /**
     * Handle saving contract section
     */
    private function handle_contract_sections_save() {
        if (!isset($_POST['fs_section_nonce']) || !wp_verify_nonce($_POST['fs_section_nonce'], 'fs_save_contract_section')) {
            wp_die(esc_html__('Security check failed', 'freelance-studio'));
        }
        
        $section_id = isset($_POST['section_id']) ? intval($_POST['section_id']) : 0;
        
        $data = array(
            'title' => sanitize_text_field(wp_unslash($_POST['section_title'])),
            'content' => wp_kses_post($_POST['section_content']),
            'is_active' => isset($_POST['is_active']) ? 1 : 0,
        );
        
        if ($section_id) {
            // Update existing
            FS_Contract_Section::update($section_id, $data);
        }
        
        FS_Admin::safe_redirect(admin_url('admin.php?page=fs-contract-sections&message=saved'));
        exit;
    }

    /**
     * Render bug report footer on all FS admin pages
     */
    /**
     * Output PWA meta tags for home screen bookmarks
     * Provides touch icons and web app manifest for mobile devices
     */
    public function render_pwa_meta() {
        $screen = get_current_screen();
        if (!$screen || (strpos($screen->id, 'fs-') === false && strpos($screen->id, 'freelance-studio') === false)) {
            return;
        }

        $icon_base = plugin_dir_url(dirname(__FILE__)) . 'admin/img/';
        $manifest_url = plugin_dir_url(dirname(__FILE__)) . 'admin/manifest.json';
        ?>
        <!-- Freelance Studio - Home Screen / PWA -->
        <link rel="apple-touch-icon" sizes="180x180" href="<?php echo esc_url($icon_base . 'icon-180x180.png'); ?>">
        <link rel="apple-touch-icon" sizes="152x152" href="<?php echo esc_url($icon_base . 'icon-152x152.png'); ?>">
        <link rel="apple-touch-icon" sizes="120x120" href="<?php echo esc_url($icon_base . 'icon-120x120.png'); ?>">
        <link rel="apple-touch-icon" sizes="76x76" href="<?php echo esc_url($icon_base . 'icon-76x76.png'); ?>">
        <link rel="icon" type="image/png" sizes="192x192" href="<?php echo esc_url($icon_base . 'icon-192x192.png'); ?>">
        <link rel="icon" type="image/png" sizes="32x32" href="<?php echo esc_url($icon_base . 'favicon-32x32.png'); ?>">
        <link rel="manifest" href="<?php echo esc_url($manifest_url); ?>">
        <meta name="theme-color" content="#1FBBA6">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="default">
        <meta name="apple-mobile-web-app-title" content="Freelance Studio">
        <?php
    }

    public function render_bug_report_footer() {
        $screen = get_current_screen();
        if (!$screen || strpos($screen->id, 'fs-') === false && strpos($screen->id, 'freelance-studio') === false) {
            return;
        }
        
        // Gather debug info
        global $wpdb;
        $debug_info = array(
            'Plugin Version' => defined('JEEB_FS_VERSION') ? JEEB_FS_VERSION : 'Unknown',
            'DB Version' => get_option('fs_version', 'Not set'),
            'PHP Version' => PHP_VERSION,
            'WordPress Version' => get_bloginfo('version'),
            'Theme' => wp_get_theme()->get('Name') . ' ' . wp_get_theme()->get('Version'),
            'Multisite' => is_multisite() ? 'Yes' : 'No',
            'Server' => isset($_SERVER['SERVER_SOFTWARE']) ? sanitize_text_field($_SERVER['SERVER_SOFTWARE']) : 'Unknown',
            'MySQL Version' => $wpdb->db_version(),
            'PHP Memory Limit' => ini_get('memory_limit'),
            'WP Memory Limit' => WP_MEMORY_LIMIT,
            'Max Upload Size' => size_format(wp_max_upload_size()),
        );
        $debug_string = '';
        foreach ($debug_info as $key => $value) {
            $debug_string .= $key . ': ' . $value . "\n";
        }
        
        $current_page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
        $current_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : '';
        $page_context = $current_page;
        if ($current_tab) {
            $page_context .= ' > ' . $current_tab;
        }
        ?>
        <div class="fs-bug-report-footer">
            <button type="button" id="fs-bug-report-trigger">
                <span class="dashicons dashicons-buddicons-replies"></span>
                <span class="fs-bug-report-label"><?php esc_html_e('Report a Bug', 'freelance-studio'); ?></span>
            </button>
        </div>

        <div class="fs-bug-report-overlay" id="fs-bug-report-overlay"></div>
        <div class="fs-bug-report-modal" id="fs-bug-report-modal">
            <div class="fs-bug-report-header">
                <h2><?php esc_html_e('Report a Bug', 'freelance-studio'); ?></h2>
                <button type="button" class="fs-bug-report-close" id="fs-bug-report-close">&times;</button>
            </div>
            <div class="fs-bug-report-body">
                <p class="fs-bug-report-intro"><?php esc_html_e('Help us improve Freelance Studio! Please describe the bug as clearly as possible.', 'freelance-studio'); ?></p>
                
                <form id="fs-bug-report-form" enctype="multipart/form-data">
                    <?php wp_nonce_field('fs_bug_report', 'fs_bug_report_nonce'); ?>
                    
                    <div class="fs-bug-field">
                        <label for="fs-bug-page"><?php esc_html_e('Page / Section', 'freelance-studio'); ?></label>
                        <input type="text" id="fs-bug-page" name="bug_page" value="<?php echo esc_attr($page_context); ?>" placeholder="<?php esc_attr_e('e.g. Projects > Kanban view', 'freelance-studio'); ?>">
                    </div>

                    <div class="fs-bug-field">
                        <label for="fs-bug-doing"><?php esc_html_e('What were you doing?', 'freelance-studio'); ?></label>
                        <textarea id="fs-bug-doing" name="bug_doing" rows="2" placeholder="<?php esc_attr_e('e.g. I was adding a new time entry to the Falcon project...', 'freelance-studio'); ?>"></textarea>
                    </div>

                    <div class="fs-bug-field">
                        <label for="fs-bug-expected"><?php esc_html_e('What should have happened?', 'freelance-studio'); ?></label>
                        <textarea id="fs-bug-expected" name="bug_expected" rows="2" placeholder="<?php esc_attr_e('e.g. The time entry should have saved with the correct date...', 'freelance-studio'); ?>"></textarea>
                    </div>

                    <div class="fs-bug-field">
                        <label for="fs-bug-actual"><?php esc_html_e('What actually happened?', 'freelance-studio'); ?></label>
                        <textarea id="fs-bug-actual" name="bug_actual" rows="2" placeholder="<?php esc_attr_e('e.g. The date was off by one day and the entry showed 0:00 duration...', 'freelance-studio'); ?>"></textarea>
                    </div>

                    <div class="fs-bug-field">
                        <label><?php esc_html_e('Screenshots', 'freelance-studio'); ?> <span class="fs-bug-optional"><?php esc_html_e('(optional)', 'freelance-studio'); ?></span></label>
                        <div class="fs-bug-upload-area" id="fs-bug-upload-area">
                            <input type="file" id="fs-bug-files" name="bug_screenshots[]" multiple accept="image/*" class="fs-bug-file-input">
                            <div class="fs-bug-upload-prompt">
                                <span class="dashicons dashicons-cloud-upload"></span>
                                <p><?php esc_html_e('Click or drag images here', 'freelance-studio'); ?></p>
                                <span class="fs-bug-upload-hint"><?php esc_html_e('PNG, JPG up to 5MB each. Max 3 files.', 'freelance-studio'); ?></span>
                            </div>
                        </div>
                        <div class="fs-bug-preview-list" id="fs-bug-preview-list"></div>
                    </div>

                    <details class="fs-bug-debug-details">
                        <summary><?php esc_html_e('Debug information (auto-included)', 'freelance-studio'); ?></summary>
                        <pre class="fs-bug-debug-info"><?php echo esc_html($debug_string); ?></pre>
                    </details>
                    <input type="hidden" name="debug_info" value="<?php echo esc_attr($debug_string); ?>">
                    <input type="hidden" name="user_email" value="<?php echo esc_attr(wp_get_current_user()->user_email); ?>">
                    <input type="hidden" name="user_name" value="<?php echo esc_attr(wp_get_current_user()->display_name); ?>">
                    <input type="hidden" name="site_url" value="<?php echo esc_attr(home_url()); ?>">

                    <div class="fs-bug-actions">
                        <button type="button" class="fs-btn fs-btn-secondary" id="fs-bug-cancel"><?php esc_html_e('Cancel', 'freelance-studio'); ?></button>
                        <button type="submit" class="fs-btn fs-btn-primary" id="fs-bug-submit">
                            <span class="dashicons dashicons-email-alt"></span>
                            <?php esc_html_e('Send Report', 'freelance-studio'); ?>
                        </button>
                    </div>
                </form>

                <div class="fs-bug-success" id="fs-bug-success" style="display:none;">
                    <span class="dashicons dashicons-yes-alt"></span>
                    <h3><?php esc_html_e('Bug report sent!', 'freelance-studio'); ?></h3>
                    <p><?php esc_html_e('Thank you for helping improve Freelance Studio.', 'freelance-studio'); ?></p>
                </div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            var $modal = $('#fs-bug-report-modal'),
                $overlay = $('#fs-bug-report-overlay'),
                $form = $('#fs-bug-report-form'),
                $success = $('#fs-bug-success'),
                maxFiles = 3,
                selectedFiles = [];

            function openModal() {
                $overlay.addClass('active');
                $modal.addClass('active');
                $form.show();
                $success.hide();
                $('body').css('overflow', 'hidden');
            }

            function closeModal() {
                $overlay.removeClass('active');
                $modal.removeClass('active');
                $('body').css('overflow', '');
                setTimeout(function() {
                    $form[0].reset();
                    selectedFiles = [];
                    $('#fs-bug-preview-list').empty();
                    $('#fs-bug-page').val('<?php echo esc_js($page_context); ?>');
                }, 300);
            }

            $('#fs-bug-report-trigger').on('click', openModal);
            $('#fs-bug-report-close, #fs-bug-cancel').on('click', closeModal);
            $overlay.on('click', closeModal);

            // File handling
            var $uploadArea = $('#fs-bug-upload-area'),
                $fileInput = $('#fs-bug-files'),
                $previewList = $('#fs-bug-preview-list');

            $uploadArea.on('dragover', function(e) {
                e.preventDefault();
                $(this).addClass('dragover');
            }).on('dragleave drop', function(e) {
                e.preventDefault();
                $(this).removeClass('dragover');
            }).on('drop', function(e) {
                handleFiles(e.originalEvent.dataTransfer.files);
            });

            $fileInput.on('change', function() {
                handleFiles(this.files);
                this.value = '';
            });

            function handleFiles(files) {
                Array.from(files).forEach(function(file) {
                    if (selectedFiles.length >= maxFiles) return;
                    if (!file.type.startsWith('image/')) return;
                    if (file.size > 5 * 1024 * 1024) {
                        alert('<?php echo esc_js(__('File too large. Maximum 5MB per file.', 'freelance-studio')); ?>');
                        return;
                    }
                    selectedFiles.push(file);
                    addPreview(file, selectedFiles.length - 1);
                });
                if (selectedFiles.length >= maxFiles) {
                    $uploadArea.hide();
                }
            }

            function addPreview(file, index) {
                var reader = new FileReader();
                reader.onload = function(e) {
                    var $item = $('<div class="fs-bug-preview-item" data-index="' + index + '">' +
                        '<img src="' + e.target.result + '">' +
                        '<button type="button" class="fs-bug-preview-remove">&times;</button>' +
                        '<span class="fs-bug-preview-name">' + file.name + '</span>' +
                        '</div>');
                    $previewList.append($item);
                };
                reader.readAsDataURL(file);
            }

            $previewList.on('click', '.fs-bug-preview-remove', function() {
                var $item = $(this).closest('.fs-bug-preview-item');
                var index = $item.data('index');
                selectedFiles[index] = null;
                $item.remove();
                $uploadArea.show();
            });

            // Submit
            $form.on('submit', function(e) {
                e.preventDefault();

                var doing = $('#fs-bug-doing').val().trim();
                var actual = $('#fs-bug-actual').val().trim();
                if (!doing || !actual) {
                    alert('<?php echo esc_js(__('Please describe what you were doing and what actually happened.', 'freelance-studio')); ?>');
                    return;
                }

                var formData = new FormData();
                formData.append('action', 'fs_submit_bug_report');
                formData.append('fs_bug_report_nonce', $('[name=fs_bug_report_nonce]').val());
                formData.append('bug_page', $('#fs-bug-page').val());
                formData.append('bug_doing', doing);
                formData.append('bug_expected', $('#fs-bug-expected').val());
                formData.append('bug_actual', actual);
                formData.append('debug_info', $('[name=debug_info]').val());
                formData.append('user_email', $('[name=user_email]').val());
                formData.append('user_name', $('[name=user_name]').val());
                formData.append('site_url', $('[name=site_url]').val());

                selectedFiles.forEach(function(file, i) {
                    if (file) formData.append('screenshots[]', file);
                });

                var $btn = $('#fs-bug-submit');
                $btn.prop('disabled', true).html('<span class="dashicons dashicons-update fs-spin"></span> <?php echo esc_js(__('Sending...', 'freelance-studio')); ?>');

                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(response) {
                        if (response.success) {
                            $form.hide();
                            $success.show();
                            setTimeout(closeModal, 2500);
                        } else {
                            alert(response.data || '<?php echo esc_js(__('Failed to send report. Please try again.', 'freelance-studio')); ?>');
                            $btn.prop('disabled', false).html('<span class="dashicons dashicons-email-alt"></span> <?php echo esc_js(__('Send Report', 'freelance-studio')); ?>');
                        }
                    },
                    error: function() {
                        alert('<?php echo esc_js(__('Failed to send report. Please try again.', 'freelance-studio')); ?>');
                        $btn.prop('disabled', false).html('<span class="dashicons dashicons-email-alt"></span> <?php echo esc_js(__('Send Report', 'freelance-studio')); ?>');
                    }
                });
            });
        });
        </script>
        <?php
    }

    /**
     * Render floating timer indicator
     * Shows a small fixed-position widget on all FS admin pages when a timer is running
     */
    public function render_floating_timer() {
        $screen = get_current_screen();
        if (!$screen || (strpos($screen->id, 'fs-') === false && strpos($screen->id, 'freelance-studio') === false)) {
            return;
        }

        $running_timer = FS_Time_Entry::get_running();
        if (!$running_timer) {
            return;
        }

        // Get task name if available
        $task_name = '';
        $task_id = 0;
        if (!empty($running_timer->task_id)) {
            $task_id = intval($running_timer->task_id);
            $task = FS_Task::get($task_id);
            if ($task) {
                $task_name = $task->title;
            }
        }
        
        $project_id = intval($running_timer->project_id ?: 0);
        $project_name = $running_timer->project_name ?: '';
        ?>
        <div id="fs-floating-timer" class="fs-floating-timer <?php echo !empty($running_timer->is_paused) ? 'paused' : 'running'; ?>">
            <div class="fs-floating-timer-pulse"></div>
            <div class="fs-floating-timer-info">
                <span class="fs-floating-timer-time" id="fs-floating-time">00:00:00</span>
                <?php if ($task_name): ?>
                    <span class="fs-floating-timer-task"><?php echo esc_html($task_name); ?></span>
                <?php elseif ($project_name): ?>
                    <span class="fs-floating-timer-task"><?php echo esc_html($project_name); ?></span>
                <?php endif; ?>
            </div>
            <button type="button" class="fs-floating-timer-popout" 
                    data-task-id="<?php echo esc_attr($task_id); ?>"
                    data-project-id="<?php echo esc_attr($project_id); ?>"
                    data-task-name="<?php echo esc_attr($task_name); ?>"
                    title="<?php esc_attr_e('Open timer window', 'freelance-studio'); ?>">
                <span class="dashicons dashicons-external"></span>
            </button>
        </div>
        <script>
        jQuery(document).ready(function($) {
            var $floatingTimer = $('#fs-floating-timer');
            if (!$floatingTimer.length) return;
            
            var startTime = <?php echo intval(strtotime($running_timer->start_time . ' UTC')); ?>;
            var pausedDuration = <?php echo intval($running_timer->paused_duration); ?>;
            var isPaused = <?php echo !empty($running_timer->is_paused) ? 'true' : 'false'; ?>;
            
            function updateFloatingTime() {
                var elapsed;
                if (isPaused) {
                    elapsed = pausedDuration;
                } else {
                    var now = Math.floor(Date.now() / 1000);
                    elapsed = (now - startTime) + pausedDuration;
                }
                
                var hours = Math.floor(elapsed / 3600);
                var minutes = Math.floor((elapsed % 3600) / 60);
                var seconds = elapsed % 60;
                
                $('#fs-floating-time').text(
                    String(hours).padStart(2, '0') + ':' +
                    String(minutes).padStart(2, '0') + ':' +
                    String(seconds).padStart(2, '0')
                );
            }
            
            updateFloatingTime();
            if (!isPaused) {
                setInterval(updateFloatingTime, 1000);
            }
            
            // Pop out button
            $floatingTimer.find('.fs-floating-timer-popout').on('click', function() {
                var taskId = $(this).data('task-id') || 0;
                var projectId = $(this).data('project-id') || '';
                var taskName = $(this).data('task-name') || '';
                
                var url = (typeof ajaxurl !== 'undefined' ? ajaxurl : '').replace('admin-ajax.php', 'admin.php') + 
                    '?page=fs-time-tracking&action=popup_timer' +
                    '&task_id=' + taskId +
                    '&project_id=' + projectId +
                    '&task_name=' + encodeURIComponent(taskName);
                
                var w = 340, h = 440;
                var left = (screen.width - w) / 2;
                var top = (screen.height - h) / 2;
                
                window.open(url, 'fs_popup_timer', 
                    'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top + 
                    ',menubar=no,toolbar=no,location=no,status=no,resizable=yes');
            });
            
            // Click on the timer info to go to time tracking page
            $floatingTimer.find('.fs-floating-timer-info').on('click', function() {
                window.location.href = '<?php echo  esc_url( admin_url('admin.php?page=fs-time-tracking') ); ?>';
            });
            
            // Listen for timer state changes from other windows (popup, other tabs)
            try {
                var fsTimerChannel = new BroadcastChannel('fs_timer_sync');
                fsTimerChannel.onmessage = function(event) {
                    var msg = event.data;
                    if (msg.type === 'timer_stopped') {
                        // Timer was stopped — hide the floating widget and reload to update any page-level timer UI
                        $floatingTimer.fadeOut(200);
                        location.reload();
                    } else if (msg.type === 'timer_paused') {
                        $floatingTimer.removeClass('running').addClass('paused');
                        isPaused = true;
                    } else if (msg.type === 'timer_resumed') {
                        $floatingTimer.removeClass('paused').addClass('running');
                        isPaused = false;
                    } else if (msg.type === 'timer_started') {
                        // Timer was started from popup — reload to show the floating widget
                        location.reload();
                    }
                };
            } catch(e) {}
        });
        </script>
        <?php
    }

    /**
     * Handle bug report AJAX submission
     */
    public function handle_bug_report() {
        check_ajax_referer('fs_bug_report', 'fs_bug_report_nonce');

        $to = 'admin@freelancestud.io';
        $user_name = sanitize_text_field($_POST['user_name'] ?? '');
        $user_email = sanitize_email($_POST['user_email'] ?? '');
        $site_url = esc_url_raw($_POST['site_url'] ?? '');
        $bug_page = sanitize_text_field($_POST['bug_page'] ?? '');
        $bug_doing = sanitize_textarea_field($_POST['bug_doing'] ?? '');
        $bug_expected = sanitize_textarea_field($_POST['bug_expected'] ?? '');
        $bug_actual = sanitize_textarea_field($_POST['bug_actual'] ?? '');
        $debug_info = sanitize_textarea_field($_POST['debug_info'] ?? '');

        $subject = '[Freelance Studio Bug] ' . ($bug_page ? $bug_page : 'General') . ' - ' . substr($bug_actual, 0, 60);

        $body = "Bug Report from Freelance Studio\n";
        $body .= "================================\n\n";
        $body .= "Reported by: {$user_name} <{$user_email}>\n";
        $body .= "Site: {$site_url}\n";
        $body .= "Page/Section: {$bug_page}\n\n";
        $body .= "WHAT THEY WERE DOING:\n{$bug_doing}\n\n";
        $body .= "EXPECTED BEHAVIOR:\n{$bug_expected}\n\n";
        $body .= "ACTUAL BEHAVIOR:\n{$bug_actual}\n\n";
        $body .= "DEBUG INFORMATION:\n{$debug_info}\n";

        $headers = array(
            'From: ' . $user_name . ' <' . $user_email . '>',
            'Reply-To: ' . $user_email,
        );

        // Handle screenshot attachments
        $attachments = array();
        if (!empty($_FILES['screenshots'])) {
            $upload_dir = wp_upload_dir();
            $bug_dir = $upload_dir['basedir'] . '/fs-bug-reports/';
            if (!file_exists($bug_dir)) {
                wp_mkdir_p($bug_dir);
            }

            foreach ($_FILES['screenshots']['name'] as $i => $name) {
                if ($_FILES['screenshots']['error'][$i] !== UPLOAD_ERR_OK) continue;
                if ($_FILES['screenshots']['size'][$i] > 5 * 1024 * 1024) continue;

                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                if (!in_array($ext, array('jpg', 'jpeg', 'png', 'gif', 'webp'))) continue;

                $filename = 'bug-' . time() . '-' . $i . '.' . $ext;
                $filepath = $bug_dir . $filename;

                // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- Core wp_handle_upload() doesn't support multi-file arrays; direct move is necessary here.
                if (move_uploaded_file($_FILES['screenshots']['tmp_name'][$i], $filepath)) {
                    $attachments[] = $filepath;
                }
            }
        }

        $sent = wp_mail($to, $subject, $body, $headers, $attachments);

        // Clean up attachment files after sending
        foreach ($attachments as $file) {
            if (file_exists($file)) {
                wp_delete_file($file);
            }
        }

        if ($sent) {
            wp_send_json_success();
        } else {
            wp_send_json_error(__('Failed to send email. Please check your site email configuration.', 'freelance-studio'));
        }
    }
}
