/**
 * Descomplicar® Crescimento Digital
 * https://descomplicar.pt
 */

<?php

defined('BASEPATH') or exit('No direct script access allowed');

require_once(__DIR__ . '/../libraries/DocumentAccessControl.php');
require_once(__DIR__ . '/../libraries/ClientNotificationService.php');

/**
 * Desk-Moloni Client Portal Controller
 * Handles client-facing document access and portal functionality
 * 
 * @package    Desk-Moloni
 * @version    3.0.0
 * @author     Descomplicar Business Solutions
 */
class ClientPortalController extends ClientsController
{
    private $documentAccessControl;
    private $notificationService;
    private $currentClient;
    private $rateLimiter;

    public function __construct()
    {
        parent::__construct();
        
        // Load required models
        $this->load->model('desk_moloni/desk_moloni_model');
        $this->load->model('desk_moloni/desk_moloni_mapping_model');
        $this->load->model('desk_moloni/desk_moloni_sync_log_model');
        $this->load->model('clients_model');
        $this->load->model('invoices_model');
        $this->load->model('estimates_model');
        
        // Load libraries
        $this->load->library('form_validation');
        $this->load->helper('security');
        
        // Initialize document access control
        $this->documentAccessControl = new DocumentAccessControl();
        
        // Initialize notification service
        $this->notificationService = new ClientNotificationService();
        
        // Initialize rate limiter
        $this->_initializeRateLimiter();
        
        // Authenticate client and set up session
        $this->_authenticateClient();
    }

    /**
     * List Client Documents
     * GET /clients/desk_moloni/documents
     */
    public function documents(): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('documents_list', 60, 100)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            // Get and validate query parameters
            $filters = $this->_getDocumentFilters();
            $pagination = $this->_getPaginationParams();
            
            // Get client documents
            $documents = $this->_getClientDocuments($filters, $pagination);
            $totalCount = $this->_getClientDocumentsCount($filters);
            
            // Build response
            $response = [
                'data' => $documents,
                'pagination' => $this->_buildPaginationResponse($pagination, $totalCount),
                'filters' => $this->_getAvailableFilters()
            ];
            
            // Log access
            $this->_logDocumentAccess('list', null, 'success');
            
            $this->_respondWithSuccess($response);
            
        } catch (Exception $e) {
            log_message('error', 'Client portal documents error: ' . $e->getMessage());
            $this->_logDocumentAccess('list', null, 'error', $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * Get Document Details
     * GET /clients/desk_moloni/documents/{document_id}
     */
    public function document_details(int $documentId): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('document_details', 30, 50)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            // Validate document ID
            if (!is_numeric($documentId) || $documentId <= 0) {
                $this->_respondWithError('Invalid document ID', 400);
                return;
            }
            
            // Check document access permissions
            if (!$this->documentAccessControl->canAccessDocument($this->currentClient['userid'], $documentId)) {
                $this->_logDocumentAccess('view', $documentId, 'unauthorized');
                $this->_respondWithError('Access denied', 403);
                return;
            }
            
            // Get document details
            $document = $this->_getDocumentDetails($documentId);
            
            if (!$document) {
                $this->_respondWithError('Document not found', 404);
                return;
            }
            
            // Log successful access
            $this->_logDocumentAccess('view', $documentId, 'success');
            
            $this->_respondWithSuccess($document);
            
        } catch (Exception $e) {
            log_message('error', 'Client portal document details error: ' . $e->getMessage());
            $this->_logDocumentAccess('view', $documentId, 'error', $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * Download Document PDF
     * GET /clients/desk_moloni/documents/{document_id}/download
     */
    public function download_document(int $documentId): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('document_download', 10, 20)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            // Validate document ID
            if (!is_numeric($documentId) || $documentId <= 0) {
                $this->_respondWithError('Invalid document ID', 400);
                return;
            }
            
            // Check document access permissions
            if (!$this->documentAccessControl->canAccessDocument($this->currentClient['userid'], $documentId)) {
                $this->_logDocumentAccess('download', $documentId, 'unauthorized');
                $this->_respondWithError('Access denied', 403);
                return;
            }
            
            // Get document and PDF path
            $document = $this->_getDocumentForDownload($documentId);
            
            if (!$document || !$document['has_pdf']) {
                $this->_respondWithError('Document or PDF not found', 404);
                return;
            }
            
            // Serve PDF file
            $this->_servePDFFile($document, 'attachment');
            
            // Log successful download
            $this->_logDocumentAccess('download', $documentId, 'success');
            
        } catch (Exception $e) {
            log_message('error', 'Client portal document download error: ' . $e->getMessage());
            $this->_logDocumentAccess('download', $documentId, 'error', $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * View Document PDF (inline)
     * GET /clients/desk_moloni/documents/{document_id}/view
     */
    public function view_document(int $documentId): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('document_view', 30, 100)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            // Validate document ID
            if (!is_numeric($documentId) || $documentId <= 0) {
                $this->_respondWithError('Invalid document ID', 400);
                return;
            }
            
            // Check document access permissions
            if (!$this->documentAccessControl->canAccessDocument($this->currentClient['userid'], $documentId)) {
                $this->_logDocumentAccess('view_pdf', $documentId, 'unauthorized');
                $this->_respondWithError('Access denied', 403);
                return;
            }
            
            // Get document and PDF path
            $document = $this->_getDocumentForDownload($documentId);
            
            if (!$document || !$document['has_pdf']) {
                $this->_respondWithError('Document or PDF not found', 404);
                return;
            }
            
            // Serve PDF file for inline viewing
            $this->_servePDFFile($document, 'inline');
            
            // Log successful view
            $this->_logDocumentAccess('view_pdf', $documentId, 'success');
            
        } catch (Exception $e) {
            log_message('error', 'Client portal document view error: ' . $e->getMessage());
            $this->_logDocumentAccess('view_pdf', $documentId, 'error', $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * Get Client Dashboard Data
     * GET /clients/desk_moloni/dashboard
     */
    public function dashboard(): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('dashboard', 60, 200)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            $dashboard = [
                'summary' => $this->_getDashboardSummary(),
                'recent_documents' => $this->_getRecentDocuments(10),
                'payment_status' => $this->_getPaymentStatusStats(),
                'monthly_totals' => $this->_getMonthlyTotals(12)
            ];
            
            // Log dashboard access
            $this->_logDocumentAccess('dashboard', null, 'success');
            
            $this->_respondWithSuccess($dashboard);
            
        } catch (Exception $e) {
            log_message('error', 'Client portal dashboard error: ' . $e->getMessage());
            $this->_logDocumentAccess('dashboard', null, 'error', $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * Get Client Notifications
     * GET /clients/desk_moloni/notifications
     */
    public function notifications(): void
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('notifications', 60, 100)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            $unreadOnly = $this->input->get('unread_only') === 'true';
            $limit = (int) $this->input->get('limit') ?: 10;
            $limit = min($limit, 50); // Max 50 notifications
            
            $notifications = $this->_getClientNotifications($unreadOnly, $limit);
            $unreadCount = $this->_getUnreadNotificationsCount();
            
            $response = [
                'notifications' => $notifications,
                'unread_count' => $unreadCount
            ];
            
            $this->_respondWithSuccess($response);
            
        } catch (Exception $e) {
            log_message('error', 'Client portal notifications error: ' . $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    /**
     * Mark Notification as Read
     * POST /clients/desk_moloni/notifications/{notification_id}/mark_read
     */
    public function mark_notification_read($notificationId)
    {
        // Rate limiting check
        if (!$this->_checkRateLimit('mark_notification', 30, 50)) {
            $this->_respondWithError('Rate limit exceeded', 429);
            return;
        }

        try {
            // Validate notification ID
            if (!is_numeric($notificationId) || $notificationId <= 0) {
                $this->_respondWithError('Invalid notification ID', 400);
                return;
            }
            
            // Check if notification belongs to current client
            if (!$this->_canAccessNotification($notificationId)) {
                $this->_respondWithError('Access denied', 403);
                return;
            }
            
            // Mark as read
            $success = $this->_markNotificationAsRead($notificationId);
            
            if ($success) {
                $this->_respondWithSuccess(['message' => 'Notification marked as read']);
            } else {
                $this->_respondWithError('Failed to mark notification as read', 500);
            }
            
        } catch (Exception $e) {
            log_message('error', 'Client portal mark notification error: ' . $e->getMessage());
            $this->_respondWithError($e->getMessage(), 500);
        }
    }

    // Private Methods

    /**
     * Authenticate client and set up session
     */
    private function _authenticateClient()
    {
        // Check if client is logged in through Perfex CRM client portal
        if (!is_client_logged_in()) {
            $this->_respondWithError('Client authentication required', 401);
            return;
        }
        
        // Get current client
        $clientId = get_client_user_id();
        $this->currentClient = $this->clients_model->get($clientId);
        
        if (!$this->currentClient) {
            $this->_respondWithError('Invalid client session', 401);
            return;
        }
        
        // Verify client is active
        if ($this->currentClient['active'] != 1) {
            $this->_respondWithError('Client account is not active', 403);
            return;
        }
    }

    /**
     * Initialize rate limiter
     */
    private function _initializeRateLimiter()
    {
        $this->load->driver('cache', ['adapter' => 'redis']);
        if (!$this->cache->is_supported('redis')) {
            // Fallback to file cache
            $this->load->driver('cache', ['adapter' => 'file']);
        }
    }

    /**
     * Check rate limit
     */
    private function _checkRateLimit($action, $windowSeconds, $maxRequests)
    {
        if (!$this->currentClient) {
            return true; // Skip rate limiting if no client authenticated yet
        }
        
        $clientId = $this->currentClient['userid'];
        $clientIp = $this->input->ip_address();
        
        // Create rate limit key
        $key = "rate_limit_{$action}_{$clientId}_{$clientIp}_" . floor(time() / $windowSeconds);
        
        // Get current count
        $currentCount = (int) $this->cache->get($key);
        
        if ($currentCount >= $maxRequests) {
            return false;
        }
        
        // Increment count
        $this->cache->save($key, $currentCount + 1, $windowSeconds);
        
        return true;
    }

    /**
     * Get document filters from query parameters
     */
    private function _getDocumentFilters()
    {
        $filters = [];
        
        // Document type filter
        $type = $this->input->get('type');
        if ($type && in_array($type, ['invoice', 'estimate', 'credit_note', 'receipt'])) {
            $filters['type'] = $type;
        }
        
        // Status filter
        $status = $this->input->get('status');
        if ($status && in_array($status, ['paid', 'unpaid', 'overdue', 'draft', 'pending'])) {
            $filters['status'] = $status;
        }
        
        // Date range filters
        $fromDate = $this->input->get('from_date');
        if ($fromDate && $this->_isValidDate($fromDate)) {
            $filters['from_date'] = $fromDate;
        }
        
        $toDate = $this->input->get('to_date');
        if ($toDate && $this->_isValidDate($toDate)) {
            $filters['to_date'] = $toDate;
        }
        
        // Search filter
        $search = $this->input->get('search');
        if ($search) {
            $filters['search'] = $this->security->xss_clean(trim($search));
        }
        
        return $filters;
    }

    /**
     * Get pagination parameters
     */
    private function _getPaginationParams()
    {
        $page = max(1, (int) $this->input->get('page'));
        $perPage = min(100, max(1, (int) $this->input->get('per_page') ?: 20));
        
        return [
            'page' => $page,
            'per_page' => $perPage,
            'offset' => ($page - 1) * $perPage
        ];
    }

    /**
     * Get client documents
     */
    private function _getClientDocuments($filters, $pagination)
    {
        $clientId = $this->currentClient['userid'];
        $documents = [];
        
        // Get invoices
        if (!isset($filters['type']) || $filters['type'] === 'invoice') {
            $invoices = $this->_getFilteredInvoices($clientId, $filters, $pagination);
            $documents = array_merge($documents, $invoices);
        }
        
        // Get estimates
        if (!isset($filters['type']) || $filters['type'] === 'estimate') {
            $estimates = $this->_getFilteredEstimates($clientId, $filters, $pagination);
            $documents = array_merge($documents, $estimates);
        }
        
        // Sort by date (newest first)
        usort($documents, function($a, $b) {
            return strtotime($b['date']) - strtotime($a['date']);
        });
        
        // Apply pagination to combined results
        $offset = $pagination['offset'];
        $perPage = $pagination['per_page'];
        
        return array_slice($documents, $offset, $perPage);
    }

    /**
     * Get filtered invoices
     */
    private function _getFilteredInvoices($clientId, $filters, $pagination)
    {
        $invoices = $this->invoices_model->get('', [
            'clientid' => $clientId
        ]);
        
        $documents = [];
        foreach ($invoices as $invoice) {
            // Apply filters
            if (isset($filters['status']) && $this->_getInvoiceStatus($invoice) !== $filters['status']) {
                continue;
            }
            
            if (isset($filters['from_date']) && $invoice['date'] < $filters['from_date']) {
                continue;
            }
            
            if (isset($filters['to_date']) && $invoice['date'] > $filters['to_date']) {
                continue;
            }
            
            if (isset($filters['search']) && !$this->_documentMatchesSearch($invoice, $filters['search'])) {
                continue;
            }
            
            $documents[] = $this->_formatDocumentSummary($invoice, 'invoice');
        }
        
        return $documents;
    }

    /**
     * Get filtered estimates
     */
    private function _getFilteredEstimates($clientId, $filters, $pagination)
    {
        $estimates = $this->estimates_model->get('', [
            'clientid' => $clientId
        ]);
        
        $documents = [];
        foreach ($estimates as $estimate) {
            // Apply filters
            if (isset($filters['status']) && $this->_getEstimateStatus($estimate) !== $filters['status']) {
                continue;
            }
            
            if (isset($filters['from_date']) && $estimate['date'] < $filters['from_date']) {
                continue;
            }
            
            if (isset($filters['to_date']) && $estimate['date'] > $filters['to_date']) {
                continue;
            }
            
            if (isset($filters['search']) && !$this->_documentMatchesSearch($estimate, $filters['search'])) {
                continue;
            }
            
            $documents[] = $this->_formatDocumentSummary($estimate, 'estimate');
        }
        
        return $documents;
    }

    /**
     * Get client documents count
     */
    private function _getClientDocumentsCount($filters)
    {
        $clientId = $this->currentClient['userid'];
        $count = 0;
        
        // Count invoices
        if (!isset($filters['type']) || $filters['type'] === 'invoice') {
            $invoices = $this->_getFilteredInvoices($clientId, $filters, ['offset' => 0, 'per_page' => 999999]);
            $count += count($invoices);
        }
        
        // Count estimates
        if (!isset($filters['type']) || $filters['type'] === 'estimate') {
            $estimates = $this->_getFilteredEstimates($clientId, $filters, ['offset' => 0, 'per_page' => 999999]);
            $count += count($estimates);
        }
        
        return $count;
    }

    /**
     * Format document summary
     */
    private function _formatDocumentSummary($document, $type)
    {
        $baseUrl = site_url("clients/desk_moloni/documents/{$document['id']}");
        
        return [
            'id' => (int) $document['id'],
            'type' => $type,
            'number' => $document['number'] ?? '',
            'date' => $document['date'],
            'due_date' => $document['duedate'] ?? null,
            'amount' => (float) ($document['subtotal'] ?? 0),
            'currency' => get_base_currency()->name,
            'status' => $type === 'invoice' ? $this->_getInvoiceStatus($document) : $this->_getEstimateStatus($document),
            'moloni_id' => $this->_getMoloniId($type, $document['id']),
            'has_pdf' => $this->_documentHasPDF($type, $document['id']),
            'pdf_url' => $baseUrl,
            'view_url' => $baseUrl . '/view',
            'download_url' => $baseUrl . '/download',
            'created_at' => $document['datecreated']
        ];
    }

    /**
     * Get document details
     */
    private function _getDocumentDetails($documentId)
    {
        // Try to find in invoices first
        $invoice = $this->invoices_model->get($documentId);
        if ($invoice && $invoice['clientid'] == $this->currentClient['userid']) {
            return $this->_formatDocumentDetails($invoice, 'invoice');
        }
        
        // Try estimates
        $estimate = $this->estimates_model->get($documentId);
        if ($estimate && $estimate['clientid'] == $this->currentClient['userid']) {
            return $this->_formatDocumentDetails($estimate, 'estimate');
        }
        
        return null;
    }

    /**
     * Format document details
     */
    private function _formatDocumentDetails($document, $type)
    {
        $baseUrl = site_url("clients/desk_moloni/documents/{$document['id']}");
        
        $details = [
            'id' => (int) $document['id'],
            'type' => $type,
            'number' => $document['number'] ?? '',
            'date' => $document['date'],
            'due_date' => $document['duedate'] ?? null,
            'amount' => (float) ($document['subtotal'] ?? 0),
            'tax_amount' => (float) ($document['total_tax'] ?? 0),
            'total_amount' => (float) ($document['total'] ?? 0),
            'currency' => get_base_currency()->name,
            'status' => $type === 'invoice' ? $this->_getInvoiceStatus($document) : $this->_getEstimateStatus($document),
            'moloni_id' => $this->_getMoloniId($type, $document['id']),
            'notes' => $document['adminnote'] ?? '',
            'items' => $this->_getDocumentItems($type, $document['id']),
            'payment_info' => $type === 'invoice' ? $this->_getPaymentInfo($document) : null,
            'has_pdf' => $this->_documentHasPDF($type, $document['id']),
            'pdf_url' => $baseUrl,
            'view_url' => $baseUrl . '/view',
            'download_url' => $baseUrl . '/download',
            'created_at' => $document['datecreated'],
            'updated_at' => $document['date']
        ];
        
        return $details;
    }

    /**
     * Get dashboard summary
     */
    private function _getDashboardSummary()
    {
        $clientId = $this->currentClient['userid'];
        
        // Get all client invoices
        $invoices = $this->invoices_model->get('', ['clientid' => $clientId]);
        
        $summary = [
            'total_documents' => 0,
            'pending_payments' => 0,
            'overdue_documents' => 0,
            'total_amount_due' => 0.0,
            'total_paid_this_year' => 0.0
        ];
        
        $currentYear = date('Y');
        
        foreach ($invoices as $invoice) {
            $summary['total_documents']++;
            
            $status = $this->_getInvoiceStatus($invoice);
            
            if ($status === 'unpaid') {
                $summary['pending_payments']++;
                $summary['total_amount_due'] += (float) $invoice['total'];
            } elseif ($status === 'overdue') {
                $summary['overdue_documents']++;
                $summary['total_amount_due'] += (float) $invoice['total'];
            } elseif ($status === 'paid' && date('Y', strtotime($invoice['date'])) === $currentYear) {
                $summary['total_paid_this_year'] += (float) $invoice['total'];
            }
        }
        
        // Add estimates
        $estimates = $this->estimates_model->get('', ['clientid' => $clientId]);
        $summary['total_documents'] += count($estimates);
        
        return $summary;
    }

    /**
     * Get recent documents
     */
    private function _getRecentDocuments($limit = 10)
    {
        $documents = $this->_getClientDocuments([], ['offset' => 0, 'per_page' => $limit]);
        return array_slice($documents, 0, $limit);
    }

    /**
     * Get payment status stats
     */
    private function _getPaymentStatusStats()
    {
        $clientId = $this->currentClient['userid'];
        $invoices = $this->invoices_model->get('', ['clientid' => $clientId]);
        
        $stats = [
            'paid' => 0,
            'unpaid' => 0,
            'overdue' => 0
        ];
        
        foreach ($invoices as $invoice) {
            $status = $this->_getInvoiceStatus($invoice);
            if (isset($stats[$status])) {
                $stats[$status]++;
            }
        }
        
        return $stats;
    }

    /**
     * Get monthly totals
     */
    private function _getMonthlyTotals($months = 12)
    {
        $clientId = $this->currentClient['userid'];
        $invoices = $this->invoices_model->get('', ['clientid' => $clientId]);
        
        $totals = [];
        
        // Initialize last 12 months
        for ($i = $months - 1; $i >= 0; $i--) {
            $month = date('Y-m', strtotime("-{$i} months"));
            $totals[] = [
                'month' => $month,
                'total' => 0.0
            ];
        }
        
        // Calculate totals
        foreach ($invoices as $invoice) {
            if ($this->_getInvoiceStatus($invoice) === 'paid') {
                $invoiceMonth = date('Y-m', strtotime($invoice['date']));
                
                for ($i = 0; $i < count($totals); $i++) {
                    if ($totals[$i]['month'] === $invoiceMonth) {
                        $totals[$i]['total'] += (float) $invoice['total'];
                        break;
                    }
                }
            }
        }
        
        return $totals;
    }

    /**
     * Serve PDF file
     */
    private function _servePDFFile($document, $disposition = 'attachment')
    {
        $pdfPath = $this->_getPDFPath($document['type'], $document['id']);
        
        if (!file_exists($pdfPath)) {
            // Generate PDF if it doesn't exist
            $this->_generateDocumentPDF($document['type'], $document['id']);
        }
        
        if (!file_exists($pdfPath)) {
            throw new Exception('PDF file not found or could not be generated');
        }
        
        // Security check - ensure file is within allowed directory
        $realPath = realpath($pdfPath);
        $allowedPath = realpath(FCPATH . 'uploads/desk_moloni/');
        
        if (strpos($realPath, $allowedPath) !== 0) {
            throw new Exception('Access denied to file location');
        }
        
        // Set headers for PDF download/view
        $filename = $this->_generatePDFFilename($document);
        
        header('Content-Type: application/pdf');
        header("Content-Disposition: {$disposition}; filename=\"{$filename}\"");
        header('Content-Length: ' . filesize($pdfPath));
        header('Cache-Control: private, max-age=0, must-revalidate');
        header('Pragma: public');
        
        // Output file
        readfile($pdfPath);
        exit;
    }

    /**
     * Log document access
     */
    private function _logDocumentAccess($action, $documentId, $status, $errorMessage = null)
    {
        $logData = [
            'client_id' => $this->currentClient['userid'],
            'action' => $action,
            'document_id' => $documentId,
            'status' => $status,
            'error_message' => $errorMessage,
            'ip_address' => $this->input->ip_address(),
            'user_agent' => $this->input->user_agent(),
            'timestamp' => date('Y-m-d H:i:s')
        ];
        
        // Use existing sync log model for audit trail
        $this->desk_moloni_sync_log_model->logClientPortalAccess($logData);
    }

    /**
     * Respond with success
     */
    private function _respondWithSuccess($data)
    {
        $this->output
            ->set_content_type('application/json')
            ->set_status_header(200)
            ->set_output(json_encode([
                'success' => true,
                'data' => $data
            ]));
    }

    /**
     * Respond with error
     */
    private function _respondWithError($message, $statusCode = 400)
    {
        $this->output
            ->set_content_type('application/json')
            ->set_status_header($statusCode)
            ->set_output(json_encode([
                'success' => false,
                'message' => $message
            ]));
    }

    // Helper methods for document status, PDF generation, etc.
    // These would be implemented based on Perfex CRM specific logic

    private function _getInvoiceStatus($invoice)
    {
        // Implement Perfex CRM invoice status logic
        if ($invoice['status'] == 2) return 'paid';
        if ($invoice['status'] == 4) return 'overdue';
        if ($invoice['status'] == 1) return 'unpaid';
        return 'draft';
    }

    private function _getEstimateStatus($estimate)
    {
        // Implement Perfex CRM estimate status logic
        if ($estimate['status'] == 4) return 'paid';
        if ($estimate['status'] == 3) return 'draft';
        return 'pending';
    }

    private function _getMoloniId($type, $perfexId)
    {
        $mapping = $this->desk_moloni_mapping_model->getMappingByPerfexId($type, $perfexId);
        return $mapping ? (int) $mapping['moloni_id'] : null;
    }

    private function _documentHasPDF($type, $id)
    {
        $pdfPath = $this->_getPDFPath($type, $id);
        return file_exists($pdfPath);
    }

    private function _getPDFPath($type, $id)
    {
        $uploadsPath = FCPATH . 'uploads/desk_moloni/pdfs/';
        if (!is_dir($uploadsPath)) {
            mkdir($uploadsPath, 0755, true);
        }
        return $uploadsPath . "{$type}_{$id}.pdf";
    }

    private function _generateDocumentPDF($type, $id)
    {
        // Implementation would depend on Perfex CRM PDF generation system
        // This is a placeholder for the actual PDF generation logic
        return true;
    }

    private function _generatePDFFilename($document)
    {
        $type = ucfirst($document['type']);
        $number = preg_replace('/[^a-zA-Z0-9_-]/', '_', $document['number']);
        return "{$type}_{$number}.pdf";
    }

    private function _isValidDate($date)
    {
        return DateTime::createFromFormat('Y-m-d', $date) !== false;
    }

    private function _documentMatchesSearch($document, $search)
    {
        $searchLower = strtolower($search);
        $fields = ['number', 'clientnote', 'adminnote'];
        
        foreach ($fields as $field) {
            if (isset($document[$field]) && strpos(strtolower($document[$field]), $searchLower) !== false) {
                return true;
            }
        }
        
        return false;
    }

    private function _buildPaginationResponse($pagination, $totalCount)
    {
        $totalPages = ceil($totalCount / $pagination['per_page']);
        
        return [
            'current_page' => $pagination['page'],
            'per_page' => $pagination['per_page'],
            'total' => $totalCount,
            'total_pages' => $totalPages,
            'has_previous' => $pagination['page'] > 1,
            'has_next' => $pagination['page'] < $totalPages
        ];
    }

    private function _getAvailableFilters()
    {
        return [
            'available_types' => ['invoice', 'estimate'],
            'available_statuses' => ['paid', 'unpaid', 'overdue', 'draft', 'pending'],
            'date_range' => [
                'min_date' => date('Y-01-01', strtotime('-2 years')),
                'max_date' => date('Y-m-d')
            ]
        ];
    }

    private function _getDocumentForDownload($documentId)
    {
        $document = $this->_getDocumentDetails($documentId);
        return $document;
    }

    private function _getDocumentItems($type, $id)
    {
        // Get line items for document
        if ($type === 'invoice') {
            $items = $this->db->get_where('tblinvoiceitems', ['invoiceid' => $id])->result_array();
        } else {
            $items = $this->db->get_where('tblestimate_items', ['estimateid' => $id])->result_array();
        }
        
        $formattedItems = [];
        foreach ($items as $item) {
            $formattedItems[] = [
                'name' => $item['description'],
                'description' => $item['long_description'] ?? '',
                'quantity' => (float) $item['qty'],
                'unit_price' => (float) $item['rate'],
                'discount' => 0.0, // Perfex CRM handles discounts differently
                'tax_rate' => (float) ($item['taxrate'] ?? 0),
                'subtotal' => (float) $item['qty'] * (float) $item['rate']
            ];
        }
        
        return $formattedItems;
    }

    private function _getPaymentInfo($invoice)
    {
        // Get payment information for invoice
        $payments = $this->db->get_where('tblinvoicepaymentrecords', ['invoiceid' => $invoice['id']])->result_array();
        
        $totalPaid = 0;
        $lastPayment = null;
        
        foreach ($payments as $payment) {
            $totalPaid += (float) $payment['amount'];
            if (!$lastPayment || strtotime($payment['date']) > strtotime($lastPayment['date'])) {
                $lastPayment = $payment;
            }
        }
        
        return [
            'payment_date' => $lastPayment ? $lastPayment['date'] : null,
            'payment_method' => $lastPayment ? $lastPayment['paymentmethod'] : null,
            'paid_amount' => $totalPaid,
            'remaining_amount' => (float) $invoice['total'] - $totalPaid
        ];
    }

    private function _getClientNotifications($unreadOnly, $limit)
    {
        $clientId = $this->currentClient['userid'];
        return $this->notificationService->getClientNotifications($clientId, $unreadOnly, $limit);
    }

    private function _getUnreadNotificationsCount()
    {
        $clientId = $this->currentClient['userid'];
        return $this->notificationService->getUnreadCount($clientId);
    }

    private function _canAccessNotification($notificationId)
    {
        $clientId = $this->currentClient['userid'];
        $notification = $this->notificationService->getNotificationById($notificationId, $clientId);
        return $notification !== null;
    }

    private function _markNotificationAsRead($notificationId)
    {
        $clientId = $this->currentClient['userid'];
        return $this->notificationService->markAsRead($notificationId, $clientId);
    }

    /**
     * Health check endpoint
     * GET /clients/desk_moloni/health
     */
    public function health_check()
    {
        try {
            $health = [
                'status' => 'healthy',
                'timestamp' => date('Y-m-d H:i:s'),
                'version' => '3.0.0',
                'checks' => [
                    'database' => $this->_checkDatabaseHealth(),
                    'auth' => $this->_checkAuthHealth(),
                    'permissions' => $this->_checkPermissionsHealth(),
                    'notifications' => $this->_checkNotificationsHealth()
                ]
            ];

            // Overall status based on individual checks
            $allHealthy = true;
            foreach ($health['checks'] as $check) {
                if ($check['status'] !== 'healthy') {
                    $allHealthy = false;
                    break;
                }
            }

            $health['status'] = $allHealthy ? 'healthy' : 'degraded';
            $statusCode = $allHealthy ? 200 : 503;

            $this->output
                ->set_content_type('application/json')
                ->set_status_header($statusCode)
                ->set_output(json_encode($health));

        } catch (Exception $e) {
            $this->output
                ->set_content_type('application/json')
                ->set_status_header(503)
                ->set_output(json_encode([
                    'status' => 'unhealthy',
                    'error' => $e->getMessage(),
                    'timestamp' => date('Y-m-d H:i:s')
                ]));
        }
    }

    /**
     * Status endpoint for monitoring
     * GET /clients/desk_moloni/status
     */
    public function status()
    {
        try {
            $status = [
                'service' => 'Desk-Moloni Client Portal',
                'version' => '3.0.0',
                'status' => 'operational',
                'uptime' => $this->_getUptime(),
                'client_stats' => [
                    'active_sessions' => $this->_getActiveClientSessions(),
                    'documents_served_today' => $this->_getDocumentsServedToday(),
                    'api_requests_last_hour' => $this->_getApiRequestsLastHour()
                ],
                'performance' => [
                    'avg_response_time_ms' => $this->_getAverageResponseTime(),
                    'cache_hit_rate' => $this->_getCacheHitRate()
                ]
            ];

            $this->_respondWithSuccess($status);

        } catch (Exception $e) {
            $this->_respondWithError('Status check failed: ' . $e->getMessage(), 500);
        }
    }

    // Private health check methods

    private function _checkDatabaseHealth()
    {
        try {
            $this->db->query('SELECT 1');
            return ['status' => 'healthy', 'message' => 'Database connection OK'];
        } catch (Exception $e) {
            return ['status' => 'unhealthy', 'message' => 'Database connection failed'];
        }
    }

    private function _checkAuthHealth()
    {
        try {
            if (is_client_logged_in()) {
                return ['status' => 'healthy', 'message' => 'Client authentication OK'];
            } else {
                return ['status' => 'healthy', 'message' => 'No client authenticated (normal for health check)'];
            }
        } catch (Exception $e) {
            return ['status' => 'unhealthy', 'message' => 'Authentication system error'];
        }
    }

    private function _checkPermissionsHealth()
    {
        try {
            // Test document access control initialization
            $accessControl = new DocumentAccessControl();
            return ['status' => 'healthy', 'message' => 'Access control system OK'];
        } catch (Exception $e) {
            return ['status' => 'unhealthy', 'message' => 'Access control system error'];
        }
    }

    private function _checkNotificationsHealth()
    {
        try {
            // Test notification service initialization
            $notificationService = new ClientNotificationService();
            return ['status' => 'healthy', 'message' => 'Notification system OK'];
        } catch (Exception $e) {
            return ['status' => 'unhealthy', 'message' => 'Notification system error'];
        }
    }

    private function _getUptime()
    {
        // This would track actual service uptime
        // For now, return a placeholder
        return '99.9%';
    }

    private function _getActiveClientSessions()
    {
        // Count active client sessions
        return $this->db->where('last_activity >', date('Y-m-d H:i:s', strtotime('-30 minutes')))
                       ->count_all_results('tblclients');
    }

    private function _getDocumentsServedToday()
    {
        // Count document access logs for today
        $today = date('Y-m-d');
        return count($this->desk_moloni_sync_log_model->getClientPortalAccessLogs(0, [
            'start_date' => $today . ' 00:00:00',
            'end_date' => $today . ' 23:59:59'
        ], 10000));
    }

    private function _getApiRequestsLastHour()
    {
        // Count API requests in the last hour
        $lastHour = date('Y-m-d H:i:s', strtotime('-1 hour'));
        return count($this->desk_moloni_sync_log_model->getClientPortalAccessLogs(0, [
            'start_date' => $lastHour
        ], 10000));
    }

    private function _getAverageResponseTime()
    {
        // This would be tracked by application monitoring
        // For now, return a placeholder
        return 150; // ms
    }

    private function _getCacheHitRate()
    {
        // This would be tracked by cache monitoring
        // For now, return a placeholder
        return 85.5; // percentage
    }
}