<?php

declare(strict_types=1);

require_once __DIR__ . '/helpers.php';
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/Client.php';
require_once __DIR__ . '/ContractTemplate.php';
require_once __DIR__ . '/FileManager.php';
require_once __DIR__ . '/MultiTenant/TenantManager.php';
require_once __DIR__ . '/TenantDB.php';

class Contract
{
    public static function create(array $data): int
    {
        $pdo = getPDO();
        
        // Get current workspace company ID
        $companyId = null;
        $workspaceParam = $_GET['workspace'] ?? null;
        if ($workspaceParam) {
            $stmt = $pdo->prepare("SELECT id FROM companies WHERE username = ?");
            $stmt->execute([$workspaceParam]);
            $company = $stmt->fetch();
            $companyId = $company['id'] ?? null;
        }
        
        // Fallback to TenantManager
        if (!$companyId) {
            $companyId = TenantManager::getCurrentCompanyId();
        }
        
        if (!$companyId) {
            throw new Exception("No company context available for contract creation");
        }
        
        // Validate client_id exists and belongs to current company
        if (isset($data['client_id']) && $data['client_id'] > 0) {
            $stmt = $pdo->prepare('SELECT id FROM clients WHERE id = ? AND company_id = ?');
            $stmt->execute([$data['client_id'], $companyId]);
            if (!$stmt->fetch()) {
                throw new Exception('Invalid client ID or client not found in current workspace');
            }
        }
        
        // Validate proposal_id exists (if provided)
        if (isset($data['proposal_id']) && $data['proposal_id'] > 0) {
            $stmt = $pdo->prepare('SELECT id FROM proposals WHERE id = ? AND company_id = ?');
            $stmt->execute([$data['proposal_id'], $companyId]);
            if (!$stmt->fetch()) {
                $data['proposal_id'] = null; // Remove invalid proposal_id
            }
        }
        
        // Validate contract_template_id exists and belongs to current company (if provided)
        if (isset($data['contract_template_id']) && $data['contract_template_id'] > 0) {
            $stmt = $pdo->prepare('SELECT id FROM contract_templates WHERE id = ? AND company_id = ?');
            $stmt->execute([$data['contract_template_id'], $companyId]);
            if (!$stmt->fetch()) {
                $data['contract_template_id'] = null; // Remove invalid template_id
            }
        }
        
        // Generate signing tokens
        $clientToken = 'CLIENT_' . bin2hex(random_bytes(16));
        $internalToken = 'INTERNAL_' . bin2hex(random_bytes(16));
        
        // Generate workspace-specific contract number
        $contractNumber = self::generateContractNumber($data['workspace'] ?? null);
        
        // Insert contract with company_id
        $stmt = $pdo->prepare('INSERT INTO contracts (company_id, client_id, proposal_id, contract_template_id, title, contract_number, modules_json, variables_json, status, signing_token_client, signing_token_internal, created_at, updated_at) VALUES (:company_id, :client_id, :proposal_id, :contract_template_id, :title, :contract_number, :modules_json, :variables_json, :status, :signing_token_client, :signing_token_internal, :created_at, :updated_at)');
        
        $now = date('Y-m-d H:i:s');
        
        // Log the data being inserted for debugging
        $debugData = [
            'client_id' => $data['client_id'],
            'contract_template_id' => $data['contract_template_id'],
            'proposal_id' => $data['proposal_id'] ?? null,
            'title' => $data['title'] ?? 'Contract',
            'client_id_type' => gettype($data['client_id']),
            'contract_template_id_type' => gettype($data['contract_template_id']),
            'proposal_id_type' => gettype($data['proposal_id'] ?? null),
            'proposal_id_is_null' => is_null($data['proposal_id'] ?? null),
            'proposal_id_is_empty' => empty($data['proposal_id'] ?? null),
        ];
        error_log('Contract creation data: ' . print_r($debugData, true));
        
        try {
            // Ensure proposal_id is null if empty
            $proposalId = $data['proposal_id'] ?? null;
            if ($proposalId === '' || $proposalId === null) {
                $proposalId = null;
            }
            
            $stmt->execute([
                ':company_id' => $companyId,
                ':client_id' => $data['client_id'],
                ':proposal_id' => $data['proposal_id'] ?? null,
                ':contract_template_id' => $data['contract_template_id'] ?? null,
                ':title' => $data['title'] ?? 'Contract',
                ':contract_number' => $contractNumber,
                ':modules_json' => json_encode($data['modules_json'] ?? []),
                ':variables_json' => json_encode($data['variables_json'] ?? []),
                ':status' => $data['status'] ?? 'draft',
                ':signing_token_client' => $clientToken,
                ':signing_token_internal' => $internalToken,
                ':created_at' => $now,
                ':updated_at' => $now,
            ]);
        } catch (PDOException $e) {
            // Log the full error details
            $errorMessage = $e->getMessage();
            $errorCode = $e->getCode();
            error_log('PDO Error Details:');
            error_log('Error Code: ' . $errorCode);
            error_log('Error Message: ' . $errorMessage);
            error_log('Error Code Type: ' . gettype($errorCode));
            
            // For debugging, let's show the actual error
            throw new Exception('Database error: ' . $errorMessage . ' (Code: ' . $errorCode . ')');
            
            // Original error handling below (commented out for now)
            /*
            // Check if it's a foreign key constraint violation
            if ($errorCode == 23000 || strpos($errorMessage, 'FOREIGN KEY constraint failed') !== false) {
                error_log('Foreign key constraint error detected');
                
                // Check which foreign key failed - be more specific
                if (strpos($errorMessage, 'client_id') !== false || strpos($errorMessage, 'clients') !== false) {
                    throw new Exception('Invalid client selected - the client may not exist');
                } elseif (strpos($errorMessage, 'contract_template_id') !== false || strpos($errorMessage, 'contract_templates') !== false) {
                    throw new Exception('Invalid contract template selected - the template may not exist');
                } elseif (strpos($errorMessage, 'proposal_id') !== false || strpos($errorMessage, 'proposals') !== false) {
                    throw new Exception('Invalid proposal selected - the proposal may not exist');
                } else {
                    // Log the full error for debugging
                    error_log('Foreign key constraint failed with unknown field: ' . $errorMessage);
                    throw new Exception('Invalid reference data provided - please check all selected items exist');
                }
            }
            
            // If it's not a foreign key error, log and re-throw
            error_log('Non-foreign key PDO error: ' . $errorMessage);
            throw $e;
            */
        }
        
        $contractId = (int) $pdo->lastInsertId();
        
        // Generate initial PDF version
        try {
            self::generatePdf($contractId);
        } catch (Exception $e) {
            error_log('Initial PDF generation failed: ' . $e->getMessage());
        }
        
        return $contractId;
    }

    public static function find(int $id): ?array
    {
        $pdo = getPDO();
        $stmt = $pdo->prepare('SELECT c.*, cl.name as client_name, cl.company as client_company, cl.email as client_email FROM contracts c LEFT JOIN clients cl ON c.client_id = cl.id WHERE c.id = ?');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($row) {
            $row['modules_json'] = json_decode($row['modules_json'] ?? '[]', true);
            $row['variables_json'] = json_decode($row['variables_json'] ?? '{}', true);
        }
        
        return $row ?: null;
    }

    public static function all(): array
    {
        $pdo = getPDO();
        
        // Get current workspace company ID
        $companyId = null;
        $workspaceParam = $_GET['workspace'] ?? null;
        if ($workspaceParam) {
            $stmt = $pdo->prepare("SELECT id FROM companies WHERE username = ?");
            $stmt->execute([$workspaceParam]);
            $company = $stmt->fetch();
            $companyId = $company['id'] ?? null;
        }
        
        // Fallback to tenant context
        if (!$companyId) {
            $companyId = TenantDB::getTenant();
        }
        
        if (!$companyId) {
            return [];
        }
        
        $stmt = $pdo->prepare('SELECT c.*, cl.name as client_name, cl.company as client_company FROM contracts c LEFT JOIN clients cl ON c.client_id = cl.id WHERE c.company_id = ? ORDER BY c.created_at DESC');
        $stmt->execute([$companyId]);
        $all = $stmt->fetchAll(PDO::FETCH_ASSOC);

        foreach ($all as &$row) {
            $row['modules_json'] = json_decode($row['modules_json'] ?? '[]', true);
            $row['variables_json'] = json_decode($row['variables_json'] ?? '{}', true);
            $row['pricing_json'] = json_decode($row['pricing_json'] ?? '[]', true);
        }

        return $all;
    }

    public static function update(int $id, array $data): void
    {
        $pdo = getPDO();
        $fields = [];
        $values = [':id' => $id];

        if (isset($data['title'])) {
            $fields[] = 'title = :title';
            $values[':title'] = $data['title'];
        }
        if (isset($data['contract_template_id'])) {
            $fields[] = 'contract_template_id = :contract_template_id';
            $values[':contract_template_id'] = $data['contract_template_id'];
        }
        if (isset($data['proposal_id'])) {
            $fields[] = 'proposal_id = :proposal_id';
            $values[':proposal_id'] = $data['proposal_id'] ?: null;
        }
        if (isset($data['status'])) {
            $fields[] = 'status = :status';
            $values[':status'] = $data['status'];
        }
        if (isset($data['modules_json'])) {
            $fields[] = 'modules_json = :modules_json';
            $values[':modules_json'] = json_encode($data['modules_json']);
        }
        if (isset($data['variables_json'])) {
            $fields[] = 'variables_json = :variables_json';
            $values[':variables_json'] = json_encode($data['variables_json']);
        }
        if (isset($data['pricing_json'])) {
            $fields[] = 'pricing_json = :pricing_json';
            $values[':pricing_json'] = json_encode($data['pricing_json']);
        }
        if (isset($data['currency'])) {
            $fields[] = 'currency = :currency';
            $values[':currency'] = $data['currency'];
        }
        if (isset($data['client_signature_data'])) {
            $fields[] = 'client_signature_data = :client_signature_data';
            $values[':client_signature_data'] = $data['client_signature_data'];
        }
        if (isset($data['client_signed_at'])) {
            $fields[] = 'client_signed_at = :client_signed_at';
            $values[':client_signed_at'] = $data['client_signed_at'];
        }
        if (isset($data['client_ip'])) {
            $fields[] = 'client_ip = :client_ip';
            $values[':client_ip'] = $data['client_ip'];
        }
        if (isset($data['client_user_agent'])) {
            $fields[] = 'client_user_agent = :client_user_agent';
            $values[':client_user_agent'] = $data['client_user_agent'];
        }
        if (isset($data['internal_signature_data'])) {
            $fields[] = 'internal_signature_data = :internal_signature_data';
            $values[':internal_signature_data'] = $data['internal_signature_data'];
        }
        if (isset($data['internal_signed_at'])) {
            $fields[] = 'internal_signed_at = :internal_signed_at';
            $values[':internal_signed_at'] = $data['internal_signed_at'];
        }
        if (isset($data['internal_ip'])) {
            $fields[] = 'internal_ip = :internal_ip';
            $values[':internal_ip'] = $data['internal_ip'];
        }
        if (isset($data['internal_user_agent'])) {
            $fields[] = 'internal_user_agent = :internal_user_agent';
            $values[':internal_user_agent'] = $data['internal_user_agent'];
        }
        if (isset($data['executed_at'])) {
            $fields[] = 'executed_at = :executed_at';
            $values[':executed_at'] = $data['executed_at'];
        }
        if (isset($data['pdf_path'])) {
            $fields[] = 'pdf_path = :pdf_path';
            $values[':pdf_path'] = $data['pdf_path'];
        }

        $fields[] = 'updated_at = NOW()';

        // Check if contract is fully signed
        if (isset($data['client_signed_at']) && isset($data['internal_signed_at'])) {
            $fields[] = 'executed_at = NOW()';
            $fields[] = 'status = \'signed\'';
        }

        if (!empty($fields)) {
            $sql = 'UPDATE contracts SET ' . implode(', ', $fields) . ' WHERE id = :id';
            $stmt = $pdo->prepare($sql);
            try {
                $stmt->execute($values);
            } catch (PDOException $e) {
                $message = $e->getMessage();
                $isStatusTruncation =
                    isset($data['status'])
                    && is_string($data['status'])
                    && (stripos($message, "Data truncated for column 'status'") !== false
                        || stripos($message, 'Data truncated for column') !== false);

                if (!$isStatusTruncation) {
                    throw $e;
                }

                // Best-effort schema fix: expand ENUM to include the statuses the app supports.
                // Existing schemas may not include 'completed' and will fail updates.
                try {
                    $pdo->exec(
                        "ALTER TABLE contracts MODIFY status ENUM('draft','sent','signed','completed','expired','terminated') DEFAULT 'draft'"
                    );
                } catch (PDOException $schemaError) {
                    // If we can't alter schema (permissions/db type), rethrow original error.
                    throw $e;
                }

                // Retry once after schema update.
                $stmt = $pdo->prepare($sql);
                $stmt->execute($values);
            }
        }
    }

    public static function delete(int $id): void
    {
        // Get contract info to delete PDF file
        $contract = self::find($id);
        if ($contract && !empty($contract['pdf_path'])) {
            $pdfPath = __DIR__ . '/..' . $contract['pdf_path'];
            if (file_exists($pdfPath)) {
                unlink($pdfPath);
            }
        }
        
        $pdo = getPDO();
        $stmt = $pdo->prepare('DELETE FROM contracts WHERE id = ?');
        $stmt->execute([$id]);
    }

    public static function findByToken(string $token): ?array
    {
        $pdo = getPDO();
        $stmt = $pdo->prepare('SELECT c.*, cl.name as client_name, cl.company as client_company, cl.email as client_email FROM contracts c LEFT JOIN clients cl ON c.client_id = cl.id WHERE c.signing_token_client = ? OR c.signing_token_internal = ?');
        $stmt->execute([$token, $token]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($row) {
            $row['modules_json'] = json_decode($row['modules_json'] ?? '[]', true);
            $row['variables_json'] = json_decode($row['variables_json'] ?? '{}', true);
            $row['pricing_json'] = json_decode($row['pricing_json'] ?? '[]', true);
        }
        
        return $row ?: null;
    }

    public static function generateContractNumber(?string $workspace = null): string
    {
        $pdo = getPDO();
        
        // Get company ID from workspace
        $companyId = null;
        $workspaceParam = $workspace ?? $_GET['workspace'] ?? null;
        if ($workspaceParam) {
            $stmt = $pdo->prepare("SELECT id FROM companies WHERE username = ?");
            $stmt->execute([$workspaceParam]);
            $company = $stmt->fetch();
            $companyId = $company['id'] ?? null;
        }
        
        // Fallback to TenantDB
        if (!$companyId) {
            $companyId = TenantDB::getTenant();
        }
        
        if (!$companyId) {
            throw new Exception("No company context available for contract number generation");
        }
        
        // Count contracts for this company only
        $stmt = $pdo->prepare('SELECT COUNT(*) as count FROM contracts WHERE company_id = ?');
        $stmt->execute([$companyId]);
        $count = $stmt->fetch(PDO::FETCH_ASSOC)['count'] + 1;
        return "CTR-" . $count;
    }

    private static function generateSigningToken(): string
    {
        return bin2hex(random_bytes(32));
    }

    public static function generatePdf(int $id, ?int $version = null, ?string $workspace = null): string
    {
        $contract = self::find($id);
        if (!$contract) {
            throw new Exception('Contract not found');
        }

        // Load contract template if available
        $template = null;
        if (!empty($contract['contract_template_id'])) {
            $template = ContractTemplate::find($contract['contract_template_id']);
        }

        require_once __DIR__ . '/helpers.php';
        
        // Get client data
        $client = Client::find($contract['client_id']);
        
        if (!$client) {
            throw new Exception('Client not found');
        }

        // Calculate version if not provided
        if ($version === null) {
            $pdo = getPDO();
            $stmt = $pdo->prepare('SELECT pdf_path FROM contracts WHERE id = ?');
            $stmt->execute([$id]);
            $existingPath = $stmt->fetch(PDO::FETCH_ASSOC)['pdf_path'] ?? '';
            
            if ($existingPath && preg_match('/_v(\d+)\.pdf$/', $existingPath, $matches)) {
                $version = (int) $matches[1] + 1;
            } else {
                $version = 1;
            }
        }
        
        // Create company-specific storage directory if it doesn't exist
        $companyId = null;
        
        // Try to get company ID from workspace parameter
        $workspaceParam = $workspace ?? $_GET['workspace'] ?? null;
        if ($workspaceParam) {
            $pdo = getPDO();
            $stmt = $pdo->prepare("SELECT id FROM companies WHERE username = ?");
            $stmt->execute([$workspaceParam]);
            $company = $stmt->fetch();
            $companyId = $company['id'] ?? null;
        }
        
        // Fallback to TenantManager
        if (!$companyId) {
            $companyId = TenantManager::getCurrentCompanyId();
        }
        
        if (!$companyId) {
            throw new Exception("No company context available for PDF generation");
        }
        
        FileManager::ensureCompanyDirectories($companyId);

        // Delete existing PDFs if this is a signature update
        if ($version > 1) {
            // Keep old versions for audit trail, but update current path
            if ($contract['pdf_path']) {
                $existingPdfFilename = basename($contract['pdf_path']);
                $existingPdfPath = FileManager::getFilePath($companyId, 'contracts', $existingPdfFilename);
                if (file_exists($existingPdfPath)) {
                    // Archive old version
                    $oldVersion = $version - 1;
                    $oldFilename = pathinfo($existingPdfFilename, PATHINFO_FILENAME) . '_v' . $oldVersion . '.pdf';
                    $oldPath = FileManager::getFilePath($companyId, 'contracts', $oldFilename);
                    rename($existingPdfPath, $oldPath);
                }
            }
        }

        // Generate new PDF path with version using company-specific storage
        $filename = 'contract_' . $id . '_v' . $version . '.pdf';
        $pdfPath = '/storage/companies/' . $companyId . '/contracts/' . $filename;
        $fullPath = FileManager::getFilePath($companyId, 'contracts', $filename);

        // Get contract modules data
        $modules = is_array($contract['modules_json']) ? $contract['modules_json'] : [];
        
        // Get contract variables
        $variables = is_array($contract['variables_json']) ? $contract['variables_json'] : [];

        // Load linked proposal (if any) so we can show its number like proposals
        $linkedProposal = null;
        $linkedProposalNumber = null;
        if (!empty($contract['proposal_id'])) {
            require_once __DIR__ . '/Proposal.php';
            $linkedProposal = Proposal::find((int) $contract['proposal_id']);
            if ($linkedProposal) {
                $linkedProposalNumber = $linkedProposal['proposal_number'] ?? ('PRO-' . $linkedProposal['id']);
            }
        }
        
        // Create template variables for rendering
        $templateVariables = array_merge($variables, [
            'client_name' => $client['name'] ?? '',
            'client_company' => $client['company'] ?? '',
            'client_email' => $client['email'] ?? '',
            'client_phone' => $client['phone'] ?? '',
            'client_address' => $client['address'] ?? '',
            'contract_title' => $contract['title'] ?? '',
            'contract_number' => $contract['contract_number'] ?? 'CTR-' . $id,
            // Expose linked proposal number so templates can use {{proposal_number}}
            'proposal_number' => $linkedProposalNumber ?? '',
            'created_at' => date('F jS, Y', strtotime($contract['created_at'] ?? 'now')),
            // Party information from variables
            'party1_name' => $variables['party1_name'] ?? ($client['name'] ?? ''),
            'party1_email' => $variables['party1_email'] ?? ($client['email'] ?? ''),
            'party1_company' => $variables['party1_company'] ?? ($client['company'] ?? ''),
            'party1_phone' => $variables['party1_phone'] ?? ($client['phone'] ?? ''),
            'party1_address' => $variables['party1_address'] ?? ($client['address'] ?? ''),
            'party2_name' => $variables['party2_name'] ?? '',
            'party2_email' => $variables['party2_email'] ?? '',
            'party2_title' => $variables['party2_title'] ?? '',
            'party2_phone' => $variables['party2_phone'] ?? '',
            'party2_company' => $variables['party2_company'] ?? '',
        ]);

        // Build HTML for PDF - EXACT COPY OF PROPOSAL STRUCTURE
        $html = '<!DOCTYPE html>';
        $html .= '<html><head>';
        $html .= '<meta charset="UTF-8">';
        $html .= '<title>' . h($contract['title'] ?? 'Contract') . '</title>';
        $html .= '<style>';
        $html .= '@page { size: A4; margin: 10mm; }';
        $html .= 'body { font-family: Arial, sans-serif; font-size: 12px; line-height: 1.4; margin: 0; padding: 0; width: 190mm; }';
        $html .= '.header { width: 100%; padding: 1mm 0; }';
        $html .= '.footer { position: fixed; bottom: 0; left: 0; right: 0; width: 100%; padding: 2mm 0 8mm 0; border-top: 1px solid #e2e8f0; font-size: 10px; color: #666; }';
        $html .= '.content { width: 100%; padding: 1mm 0 25mm 0; min-height: 200mm; }';
        $html .= '.page-number { position: fixed; bottom: 2mm; left: 0; right: 0; text-align: center; font-size: 9px; color: #666; }';
        $html .= '.page-number:after { content: "Page " counter(page); }';
        $html .= '.client-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; padding: 16px; margin: 20px 0; word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= '.client-box h3 { margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: #0f172a; }';
        $html .= '.client-box p { margin: 6px 0; font-size: 12px; color: #64748b; word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= '.module { margin-bottom: 20px; page-break-inside: avoid; word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= '.module-content { font-size: 12px; line-height: 1.6; word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= 'img { max-width: 100%; height: auto; display: block; margin: 10px 0; }';
        $html .= 'table { width: 100%; border-collapse: collapse; margin: 10px 0; table-layout: fixed; }';
        $html .= 'table td, table th { border: 1px solid #e2e8f0; padding: 6px; font-size: 11px; word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= 'table th { background: #f8fafc; font-weight: bold; }';
        $html .= 'p { word-wrap: break-word; overflow-wrap: break-word; margin: 8px 0; }';
        $html .= 'div { word-wrap: break-word; overflow-wrap: break-word; }';
        $html .= '@page { margin: 10mm; }';
        $html .= '.page-break { page-break-before: always; padding-top: 15mm; }';
        $html .= '</style>';
        $html .= '</head><body>';

        // Header (dynamic height based on content) - EXACT SAME AS PROPOSAL
        $html .= '<div class="header">';
        $headerContent = renderWithVariables($template['header_html'] ?? '<h1 style="margin: 0; font-size: 10px; font-weight: bold;">' . h($template['name'] ?? 'Contract') . '</h1>', $templateVariables);
        $html .= $headerContent; // Don't force center - preserve TinyMCE alignment
        $html .= '</div>';

        // Main content area (defined body area) - EXACT SAME AS PROPOSAL
        $html .= '<div class="content">';

        // Client information box (no title) - EXACT SAME AS PROPOSAL
        $html .= '<div class="client-box">';
        if (!empty($client['name'])) {
            $html .= '<p><strong>Name:</strong> ' . h($client['name']) . '</p>';
        }
        if (!empty($client['company'])) {
            $html .= '<p><strong>Company:</strong> ' . h($client['company']) . '</p>';
        }
        if (!empty($client['email'])) {
            $html .= '<p><strong>Email:</strong> ' . h($client['email']) . '</p>';
        }
        if (!empty($client['phone'])) {
            $html .= '<p><strong>Phone:</strong> ' . h($client['phone']) . '</p>';
        }
        if (!empty($client['address'])) {
            $html .= '<p><strong>Address:</strong> ' . h($client['address']) . '</p>';
        }
        
        // Separator line inside box
        if (!empty($client['name']) || !empty($client['company']) || !empty($client['email']) || !empty($client['phone']) || !empty($client['address'])) {
            $html .= '<hr style="margin: 12px 0; border: none; border-top: 1px solid #e2e8f0;">';
        }
        
        // Header line with contract number, then proposal (if linked), then created date
        $html .= '<p style="margin: 0; font-size: 11px; color: #64748b;">Contract Number: ' . h($contract['contract_number'] ?? 'CTR-' . $id);
        if ($linkedProposalNumber) {
            $html .= ' | Proposal: ' . h($linkedProposalNumber);
        }
        $html .= ' | Created on ' . date('F jS, Y', strtotime($contract['created_at'] ?? 'now'));
        $html .= '</p>';
        $html .= '</div>';

        // Render all modules with proper spacing - EXACT SAME AS PROPOSAL
        if (!empty($modules)) {
            foreach ($modules as $index => $module) {
                $html .= '<div class="module">';
                
                // Debug: Log module structure
                error_log('Module structure: ' . json_encode($module));
                
                // Module content - check for description field
                $content = '';
                if (!empty($module['description'])) {
                    $content = renderWithVariables($module['description'], $templateVariables);
                } elseif (!empty($module['content_html'])) {
                    $content = renderWithVariables($module['content_html'], $templateVariables);
                } elseif (!empty($module['content'])) {
                    $content = renderWithVariables($module['content'], $templateVariables);
                }
                
                if (!empty($content)) {
                    $html .= '<div class="module-content">' . $content . '</div>';
                } else {
                    $html .= '<p style="color: #64748b; font-style: italic;">No content available for this module.</p>';
                }
                
                $html .= '</div>';
            }
        } else {
            $html .= '<div class="module">';
            $html .= '<p style="color: #64748b;">No modules have been added to this contract.</p>';
            $html .= '</div>';
        }

        // Add signature boxes for contracts
        $html .= '<div class="module" style="page-break-inside: avoid;">';
        $html .= '<h3 style="margin: 30px 0 15px 0; font-size: 18px; color: #0f172a; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;">Signatures</h3>';
        $html .= '<table style="width: 100%; border: none;">';
        $html .= '<tr>';
        
        // Client Signature - Only show if actually signed
        $html .= '<td style="width: 50%; vertical-align: top; padding: 20px; border: 1px solid #e2e8f0; text-align: center;">';
        $html .= '<p style="margin: 0 0 40px 0; font-weight: bold;">Client Signature</p>';
        $html .= '<p style="margin: 0 0 20px 0; font-size: 10px; color: #666;">' . h($templateVariables['party1_name']) . ($templateVariables['party1_company'] ? ' · ' . h($templateVariables['party1_company']) : '') . '</p>';
        
        // Only show signature if client has actually signed
        if (!empty($contract['client_signed_at']) && !empty($contract['client_signature_data'])) {
            $signatureData = json_decode($contract['client_signature_data'], true);
            if ($signatureData && !empty($signatureData['signature'])) {
                $html .= '<img src="' . h($signatureData['signature']) . '" style="max-width: 200px; max-height: 80px; margin-bottom: 10px;" />';
                $html .= '<p style="margin: 0; font-size: 11px;">Name: ' . h($signatureData['name']) . '</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: ' . date('F j, Y', strtotime($signatureData['timestamp'])) . '</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 10px; color: #666;">IP: ' . h($signatureData['ip']) . '</p>';
            } else {
                $html .= '<div style="border-bottom: 1px solid #000; height: 1px; margin: 0 0 10px 0;"></div>';
                $html .= '<p style="margin: 0; font-size: 11px;">Name: _________________________</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: _________________________</p>';
            }
        } else {
            // Unsigned - show placeholder lines
            $html .= '<div style="border-bottom: 1px solid #000; height: 1px; margin: 0 0 10px 0;"></div>';
            $html .= '<p style="margin: 0; font-size: 11px;">Name: _________________________</p>';
            $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: _________________________</p>';
        }
        
        $html .= '</td>';
        
        // Company Representative Signature - Only show if actually signed
        $html .= '<td style="width: 50%; vertical-align: top; padding: 20px; border: 1px solid #e2e8f0; text-align: center;">';
        $html .= '<p style="margin: 0 0 40px 0; font-weight: bold;">Company Representative</p>';
        $html .= '<p style="margin: 0 0 20px 0; font-size: 10px; color: #666;">' . h($templateVariables['party2_name']) . ($templateVariables['party2_title'] ? ' · ' . h($templateVariables['party2_title']) : '') . ($templateVariables['party2_company'] ? ' · ' . h($templateVariables['party2_company']) : '') . '</p>';
        
        // Only show signature if company has actually signed
        if (!empty($contract['internal_signed_at']) && !empty($contract['internal_signature_data'])) {
            $signatureData = json_decode($contract['internal_signature_data'], true);
            if ($signatureData && !empty($signatureData['signature'])) {
                $html .= '<img src="' . h($signatureData['signature']) . '" style="max-width: 200px; max-height: 80px; margin-bottom: 10px;" />';
                $html .= '<p style="margin: 0; font-size: 11px;">Name: ' . h($signatureData['name']) . '</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: ' . date('F j, Y', strtotime($signatureData['timestamp'])) . '</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 10px; color: #666;">IP: ' . h($signatureData['ip']) . '</p>';
            } else {
                $html .= '<div style="border-bottom: 1px solid #000; height: 1px; margin: 0 0 10px 0;"></div>';
                $html .= '<p style="margin: 0; font-size: 11px;">Name: _________________________</p>';
                $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: _________________________</p>';
            }
        } else {
            // Unsigned - show placeholder lines
            $html .= '<div style="border-bottom: 1px solid #000; height: 1px; margin: 0 0 10px 0;"></div>';
            $html .= '<p style="margin: 0; font-size: 11px;">Name: _________________________</p>';
            $html .= '<p style="margin: 5px 0 0 0; font-size: 11px;">Date: _________________________</p>';
        }
        
        $html .= '</td>';
        $html .= '</tr>';
        $html .= '</table>';
        $html .= '</div>';

        $html .= '</div>'; // End content

        // Footer (fixed at bottom, dynamic height) - EXACT SAME AS PROPOSAL
        $html .= '<div class="footer">';
        $footerContent = renderWithVariables($template['footer_html'] ?? '<p style="margin: 0; font-size: 9px;">' . h($template['name'] ?? 'Contract') . '</p>', $templateVariables);
        $html .= $footerContent; // Don't force center - preserve TinyMCE alignment
        $html .= '</div>';

        // Page number - use CSS content - EXACT SAME AS PROPOSAL
        $html .= '<div class="page-number"></div>';
        $html .= '</body></html>';

        // Use DomPDF to generate PDF - EXACT SAME AS PROPOSAL
        require_once __DIR__ . '/../vendor/autoload.php';
        if (!class_exists('Dompdf\Dompdf')) {
            throw new Exception('DomPDF library not found. Please install with: composer require dompdf/dompdf');
        }

        $options = new \Dompdf\Options();
        $options->set('defaultFont', 'Arial');
        $options->set('isRemoteEnabled', true);
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isPhpEnabled', true);
        $options->set('defaultPaperSize', 'a4');
        $options->set('defaultPaperOrientation', 'portrait');
        $options->set('margin-top', '20mm');
        $options->set('margin-bottom', '20mm');
        $options->set('margin-left', '20mm');
        $options->set('margin-right', '20mm');
        
        $dompdf = new \Dompdf\Dompdf($options);
        $dompdf->loadHtml($html);
        $dompdf->setPaper('A4', 'portrait');
        $dompdf->render();
        
        // Save PDF
        file_put_contents($fullPath, $dompdf->output());
        
        // Update contract with PDF path
        self::update($id, ['pdf_path' => $pdfPath]);
        
        return $fullPath;
    }
}
