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

<?php

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

/**
 * OAuth Controller for Moloni Integration
 * 
 * Handles OAuth 2.0 authentication flow for Perfex CRM integration
 * 
 * @package     DeskMoloni
 * @author      Descomplicar®
 * @copyright   2025 Descomplicar
 * @version     3.0.0
 */
class OAuthController extends AdminController
{
    public function __construct()
    {
        parent::__construct();
        
        // Check if user has permission to access Moloni settings
        if (!has_permission('desk_moloni', '', 'view')) {
            access_denied('Desk-Moloni');
        }
        
        // Load required libraries
        $this->load->library('desk_moloni/moloni_oauth');
        $this->load->library('desk_moloni/token_manager');
        $this->load->helper('url');
        
        // Set page title
        $this->app_menu->add_breadcrumb(_l('desk_moloni'), admin_url('desk_moloni'));
        $this->app_menu->add_breadcrumb(_l('oauth_settings'), '');
    }
    
    /**
     * OAuth settings and initiation page
     */
    public function index()
    {
        // Handle form submission
        if ($this->input->post()) {
            $this->handle_oauth_configuration();
        }
        
        $data = [];
        
        // Get current OAuth status
        $data['oauth_status'] = $this->moloni_oauth->get_status();
        $data['token_status'] = $this->token_manager->get_token_status();
        
        // Get configuration test results
        $data['config_test'] = $this->moloni_oauth->test_configuration();
        
        // Get current settings
        $data['client_id'] = get_option('desk_moloni_client_id');
        $data['client_secret'] = get_option('desk_moloni_client_secret');
        $data['use_pkce'] = (bool)get_option('desk_moloni_use_pkce', true);
        
        // Generate CSRF token for forms
        $data['csrf_token'] = $this->security->get_csrf_hash();
        
        // Load view
        $data['title'] = _l('desk_moloni_oauth_settings');
        $this->load->view('admin/includes/header', $data);
        $this->load->view('admin/oauth_setup', $data);
        $this->load->view('admin/includes/footer');
    }
    
    /**
     * Initiate OAuth authorization flow
     */
    public function authorize()
    {
        try {
            // Verify CSRF token
            if (!$this->security->get_csrf_hash()) {
                throw new Exception('Invalid CSRF token');
            }
            
            // Check if OAuth is configured
            if (!$this->moloni_oauth->is_configured()) {
                set_alert('danger', _l('oauth_not_configured'));
                redirect(admin_url('desk_moloni/oauth'));
            }
            
            // Generate state for CSRF protection
            $state = bin2hex(random_bytes(16));
            $this->session->set_userdata('oauth_state', $state);
            
            // Get authorization URL
            $auth_url = $this->moloni_oauth->get_authorization_url($state);
            
            // Log authorization initiation
            log_activity('Desk-Moloni: OAuth authorization initiated by ' . get_staff_full_name());
            
            // Redirect to Moloni OAuth page
            redirect($auth_url);
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: OAuth authorization failed - ' . $e->getMessage());
            set_alert('danger', _l('oauth_authorization_failed') . ': ' . $e->getMessage());
            redirect(admin_url('desk_moloni/oauth'));
        }
    }
    
    /**
     * Handle OAuth callback from Moloni
     */
    public function callback()
    {
        try {
            // Get callback parameters
            $code = $this->input->get('code');
            $state = $this->input->get('state');
            $error = $this->input->get('error');
            $error_description = $this->input->get('error_description');
            
            // Handle OAuth errors
            if ($error) {
                throw new Exception("OAuth Error: {$error} - {$error_description}");
            }
            
            // Validate required parameters
            if (empty($code)) {
                throw new Exception('Authorization code not received');
            }
            
            // Verify state parameter (CSRF protection)
            $stored_state = $this->session->userdata('oauth_state');
            if (empty($stored_state) || $state !== $stored_state) {
                throw new Exception('Invalid state parameter - possible CSRF attack');
            }
            
            // Clear stored state
            $this->session->unset_userdata('oauth_state');
            
            // Exchange code for tokens
            $success = $this->moloni_oauth->handle_callback($code, $state);
            
            if ($success) {
                // Log successful authentication
                log_activity('Desk-Moloni: OAuth authentication successful for ' . get_staff_full_name());
                
                // Set success message
                set_alert('success', _l('oauth_connected_successfully'));
                
                // Test API connection
                $this->test_api_connection();
                
            } else {
                throw new Exception('Token exchange failed');
            }
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: OAuth callback failed - ' . $e->getMessage());
            set_alert('danger', _l('oauth_callback_failed') . ': ' . $e->getMessage());
        }
        
        // Redirect back to OAuth settings
        redirect(admin_url('desk_moloni/oauth'));
    }
    
    /**
     * Disconnect OAuth (revoke tokens)
     */
    public function disconnect()
    {
        try {
            // Verify CSRF token
            if (!$this->input->post() || !$this->security->get_csrf_hash()) {
                throw new Exception('Invalid request');
            }
            
            // Revoke OAuth access
            $success = $this->moloni_oauth->revoke_access();
            
            if ($success) {
                log_activity('Desk-Moloni: OAuth disconnected by ' . get_staff_full_name());
                set_alert('success', _l('oauth_disconnected_successfully'));
            } else {
                set_alert('warning', _l('oauth_disconnect_partial'));
            }
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: OAuth disconnect failed - ' . $e->getMessage());
            set_alert('danger', _l('oauth_disconnect_failed') . ': ' . $e->getMessage());
        }
        
        redirect(admin_url('desk_moloni/oauth'));
    }
    
    /**
     * Refresh OAuth tokens manually
     */
    public function refresh_token()
    {
        try {
            // Verify CSRF token
            if (!$this->input->post() || !$this->security->get_csrf_hash()) {
                throw new Exception('Invalid request');
            }
            
            // Attempt token refresh
            $success = $this->moloni_oauth->refresh_access_token();
            
            if ($success) {
                log_activity('Desk-Moloni: OAuth tokens refreshed by ' . get_staff_full_name());
                set_alert('success', _l('oauth_tokens_refreshed'));
            } else {
                set_alert('danger', _l('oauth_token_refresh_failed'));
            }
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: Token refresh failed - ' . $e->getMessage());
            set_alert('danger', _l('oauth_token_refresh_failed') . ': ' . $e->getMessage());
        }
        
        redirect(admin_url('desk_moloni/oauth'));
    }
    
    /**
     * Test OAuth configuration
     */
    public function test_config()
    {
        try {
            // Run configuration test
            $test_results = $this->moloni_oauth->test_configuration();
            
            if ($test_results['is_valid']) {
                set_alert('success', _l('oauth_config_test_passed'));
            } else {
                $issues = implode('<br>', $test_results['issues']);
                set_alert('danger', _l('oauth_config_test_failed') . ':<br>' . $issues);
            }
            
        } catch (Exception $e) {
            set_alert('danger', _l('oauth_config_test_error') . ': ' . $e->getMessage());
        }
        
        redirect(admin_url('desk_moloni/oauth'));
    }
    
    /**
     * Get OAuth status via AJAX
     */
    public function get_status()
    {
        // Verify AJAX request
        if (!$this->input->is_ajax_request()) {
            show_404();
        }
        
        try {
            $status = [
                'oauth' => $this->moloni_oauth->get_status(),
                'tokens' => $this->token_manager->get_token_status(),
                'timestamp' => date('Y-m-d H:i:s')
            ];
            
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($status));
                
        } catch (Exception $e) {
            $this->output
                ->set_status_header(500)
                ->set_content_type('application/json')
                ->set_output(json_encode(['error' => $e->getMessage()]));
        }
    }
    
    /**
     * Export OAuth logs
     */
    public function export_logs()
    {
        try {
            // Check permissions
            if (!has_permission('desk_moloni', '', 'view')) {
                throw new Exception('Insufficient permissions');
            }
            
            // Load API log model
            $this->load->model('desk_moloni_api_log_model');
            
            // Get logs from last 30 days
            $logs = $this->desk_moloni_api_log_model->get_logs([
                'start_date' => date('Y-m-d', strtotime('-30 days')),
                'end_date' => date('Y-m-d'),
                'limit' => 1000
            ]);
            
            // Generate CSV
            $csv_data = $this->generate_logs_csv($logs);
            
            // Set headers for file download
            $filename = 'moloni_oauth_logs_' . date('Y-m-d') . '.csv';
            
            $this->output
                ->set_content_type('application/csv')
                ->set_header('Content-Disposition: attachment; filename="' . $filename . '"')
                ->set_output($csv_data);
                
        } catch (Exception $e) {
            set_alert('danger', _l('export_logs_failed') . ': ' . $e->getMessage());
            redirect(admin_url('desk_moloni/oauth'));
        }
    }
    
    /**
     * Handle OAuth configuration form submission
     */
    private function handle_oauth_configuration()
    {
        try {
            // Validate CSRF token
            if (!$this->security->get_csrf_hash()) {
                throw new Exception('Invalid CSRF token');
            }
            
            // Get form data
            $client_id = $this->input->post('client_id', true);
            $client_secret = $this->input->post('client_secret', true);
            $use_pkce = (bool)$this->input->post('use_pkce');
            
            // Validate required fields
            if (empty($client_id) || empty($client_secret)) {
                throw new Exception('Client ID and Client Secret are required');
            }
            
            // Configure OAuth
            $options = [
                'use_pkce' => $use_pkce,
                'timeout' => (int)$this->input->post('timeout', true) ?: 30
            ];
            
            $success = $this->moloni_oauth->configure($client_id, $client_secret, $options);
            
            if ($success) {
                log_activity('Desk-Moloni: OAuth configuration updated by ' . get_staff_full_name());
                set_alert('success', _l('oauth_configuration_saved'));
            } else {
                throw new Exception('Configuration save failed');
            }
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: OAuth configuration failed - ' . $e->getMessage());
            set_alert('danger', _l('oauth_configuration_failed') . ': ' . $e->getMessage());
        }
    }
    
    /**
     * Test API connection after OAuth setup
     */
    private function test_api_connection()
    {
        try {
            $this->load->library('desk_moloni/moloni_api_client');
            
            // Try to get companies list (basic API test)
            $companies = $this->moloni_api_client->make_request('companies/getAll');
            
            if (!empty($companies)) {
                log_activity('Desk-Moloni: API connection test successful');
                set_alert('info', _l('api_connection_test_passed'));
            }
            
        } catch (Exception $e) {
            log_activity('Desk-Moloni: API connection test failed - ' . $e->getMessage());
            set_alert('warning', _l('api_connection_test_failed') . ': ' . $e->getMessage());
        }
    }
    
    /**
     * Generate CSV data from logs
     * 
     * @param array $logs Log entries
     * @return string CSV data
     */
    private function generate_logs_csv($logs)
    {
        $csv_lines = [];
        
        // Add header
        $csv_lines[] = 'Timestamp,Endpoint,Method,Status,Error,User';
        
        // Add log entries
        foreach ($logs as $log) {
            $csv_lines[] = sprintf(
                '"%s","%s","%s","%s","%s","%s"',
                $log['timestamp'],
                $log['endpoint'] ?? '',
                $log['method'] ?? 'POST',
                $log['error'] ? 'ERROR' : 'SUCCESS',
                str_replace('"', '""', $log['error'] ?? ''),
                $log['user_name'] ?? 'System'
            );
        }
        
        return implode("\n", $csv_lines);
    }
    
    /**
     * Security check for sensitive operations
     * 
     * @param string $action Action being performed
     * @return bool Security check passed
     */
    private function security_check($action)
    {
        // Rate limiting for sensitive operations
        $rate_limit_key = 'oauth_action_' . get_staff_user_id() . '_' . $action;
        $attempts = $this->session->userdata($rate_limit_key) ?? 0;
        
        if ($attempts >= 5) {
            throw new Exception('Too many attempts. Please wait before trying again.');
        }
        
        $this->session->set_userdata($rate_limit_key, $attempts + 1);
        
        return true;
    }
}