<?php
/**
 * Contract Model
 *
 * @package Freelance_Studio
 */

class FS_Contract {

    // Status constants
    const STATUS_DRAFT = 'draft';
    const STATUS_SENT = 'sent';
    const STATUS_VIEWED = 'viewed';
    const STATUS_SIGNED = 'signed';
    const STATUS_DECLINED = 'declined';
    const STATUS_EXPIRED = 'expired';
    const STATUS_COMPLETED = 'completed';
    const STATUS_CANCELLED = 'cancelled';

    /**
     * Create a new contract
     *
     * @param array $data Contract data
     * @return int|false Contract ID or false on failure
     */
    public static function create($data) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';

        // Parse services if it's an array
        $services = isset($data['services']) ? $data['services'] : array();
        if (is_array($services)) {
            $services = json_encode($services);
        }

        // Parse enabled_sections if it's an array
        $enabled_sections = isset($data['enabled_sections']) ? $data['enabled_sections'] : self::get_default_enabled_sections();
        if (is_array($enabled_sections)) {
            $enabled_sections = json_encode($enabled_sections);
        }

        // Auto-populate freelancer signature from settings
        $freelancer_signature = isset($data['signature_freelancer']) ? $data['signature_freelancer'] : null;
        $freelancer_name = isset($data['signature_freelancer_name']) ? $data['signature_freelancer_name'] : null;
        $freelancer_date = isset($data['signature_freelancer_date']) ? $data['signature_freelancer_date'] : null;
        
        if (empty($freelancer_signature)) {
            $saved_signature = get_option('fs_contractor_signature', '');
            if (!empty($saved_signature)) {
                $freelancer_signature = $saved_signature;
                $freelancer_name = get_option('fs_business_name', '');
                $freelancer_date = current_time('mysql');
            }
        }

        $insert_data = array(
            'proposal_id' => isset($data['proposal_id']) ? intval($data['proposal_id']) : null,
            'client_id' => intval($data['client_id']),
            'project_id' => isset($data['project_id']) ? intval($data['project_id']) : null,
            'contract_number' => self::generate_contract_number(),
            'title' => sanitize_text_field($data['title']),
            'status' => isset($data['status']) ? sanitize_text_field($data['status']) : self::STATUS_DRAFT,
            'services' => $services,
            'pricing_type' => isset($data['pricing_type']) ? sanitize_text_field($data['pricing_type']) : 'fixed',
            'hourly_rate' => isset($data['hourly_rate']) ? floatval($data['hourly_rate']) : null,
            'estimated_hours' => isset($data['estimated_hours']) ? floatval($data['estimated_hours']) : null,
            'total_amount' => isset($data['total_amount']) ? floatval($data['total_amount']) : 0,
            'deposit_required' => isset($data['deposit_required']) ? intval($data['deposit_required']) : 0,
            'deposit_type' => isset($data['deposit_type']) ? sanitize_text_field($data['deposit_type']) : 'percentage',
            'deposit_value' => isset($data['deposit_value']) ? floatval($data['deposit_value']) : 0,
            'billing_frequency' => isset($data['billing_frequency']) ? sanitize_text_field($data['billing_frequency']) : 'once',
            'first_invoice_date' => !empty($data['first_invoice_date']) ? sanitize_text_field($data['first_invoice_date']) : null,
            'enabled_sections' => $enabled_sections,
            'terms_content' => isset($data['terms_content']) ? $data['terms_content'] : null,
            'start_date' => !empty($data['start_date']) ? sanitize_text_field($data['start_date']) : null,
            'end_date' => !empty($data['end_date']) ? sanitize_text_field($data['end_date']) : null,
            'valid_until' => !empty($data['valid_until']) ? sanitize_text_field($data['valid_until']) : null,
            'notes' => isset($data['notes']) ? sanitize_textarea_field($data['notes']) : null,
            'internal_notes' => isset($data['internal_notes']) ? sanitize_textarea_field($data['internal_notes']) : null,
            'send_to_contacts' => isset($data['send_to_contacts']) ? $data['send_to_contacts'] : null,
            'client_contact_id' => !empty($data['client_contact_id']) ? intval($data['client_contact_id']) : null,
            'contact_name' => !empty($data['contact_name']) ? sanitize_text_field($data['contact_name']) : null,
            'contact_email' => !empty($data['contact_email']) ? sanitize_email($data['contact_email']) : null,
            'contact_phone' => !empty($data['contact_phone']) ? sanitize_text_field($data['contact_phone']) : null,
            'signature_freelancer' => $freelancer_signature,
            'signature_freelancer_name' => $freelancer_name,
            'signature_freelancer_date' => $freelancer_date,
            'view_token' => wp_generate_password(32, false),
        );

        $result = $wpdb->insert($table_name, $insert_data);

        if ($result === false) {
            return false;
        }

        $new_id = $wpdb->insert_id;
        
        // Fix NULL foreign keys that wpdb->insert converts to 0
        FS_DB_Helper::fix_null_fields($table_name, $new_id, array(
            'proposal_id' => $insert_data['proposal_id'],
            'project_id' => $insert_data['project_id'],
            'client_contact_id' => $insert_data['client_contact_id'],
        ));

        return $new_id;
    }

    /**
     * Update a contract
     *
     * @param int $id Contract ID
     * @param array $data Contract data
     * @return bool Success
     */
    public static function update($id, $data) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';

        // Parse services if it's an array
        if (isset($data['services']) && is_array($data['services'])) {
            $data['services'] = json_encode($data['services']);
        }

        // Parse enabled_sections if it's an array
        if (isset($data['enabled_sections']) && is_array($data['enabled_sections'])) {
            $data['enabled_sections'] = json_encode($data['enabled_sections']);
        }

        $update_data = array();
        $allowed_fields = array(
            'proposal_id', 'client_id', 'project_id', 'title', 'status',
            'services', 'pricing_type', 'hourly_rate', 'estimated_hours',
            'total_amount', 'deposit_required', 'deposit_type', 'deposit_value',
            'billing_frequency', 'first_invoice_date', 'enabled_sections', 'terms_content',
            'start_date', 'end_date', 'valid_until', 'notes', 'internal_notes',
            'send_to_contacts', 'client_contact_id', 'contact_name', 'contact_email', 'contact_phone',
            'signature_freelancer', 'signature_freelancer_name',
            'signature_freelancer_date', 'signature_client', 'signature_client_name',
            'signature_client_date', 'signature_client_ip', 'signed_pdf_path',
            'sent_at', 'viewed_at', 'signed_at'
        );

        foreach ($allowed_fields as $field) {
            if (isset($data[$field])) {
                $update_data[$field] = $data[$field];
            }
        }

        if (empty($update_data)) {
            return true;
        }

        // Track nullable foreign keys before wpdb converts them to 0
        $nullable_fk_fields = array('proposal_id', 'project_id', 'client_contact_id');
        $nullable_fks = array();
        foreach ($nullable_fk_fields as $fk_field) {
            if (array_key_exists($fk_field, $update_data) && empty($update_data[$fk_field])) {
                $nullable_fks[$fk_field] = null;
                unset($update_data[$fk_field]); // Remove from main update
            }
        }

        $result = true;
        if (!empty($update_data)) {
            $result = $wpdb->update($table_name, $update_data, array('id' => $id)) !== false;
        }
        
        // Fix NULL foreign keys
        if (!empty($nullable_fks)) {
            FS_DB_Helper::fix_null_fields($table_name, $id, $nullable_fks);
        }

        return $result;
    }

    /**
     * Get a contract by ID
     *
     * @param int $id Contract ID
     * @return object|null Contract object or null
     */
    public static function get($id) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';

        $contract = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table_name WHERE id = %d",
            $id
        ));

        if ($contract && !empty($contract->services)) {
            $contract->services = json_decode($contract->services ?? '', true);
        }

        return $contract;
    }

    /**
     * Get contract by view token
     *
     * @param string $token View token
     * @return object|null Contract object or null
     */
    public static function get_by_token($token) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';

        $contract = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table_name WHERE view_token = %s",
            $token
        ));

        if ($contract && !empty($contract->services)) {
            $contract->services = json_decode($contract->services ?? '', true);
        }

        return $contract;
    }

    /**
     * Get all contracts
     *
     * @param array $args Query arguments
     * @return array Array of contract objects
     */
    public static function get_all($args = array()) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';
        $clients_table = $wpdb->prefix . 'fs_clients';

        $defaults = array(
            'status' => null,
            'client_id' => null,
            'proposal_id' => null,
            'archived' => null,
            'orderby' => 'created_at',
            'order' => 'DESC',
            'limit' => null,
            'offset' => 0,
        );
        
        $args = wp_parse_args($args, $defaults);

        $where = array('1=1');
        $values = array();

        if ($args['archived'] !== null) {
            if ($args['archived']) {
                $where[] = 'c.archived = 1';
            } else {
                $where[] = '(c.archived = 0 OR c.archived IS NULL)';
            }
        }

        if (!empty($args['status'])) {
            if (is_array($args['status'])) {
                $placeholders = implode(', ', array_fill(0, count($args['status']), '%s'));
                $where[] = "c.status IN ($placeholders)";
                $values = array_merge($values, $args['status']);
            } else {
                $where[] = 'c.status = %s';
                $values[] = $args['status'];
            }
        }

        if (!empty($args['client_id'])) {
            $where[] = 'c.client_id = %d';
            $values[] = $args['client_id'];
        }

        if (!empty($args['proposal_id'])) {
            $where[] = 'c.proposal_id = %d';
            $values[] = $args['proposal_id'];
        }

        $where_clause = implode(' AND ', $where);
        
        // Validate orderby to prevent SQL injection
        $allowed_orderby = array('created_at', 'start_date', 'end_date', 'title', 'total_amount', 'status');
        $orderby = in_array($args['orderby'], $allowed_orderby) ? $args['orderby'] : 'created_at';
        $order = strtoupper($args['order']) === 'ASC' ? 'ASC' : 'DESC';

        $query = "SELECT c.*, cl.company_name as client_name, cl.name as client_contact_name
                  FROM $table_name c 
                  LEFT JOIN $clients_table cl ON c.client_id = cl.id 
                  WHERE $where_clause 
                  ORDER BY c.{$orderby} {$order}";

        if (!empty($values)) {
            $query = $wpdb->prepare($query, $values); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared
        }

        if ($args['limit']) {
            $query .= $wpdb->prepare(' LIMIT %d OFFSET %d', $args['limit'], $args['offset']);
        }

        $contracts = $wpdb->get_results($query); // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared

        foreach ($contracts as $contract) {
            if (!empty($contract->services)) {
                $contract->services = json_decode($contract->services ?? '', true);
            }
            // Use company_name if available, otherwise fall back to contact name
            if (empty($contract->client_name)) {
                $contract->client_name = $contract->client_contact_name;
            }
        }

        return $contracts;
    }

    /**
     * Count contracts with optional filters
     *
     * @since 8.5.4
     * @param array $args Filter arguments (status, client_id, archived)
     * @return int
     */
    public static function count($args = array()) {
        global $wpdb;
        $table = $wpdb->prefix . 'fs_contracts';
        
        $where = array('1=1');
        $values = array();
        
        if (!empty($args['status'])) {
            if (is_array($args['status'])) {
                $placeholders = implode(', ', array_fill(0, count($args['status']), '%s'));
                $where[] = "status IN ($placeholders)";
                $values = array_merge($values, $args['status']);
            } else {
                $where[] = 'status = %s';
                $values[] = $args['status'];
            }
        }
        
        if (!empty($args['client_id'])) {
            $where[] = 'client_id = %d';
            $values[] = $args['client_id'];
        }
        
        if (isset($args['archived'])) {
            if ($args['archived']) {
                $where[] = 'archived = 1';
            } else {
                $where[] = '(archived = 0 OR archived IS NULL)';
            }
        }
        
        $where_clause = implode(' AND ', $where);
        $query = "SELECT COUNT(*) FROM {$table} WHERE {$where_clause}";
        
        if (!empty($values)) {
            $query = $wpdb->prepare($query, $values); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        }
        
        return (int) $wpdb->get_var($query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    }

    /**
     * Get contracts by client ID
     *
     * @param int $client_id Client ID
     * @return array Array of contract objects
     */
    public static function get_by_client($client_id) {
        return self::get_all(array('client_id' => $client_id));
    }

    /**
     * Delete a contract
     *
     * @param int $id Contract ID
     * @return bool Success
     */
    public static function delete($id) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';

        return $wpdb->delete($table_name, array('id' => $id)) !== false;
    }

    /**
     * Update contract status
     *
     * @param int $id Contract ID
     * @param string $status New status
     * @return bool Success
     */
    public static function update_status($id, $status) {
        $update_data = array('status' => $status);

        // Update timestamp based on status
        switch ($status) {
            case self::STATUS_SENT:
                $update_data['sent_at'] = current_time('mysql');
                break;
            case self::STATUS_VIEWED:
                $update_data['viewed_at'] = current_time('mysql');
                break;
            case self::STATUS_SIGNED:
                $update_data['signed_at'] = current_time('mysql');
                break;
        }

        return self::update($id, $update_data);
    }

    /**
     * Generate a unique contract number
     *
     * @return string Contract number
     */
    public static function generate_contract_number() {
        global $wpdb;
        $table_name = $wpdb->prefix . 'fs_contracts';
        $prefix = get_option('fs_contract_prefix', 'CON');
        $year = gmdate('Y');

        // Get the highest number for this year
        $last_number = $wpdb->get_var($wpdb->prepare(
            "SELECT MAX(CAST(SUBSTRING_INDEX(contract_number, '-', -1) AS UNSIGNED)) 
             FROM $table_name 
             WHERE contract_number LIKE %s",
            $prefix . '-' . $year . '-%'
        ));

        $next_number = ($last_number ? $last_number : 0) + 1;

        return $prefix . '-' . $year . '-' . str_pad($next_number, 3, '0', STR_PAD_LEFT);
    }

    /**
     * Duplicate a contract
     *
     * @param int $id Contract ID to duplicate
     * @return int|false New contract ID or false
     */
    public static function duplicate($id) {
        $contract = self::get($id);
        if (!$contract) {
            return false;
        }

        $data = array(
            'client_id' => $contract->client_id,
            'title' => $contract->title . ' (Copy)',
            'services' => $contract->services,
            'pricing_type' => $contract->pricing_type,
            'hourly_rate' => $contract->hourly_rate,
            'estimated_hours' => $contract->estimated_hours,
            'total_amount' => $contract->total_amount,
            'deposit_required' => $contract->deposit_required,
            'deposit_type' => $contract->deposit_type,
            'deposit_value' => $contract->deposit_value,
            'billing_frequency' => $contract->billing_frequency,
            'terms_content' => $contract->terms_content,
            'notes' => $contract->notes,
        );

        return self::create($data);
    }

    /**
     * Create contract from proposal
     *
     * @param int $proposal_id Proposal ID
     * @return int|false Contract ID or false
     */
    public static function create_from_proposal($proposal_id) {
        $proposal = FS_Proposal::get($proposal_id);
        if (!$proposal) {
            return false;
        }

        $data = array(
            'proposal_id' => $proposal_id,
            'client_id' => $proposal->client_id,
            'title' => $proposal->title,
            'services' => $proposal->services,
            'pricing_type' => $proposal->pricing_type,
            'hourly_rate' => $proposal->hourly_rate,
            'estimated_hours' => $proposal->estimated_hours,
            'total_amount' => $proposal->total_amount,
            'deposit_required' => $proposal->deposit_required,
            'deposit_type' => $proposal->deposit_type,
            'deposit_value' => $proposal->deposit_value,
            'billing_frequency' => $proposal->billing_frequency,
            'first_invoice_date' => $proposal->first_invoice_date,
            'start_date' => $proposal->start_date,
            'notes' => $proposal->notes,
            'send_to_contacts' => $proposal->send_to_contacts,
        );

        // Set valid until to 30 days from now by default
        $valid_days = get_option('fs_contract_valid_days', 30);
        $data['valid_until'] = gmdate('Y-m-d', strtotime('+' . $valid_days . ' days'));

        $contract_id = self::create($data);

        if ($contract_id) {
            // Update proposal with contract reference
            FS_Proposal::update($proposal_id, array(
                'contract_id' => $contract_id,
                'status' => FS_Proposal::STATUS_CONVERTED
            ));
        }

        return $contract_id;
    }

    /**
     * Sign contract (client side)
     *
     * @param int $id Contract ID
     * @param array $signature_data Signature data
     * @return bool Success
     */
    public static function sign($id, $signature_data) {
        global $wpdb;
        
        // First, update the core signature data and status
        // Do this as a direct query to ensure it succeeds
        $table_name = $wpdb->prefix . 'fs_contracts';
        
        $result = $wpdb->update(
            $table_name,
            array(
                'signature_client' => $signature_data['signature'],
                'signature_client_name' => sanitize_text_field($signature_data['name']),
                'signature_client_date' => current_time('mysql'),
                'signature_client_ip' => $_SERVER['REMOTE_ADDR'] ?? '',
                'status' => self::STATUS_SIGNED,
                'signed_at' => current_time('mysql'),
            ),
            array('id' => $id),
            array('%s', '%s', '%s', '%s', '%s', '%s'),
            array('%d')
        );

        if ($result === false) {
            error_log('Freelance Studio - Contract signing failed: ' . $wpdb->last_error);
            return false;
        }

        // Now try to generate PDF and send emails (non-blocking)
        try {
            // Get fresh contract data with signatures
            $contract = self::get($id);
            $client = FS_Client::get($contract->client_id);
            
            // Generate and save signed PDF
            $pdf_path = null;
            if (class_exists('FS_PDF_Service') && method_exists('FS_PDF_Service', 'save_signed_contract_pdf')) {
                $pdf_path = FS_PDF_Service::save_signed_contract_pdf($contract, $client);
                if ($pdf_path) {
                    $wpdb->update($table_name, array('signed_pdf_path' => $pdf_path), array('id' => $id));
                }
            }
            
            // Send notification email to freelancer with PDF attached
            if (class_exists('FS_Email_Service')) {
                if (method_exists('FS_Email_Service', 'send_contract_signed_notification')) {
                    FS_Email_Service::send_contract_signed_notification($id, $pdf_path);
                }
                
                // Send copy of signed contract to client
                if (method_exists('FS_Email_Service', 'send_signed_contract_to_client')) {
                    FS_Email_Service::send_signed_contract_to_client($id, $pdf_path);
                }
            }
        } catch (Exception $e) {
            // Log but don't fail - the signing itself succeeded
            error_log('Freelance Studio - Post-signing tasks failed: ' . $e->getMessage());
        }

        return true;
    }

    /**
     * Sign contract (freelancer side - counter-signature)
     *
     * @param int $id Contract ID
     * @param array $signature_data Signature data
     * @return bool Success
     */
    public static function counter_sign($id, $signature_data) {
        return self::update($id, array(
            'signature_freelancer' => $signature_data['signature'],
            'signature_freelancer_name' => sanitize_text_field($signature_data['name']),
            'signature_freelancer_date' => current_time('mysql'),
        ));
    }

    /**
     * Check if contract is expired
     *
     * @param object $contract Contract object
     * @return bool True if expired
     */
    public static function is_expired($contract) {
        if (empty($contract->valid_until)) {
            return false;
        }
        if (in_array($contract->status, array(self::STATUS_SIGNED, self::STATUS_COMPLETED))) {
            return false;
        }
        return strtotime($contract->valid_until) < time();
    }

    /**
     * Calculate deposit amount
     *
     * @param object $contract Contract object
     * @return float Deposit amount
     */
    public static function calculate_deposit($contract) {
        if (!$contract->deposit_required) {
            return 0;
        }

        if ($contract->deposit_type === 'percentage') {
            return ($contract->total_amount * $contract->deposit_value) / 100;
        }

        return floatval($contract->deposit_value);
    }

    /**
     * Get available statuses
     *
     * @return array Status options
     */
    public static function get_statuses() {
        return array(
            self::STATUS_DRAFT => __('Draft', 'freelance-studio'),
            self::STATUS_SENT => __('Sent', 'freelance-studio'),
            self::STATUS_VIEWED => __('Viewed', 'freelance-studio'),
            self::STATUS_SIGNED => __('Signed', 'freelance-studio'),
            self::STATUS_DECLINED => __('Declined', 'freelance-studio'),
            self::STATUS_EXPIRED => __('Expired', 'freelance-studio'),
            self::STATUS_COMPLETED => __('Completed', 'freelance-studio'),
            self::STATUS_CANCELLED => __('Cancelled', 'freelance-studio'),
        );
    }

    /**
     * Get billing frequencies
     *
     * @return array Frequency options
     */
    public static function get_billing_frequencies() {
        return array(
            'once' => __('Once', 'freelance-studio'),
            'weekly' => __('Weekly', 'freelance-studio'),
            'bi-weekly' => __('Bi-Weekly', 'freelance-studio'),
            'monthly' => __('Monthly', 'freelance-studio'),
            'quarterly' => __('Quarterly', 'freelance-studio'),
            'milestone' => __('Per Milestone', 'freelance-studio'),
        );
    }

    /**
     * Get default enabled sections
     *
     * @return array Array of section keys that are enabled by default
     */
    public static function get_default_enabled_sections() {
        $sections = FS_Contract_Section::get_all(true); // Only active sections
        $enabled = array();
        
        foreach ($sections as $section) {
            $enabled[] = $section->section_key;
        }
        
        return $enabled;
    }

    /**
     * Get enabled sections for a contract
     *
     * @param object $contract Contract object
     * @return array Array of enabled section keys
     */
    public static function get_enabled_sections($contract) {
        if (!empty($contract->enabled_sections)) {
            $enabled = json_decode($contract->enabled_sections ?? '', true);
            if (is_array($enabled)) {
                return $enabled;
            }
        }
        
        // Return defaults if not set
        return self::get_default_enabled_sections();
    }

    /**
     * Get placeholder replacements from settings
     *
     * @param object|null $contract Optional contract for contract-specific values
     * @return array Placeholder => replacement value
     */
    public static function get_placeholder_replacements($contract = null) {
        $hourly_rate = $contract ? floatval($contract->hourly_rate) : floatval(get_option('fs_default_hourly_rate', 75));
        $jurisdiction = get_option('fs_contract_jurisdiction', '');
        if (empty($jurisdiction)) {
            $jurisdiction = get_option('fs_business_state', '');
        }
        
        return array(
            '[Number]' => get_option('fs_contract_notice_days', 14),
            '[Percentage]' => get_option('fs_contract_late_fee_percent', 1.5) . '%',
            '[Hourly Rate]' => '$' . number_format($hourly_rate, 2),
            "[Freelancer's State/Country]" => $jurisdiction,
            // Legacy placeholder for backward compatibility
            "[Designer's State/Country]" => $jurisdiction,
            // Additional specific placeholders
            '*[Number]*' => get_option('fs_contract_notice_days', 14), // With asterisks (markdown style)
            '*[Percentage]*' => get_option('fs_contract_late_fee_percent', 1.5) . '%',
        );
    }

    /**
     * Build terms content from enabled sections
     *
     * @param array $enabled_sections Array of section keys
     * @param array $replacements Placeholder replacements
     * @return string HTML content
     */
    public static function build_terms_from_sections($enabled_sections, $replacements = array()) {
        // Merge with default replacements
        $default_replacements = self::get_placeholder_replacements();
        $replacements = array_merge($default_replacements, $replacements);
        
        return FS_Contract_Section::build_terms_html($enabled_sections, $replacements);
    }

    /**
     * Get default contract terms (legacy - now uses sections)
     *
     * @return string Default terms content
     */
    public static function get_default_terms() {
        // Build from default enabled sections
        $enabled_sections = self::get_default_enabled_sections();
        return self::build_terms_from_sections($enabled_sections);
    }

    /**
     * Process section content by replacing placeholders
     *
     * @param string $content Section content with placeholders
     * @param object $contract Contract object
     * @param object $client Client object
     * @param array $settings Business settings
     * @return string Processed content
     */
    public static function process_section_content($content, $contract = null, $client = null, $settings = array()) {
        // Get placeholder replacements
        $replacements = self::get_placeholder_replacements($contract);
        
        // Add client-specific placeholders - use contact override if available
        if ($client) {
            $dc = ($contract) ? self::get_display_contact($contract, $client) : null;
            $contact_name = $dc ? $dc->name : $client->name;
            $contact_email = $dc ? $dc->email : $client->email;
            $company_name = $client->company_name ?: ($dc ? $dc->name : $client->name);
            
            $replacements['[Client Company Name]'] = $company_name;
            $replacements['[Client Name]'] = $contact_name;
            $replacements['[Contact Name]'] = $contact_name;
            $replacements['[Email Address]'] = $contact_email;
            $replacements['[Client Email Address]'] = $contact_email;
        }
        
        // Add business settings placeholders
        if (!empty($settings)) {
            $replacements['[Your Name / Business Name]'] = $settings['business_name'] ?? '';
            $replacements['[Your Business Name]'] = $settings['business_name'] ?? '';
            $replacements['[Your Email Address]'] = $settings['business_email'] ?? '';
            $replacements['[Your Phone Number]'] = $settings['business_phone'] ?? '';
            $replacements['[Your Street Address]'] = $settings['business_address'] ?? '';
            $city_state_zip = trim(($settings['business_city'] ?? '') . ', ' . ($settings['business_state'] ?? '') . ' ' . ($settings['business_zip'] ?? ''), ', ');
            $replacements['[Your City, State, ZIP, Country]'] = $city_state_zip;
        }
        
        // Replace all placeholders
        foreach ($replacements as $placeholder => $value) {
            $content = str_replace($placeholder, $value, $content);
        }
        
        return $content;
    }

    /**
     * Get the resolved client contact info for display on a contract.
     * Returns override values if set, otherwise falls back to the client record.
     *
     * @param object $contract Contract object
     * @param object $client Client object
     * @return object Object with company_name, name, email, phone properties
     */
    public static function get_display_contact($contract, $client) {
        $contact = new stdClass();
        $contact->company_name = $client ? ($client->company_name ?: '') : '';

        // If contract has contact override fields, use them
        if (!empty($contract->contact_name)) {
            $contact->name = $contract->contact_name;
            $contact->email = $contract->contact_email ?: '';
            $contact->phone = $contract->contact_phone ?: '';
        } elseif ($client) {
            $contact->name = $client->name;
            $contact->email = $client->email ?: '';
            $contact->phone = $client->phone ?: '';
        } else {
            $contact->name = '';
            $contact->email = '';
            $contact->phone = '';
        }

        return $contact;
    }

    /**
     * Archive a contract
     */
    public static function archive($id) {
        global $wpdb;
        return $wpdb->update($wpdb->prefix . 'fs_contracts', array('archived' => 1), array('id' => $id), array('%d'), array('%d')) !== false;
    }

    /**
     * Unarchive a contract
     */
    public static function unarchive($id) {
        global $wpdb;
        return $wpdb->update($wpdb->prefix . 'fs_contracts', array('archived' => 0), array('id' => $id), array('%d'), array('%d')) !== false;
    }

    /**
     * Get count by archived status
     */
    public static function get_count($archived = false) {
        global $wpdb;
        $table = $wpdb->prefix . 'fs_contracts';
        if ($archived) {
            return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE archived = 1");
        }
        return (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE archived = 0 OR archived IS NULL");
    }
}
