<?php
/**
 * Invoices Controller
 *
 * @package Freelance_Studio
 */

class FS_Invoices_Controller {

    /**
     * Handle the request
     */
    public function handle_request() {
        $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : 'list';
        $id = isset($_GET['id']) ? intval($_GET['id']) : 0;

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $this->handle_post();
            return;
        }

        switch ($action) {
            case 'add':
                $this->add();
                break;
            case 'edit':
                $this->edit($id);
                break;
            case 'view':
                $this->view($id);
                break;
            case 'pdf':
                $this->generate_pdf($id);
                break;
            case 'duplicate':
                $this->duplicate($id);
                break;
            case 'send':
                $this->send($id);
                break;
            case 'mark_sent':
                $this->mark_sent($id);
                break;
            case 'mark_paid':
                $this->mark_paid($id);
                break;
            case 'record_payment':
                $this->record_payment_page($id);
                break;
            case 'income':
                $this->income_list();
                break;
            case 'add_income':
                $this->add_income();
                break;
            case 'edit_income':
                $this->edit_income($id);
                break;
            case 'duplicate_income':
                $this->duplicate_income($id);
                break;
            default:
                $this->index();
                break;
        }
    }

    /**
     * Handle POST requests
     */
    private function handle_post() {
        // Handle manual income save
        if (isset($_POST['fs_income_nonce']) && wp_verify_nonce($_POST['fs_income_nonce'], 'fs_save_income')) {
            $this->handle_income_post();
            return;
        }
        
        if (!isset($_POST['fs_invoice_nonce']) || !wp_verify_nonce($_POST['fs_invoice_nonce'], 'fs_save_invoice')) {
            wp_die(esc_html__('Security check failed', 'freelance-studio'));
        }

        $action = isset($_POST['action']) ? sanitize_text_field(wp_unslash($_POST['action'])) : '';
        $id = isset($_POST['invoice_id']) ? intval($_POST['invoice_id']) : 0;

        // Parse items from form and collect time entry IDs
        $items = array();
        $time_entry_ids = array();
        
        if (isset($_POST['items']) && is_array($_POST['items'])) {
            foreach ($_POST['items'] as $item) {
                if (!empty($item['description'])) {
                    $items[] = array(
                        'description' => sanitize_text_field($item['description']),
                        'quantity' => floatval($item['quantity']),
                        'rate' => floatval($item['rate']),
                        'amount' => floatval($item['quantity']) * floatval($item['rate'])
                    );
                    
                    // Collect time entry IDs if present
                    if (!empty($item['entry_ids'])) {
                        $ids = array_map('intval', explode(',', $item['entry_ids']));
                        $time_entry_ids = array_merge($time_entry_ids, $ids);
                    }
                }
            }
        }
        
        // Parse expenses with markup
        $expense_ids = array();
        $expense_data = array();
        $expense_markup = isset($_POST['expense_markup']) ? floatval($_POST['expense_markup']) : 0;
        
        if (isset($_POST['expenses']) && is_array($_POST['expenses'])) {
            foreach ($_POST['expenses'] as $expense_id) {
                $expense_id = intval($expense_id);
                if ($expense_id > 0) {
                    $expense_ids[] = $expense_id;
                    
                    // Get expense details
                    $expense = FS_Expense::get($expense_id);
                    if ($expense) {
                        $billed_amount = $expense->amount * (1 + $expense_markup / 100);
                        $expense_data[] = array(
                            'id' => $expense_id,
                            'date' => $expense->expense_date,
                            'description' => $expense->expense_name,
                            'category' => $expense->category,
                            'amount' => $expense->amount,
                            'markup' => $expense_markup,
                            'billed_amount' => round($billed_amount, 2),
                        );
                    }
                }
            }
        }
        
        // Calculate expense total
        $expense_total = 0;
        foreach ($expense_data as $exp) {
            $expense_total += $exp['billed_amount'];
        }
        
        // Parse selected contacts
        $send_to_contacts = array();
        if (isset($_POST['send_to_contacts']) && is_array($_POST['send_to_contacts'])) {
            foreach ($_POST['send_to_contacts'] as $contact_id) {
                if ($contact_id !== 'primary') {
                    $send_to_contacts[] = intval($contact_id);
                }
            }
        }

        // Calculate subtotal (items + expenses)
        $items_subtotal = array_reduce($items, function($carry, $item) {
            return $carry + $item['amount'];
        }, 0);
        
        $subtotal = $items_subtotal + $expense_total;
        
        // Handle dates
        $invoice_date = !empty($_POST['invoice_date']) ? sanitize_text_field(wp_unslash($_POST['invoice_date'])) : null;
        $paid_date = !empty($_POST['paid_date']) ? sanitize_text_field(wp_unslash($_POST['paid_date'])) : null;
        $status = sanitize_text_field(wp_unslash($_POST['status']));

        $data = array(
            'client_id' => intval($_POST['client_id']),
            'project_id' => !empty($_POST['project_id']) ? intval($_POST['project_id']) : null,
            'invoice_number' => sanitize_text_field(wp_unslash($_POST['invoice_number'])),
            'invoice_date' => $invoice_date,
            'due_date' => sanitize_text_field(wp_unslash($_POST['due_date'])),
            'paid_date' => $paid_date,
            'status' => $status,
            'subtotal' => $subtotal,
            'tax_rate' => isset($_POST['tax_rate']) ? floatval($_POST['tax_rate']) : 0,
            'discount_type' => isset($_POST['discount_type']) ? sanitize_text_field(wp_unslash($_POST['discount_type'])) : null,
            'discount_value' => isset($_POST['discount_value']) ? floatval($_POST['discount_value']) : 0,
            'amount_paid' => isset($_POST['amount_paid']) ? floatval($_POST['amount_paid']) : 0,
            'notes' => sanitize_textarea_field(wp_unslash($_POST['notes'])),
            'payment_link' => isset($_POST['payment_link']) ? esc_url_raw(wp_unslash($_POST['payment_link'])) : null,
            'items' => $items,
            'expenses' => $expense_data,
            'expense_markup' => $expense_markup,
            'send_to_contacts' => !empty($send_to_contacts) ? json_encode($send_to_contacts) : null,
        );

        if ($action === 'update' && $id) {
            // Get old expenses to unmark them
            $old_invoice = FS_Invoice::get($id);
            if ($old_invoice && !empty($old_invoice->expenses)) {
                $old_expenses = is_string($old_invoice->expenses) ? json_decode($old_invoice->expenses, true) : $old_invoice->expenses;
                if (is_array($old_expenses)) {
                    $old_expense_ids = array_column($old_expenses, 'id');
                    // Unmark expenses that are no longer included
                    $removed_expenses = array_diff($old_expense_ids, $expense_ids);
                    if (!empty($removed_expenses)) {
                        FS_Expense::unmark_invoiced($removed_expenses);
                    }
                }
            }
            
            $result = FS_Invoice::update($id, $data);
            
            // Mark new expenses as invoiced
            if (!empty($expense_ids)) {
                FS_Expense::mark_invoiced($expense_ids, $id);
            }
            
            $message = $result !== false ? 'updated' : 'error';
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=' . $message));
        } else {
            $invoice_id = FS_Invoice::create($data);
            if ($invoice_id) {
                // Mark time entries as invoiced
                if (!empty($time_entry_ids)) {
                    FS_Time_Entry::mark_invoiced($time_entry_ids, $invoice_id);
                }
                
                // Mark expenses as invoiced
                if (!empty($expense_ids)) {
                    FS_Expense::mark_invoiced($expense_ids, $invoice_id);
                }
                
                // Redirect with prompt_send flag to trigger the send modal
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $invoice_id . '&message=created&prompt_send=1'));
            } else {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=add&message=error'));
            }
        }
        exit;
    }

    /**
     * List all invoices
     */
    public function index() {
        // Auto-update overdue statuses before displaying
        FS_Invoice::update_overdue_statuses();

        $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : null;
        $client_filter = isset($_GET['client_id']) ? intval($_GET['client_id']) : null;
        
        // Pagination
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 25;
        $per_page = in_array($per_page, array(10, 25, 50, 100)) ? $per_page : 25;
        $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
        $offset = ($current_page - 1) * $per_page;
        
        $count_args = array();
        $args = array(
            'limit' => $per_page,
            'offset' => $offset,
        );
        if ($status_filter) {
            $args['status'] = $status_filter;
            $count_args['status'] = $status_filter;
        }
        if ($client_filter) {
            $args['client_id'] = $client_filter;
            $count_args['client_id'] = $client_filter;
        }
        
        $invoices = FS_Invoice::get_all($args);
        $total_invoices = FS_Invoice::count($count_args);
        $total_pages = ceil($total_invoices / $per_page);
        
        $clients = FS_Client::get_for_dropdown();
        $statuses = FS_Invoice::get_statuses();
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        $messages = array(
            'created' => __('Invoice created successfully.', 'freelance-studio'),
            'updated' => __('Invoice updated successfully.', 'freelance-studio'),
            'deleted' => __('Invoice deleted successfully.', 'freelance-studio'),
            'sent' => __('Invoice sent successfully.', 'freelance-studio'),
            'paid' => __('Invoice marked as paid.', 'freelance-studio'),
            'error' => __('An error occurred. Please try again.', 'freelance-studio'),
        );
        
        // Get summary stats
        $total_outstanding = FS_Invoice::get_outstanding_total();
        $overdue_count = count(FS_Invoice::get_overdue());
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/list.php';
    }

    /**
     * Add new invoice form
     */
    public function add() {
        $invoice = null;
        $clients = FS_Client::get_for_dropdown();
        $projects = FS_Project::get_for_dropdown();
        $statuses = FS_Invoice::get_statuses();
        $next_invoice_number = FS_Invoice::generate_invoice_number();
        $default_payment_terms = get_option('fs_default_payment_terms', 30);
        $is_new = true;
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        // Pre-select client/project if passed in URL
        $selected_client_id = isset($_GET['client_id']) ? intval($_GET['client_id']) : 0;
        $selected_project_id = isset($_GET['project_id']) ? intval($_GET['project_id']) : 0;
        
        // Use client-specific payment terms if a client is pre-selected
        if ($selected_client_id) {
            $selected_client = FS_Client::get($selected_client_id);
            if ($selected_client && isset($selected_client->default_payment_terms) && $selected_client->default_payment_terms > 0) {
                $default_payment_terms = intval($selected_client->default_payment_terms);
            }
        }
        
        // Check if we're creating invoice from time entries
        $from_time = isset($_GET['from_time']) && $_GET['from_time'] == '1';
        $unbilled_time_entries = array();
        $time_items = array();
        
        // If a project is selected, auto-populate services as line items
        if ($selected_project_id && !$from_time) {
            $project = FS_Project::get($selected_project_id);
            if ($project) {
                $services = $project->services;
                if (is_string($services)) {
                    $services = json_decode($services, true);
                }
                
                if (!empty($services) && is_array($services)) {
                    foreach ($services as $svc) {
                        $name = isset($svc['name']) ? $svc['name'] : __('Service', 'freelance-studio');
                        $pricing_type = isset($svc['pricing_type']) ? $svc['pricing_type'] : 'flat';
                        $rate = isset($svc['rate']) ? floatval($svc['rate']) : 0;
                        $quantity = isset($svc['quantity']) ? floatval($svc['quantity']) : 1;
                        
                        // For hourly services, use estimated quantity
                        if ($pricing_type === 'hourly') {
                            $amount = $rate * $quantity;
                        } else {
                            // Flat fee
                            $amount = isset($svc['amount']) ? floatval($svc['amount']) : ($rate * $quantity);
                        }
                        
                        if ($amount > 0 || $rate > 0) {
                            $time_items[] = array(
                                'description' => $name,
                                'quantity' => $quantity,
                                'rate' => $rate,
                                'amount' => $amount,
                                'entry_ids' => array(),
                            );
                        }
                    }
                }
            }
        }
        
        if ($from_time && $selected_project_id) {
            // Get unbilled time entries for this project
            $unbilled_time_entries = FS_Time_Entry::get_unbilled($selected_project_id);
            
            // Group by activity type or create line items
            $grouped = array();
            foreach ($unbilled_time_entries as $entry) {
                $activity = $entry->activity ?: 'other';
                if (!isset($grouped[$activity])) {
                    $grouped[$activity] = array(
                        'description' => '',
                        'hours' => 0,
                        'rate' => $entry->hourly_rate,
                        'entry_ids' => array(),
                    );
                }
                $grouped[$activity]['hours'] += FS_Time_Entry::get_rounded_hours($entry->duration);
                $grouped[$activity]['entry_ids'][] = $entry->id;
            }
            
            $activity_types = FS_Time_Entry::get_activity_types();
            foreach ($grouped as $activity => $data) {
                $time_items[] = array(
                    'description' => $activity_types[$activity] ?? ucfirst($activity),
                    'quantity' => round($data['hours'], 2),
                    'rate' => $data['rate'],
                    'amount' => round($data['hours'] * $data['rate'], 2),
                    'entry_ids' => $data['entry_ids'],
                );
            }
        }
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/form.php';
    }

    /**
     * Edit invoice form
     */
    public function edit($id) {
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=not_found'));
            exit;
        }
        
        $clients = FS_Client::get_for_dropdown();
        $projects = FS_Project::get_for_dropdown($invoice->client_id);
        $statuses = FS_Invoice::get_statuses();
        $is_new = false;
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/form.php';
    }

    /**
     * View invoice (preview)
     */
    public function view($id) {
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=not_found'));
            exit;
        }
        
        $client = FS_Client::get($invoice->client_id);
        $project = $invoice->project_id ? FS_Project::get($invoice->project_id) : null;
        $settings = $this->get_business_settings();
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/view.php';
    }

    /**
     * Generate PDF
     */
    public function generate_pdf($id) {
        // PDF downloads are a Pro feature
        if ( function_exists( 'fs_studio_is_free_plan' ) && fs_studio_is_free_plan() ) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=pro_required'));
            exit;
        }
        
        // Clear any output buffers to prevent WordPress admin from interfering
        while (ob_get_level()) {
            ob_end_clean();
        }
        
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            wp_die(esc_html__('Invoice not found', 'freelance-studio'));
        }
        
        $client = FS_Client::get($invoice->client_id);
        
        FS_PDF_Service::generate_invoice($invoice, $client);
    }

    /**
     * Duplicate an invoice
     */
    public function duplicate($id) {
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=not_found'));
            exit;
        }
        
        // Use client-specific payment terms for the new due date
        $dup_payment_terms = get_option('fs_default_payment_terms', 30);
        if ($invoice->client_id) {
            $dup_client = FS_Client::get($invoice->client_id);
            if ($dup_client && isset($dup_client->default_payment_terms) && $dup_client->default_payment_terms > 0) {
                $dup_payment_terms = intval($dup_client->default_payment_terms);
            }
        }
        
        $data = array(
            'client_id' => $invoice->client_id,
            'project_id' => $invoice->project_id,
            'invoice_number' => FS_Invoice::generate_invoice_number(),
            'invoice_date' => current_time('Y-m-d'),
            'due_date' => gmdate('Y-m-d', strtotime('+' . $dup_payment_terms . ' days')),
            'status' => 'draft',
            'subtotal' => $invoice->subtotal,
            'tax_rate' => $invoice->tax_rate,
            'discount_type' => $invoice->discount_type,
            'discount_value' => $invoice->discount_value,
            'notes' => $invoice->notes,
            'items' => $invoice->items,
        );
        
        $new_id = FS_Invoice::create($data);
        
        if ($new_id) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=edit&id=' . $new_id . '&message=duplicated'));
        } else {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=error'));
        }
        exit;
    }

    /**
     * Send invoice to client
     */
    public function send($id) {
        // Check free plan invoice send limit
        if ( function_exists( 'fs_studio_limit_reached' ) && fs_studio_limit_reached( 'invoice_sends' ) ) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=limit_reached'));
            exit;
        }
        
        $invoice = FS_Invoice::get($id);
        
        if (!$invoice) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=error'));
            exit;
        }
        
        // Get contact IDs if specified
        $contact_ids = array();
        if (!empty($invoice->send_to_contacts)) {
            $contact_ids = json_decode($invoice->send_to_contacts, true) ?: array();
        }
        
        // Send the invoice
        $sent = FS_Email_Service::send_invoice($id, $contact_ids);
        
        // Increment the monthly send counter for free plan users
        if ( function_exists( 'fs_studio_increment_invoice_sends' ) ) {
            fs_studio_increment_invoice_sends();
        }
        
        if ($sent) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=sent'));
        } else {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=sent_no_email'));
        }
        exit;
    }

    /**
     * Mark invoice as sent
     */
    public function mark_sent($id) {
        FS_Invoice::mark_sent($id);
        FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=sent'));
        exit;
    }

    /**
     * Mark invoice as paid
     */
    public function mark_paid($id) {
        $invoice = FS_Invoice::get($id);
        if ($invoice) {
            FS_Invoice::record_payment($id, $invoice->balance_due);
        }
        FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=paid&prompt_receipt=1'));
        exit;
    }

    /**
     * Record payment - redirect to edit page with payment focus
     */
    public function record_payment_page($id) {
        $invoice = FS_Invoice::get($id);
        if (!$invoice) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&message=not_found'));
            exit;
        }
        
        // Record full payment immediately
        FS_Invoice::record_payment($id, $invoice->balance_due);
        FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=invoices&action=view&id=' . $id . '&message=paid&prompt_receipt=1'));
        exit;
    }

    /**
     * Get business settings for invoice display
     */
    private function get_business_settings() {
        return array(
            'business_name' => get_option('fs_business_name', ''),
            'business_email' => get_option('fs_business_email', ''),
            'business_phone' => get_option('fs_business_phone', ''),
            'business_website' => get_option('fs_business_website', ''),
            'business_address' => get_option('fs_business_address', ''),
            'business_city' => get_option('fs_business_city', ''),
            'business_state' => get_option('fs_business_state', ''),
            'business_zip' => get_option('fs_business_zip', ''),
            'business_country' => get_option('fs_business_country', ''),
            'business_logo' => get_option('fs_business_logo', ''),
            'business_logo_light' => get_option('fs_business_logo_light', ''),
            'invoice_primary_color' => get_option('fs_invoice_primary_color', '#2271b1'),
            'invoice_primary_color_mode' => get_option('fs_invoice_primary_color_mode', 'solid'),
            'invoice_primary_color_2' => get_option('fs_invoice_primary_color_2', '#1a5276'),
            'invoice_gradient_direction' => get_option('fs_invoice_gradient_direction', '135'),
            'payment_instructions' => is_array($pi = get_option('fs_payment_instructions', '')) ? implode("\n", array_filter($pi)) : (string) $pi,
        );
    }

    /**
     * Handle manual income POST requests
     */
    private function handle_income_post() {
        $action = isset($_POST['action']) ? sanitize_text_field(wp_unslash($_POST['action'])) : '';
        $id = isset($_POST['income_id']) ? intval($_POST['income_id']) : 0;

        $data = array(
            'source' => sanitize_text_field(wp_unslash($_POST['source'])),
            'description' => sanitize_textarea_field(wp_unslash($_POST['description'] ?? '')),
            'amount' => floatval($_POST['amount']),
            'income_date' => sanitize_text_field(wp_unslash($_POST['income_date'])),
            'category' => sanitize_text_field(wp_unslash($_POST['category'])),
            'reference' => sanitize_text_field(wp_unslash($_POST['reference'] ?? '')),
            'notes' => sanitize_textarea_field(wp_unslash($_POST['notes'] ?? '')),
        );

        if ($action === 'update' && $id) {
            $result = FS_Manual_Income::update($id, $data);
            $message = $result !== false ? 'updated' : 'error';
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&action=edit&id=' . $id . '&message=' . $message));
        } else {
            $income_id = FS_Manual_Income::create($data);
            if ($income_id) {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&action=edit&id=' . $income_id . '&message=created'));
            } else {
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&action=add&message=error'));
            }
        }
        exit;
    }

    /**
     * Manual income list
     */
    public function income_list() {
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        // Handle delete action
        if (isset($_GET['delete']) && isset($_GET['_wpnonce'])) {
            if (wp_verify_nonce($_GET['_wpnonce'], 'delete_income_' . intval($_GET['delete']))) {
                FS_Manual_Income::delete(intval($_GET['delete']));
                FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&message=deleted'));
                exit;
            }
        }
        
        // Filters
        $filters = array(
            'category' => isset($_GET['category']) ? sanitize_text_field($_GET['category']) : '',
            'start_date' => isset($_GET['start_date']) ? sanitize_text_field($_GET['start_date']) : '',
            'end_date' => isset($_GET['end_date']) ? sanitize_text_field($_GET['end_date']) : '',
        );
        
        $args = array_filter($filters);
        $income_entries = FS_Manual_Income::get_all($args);
        $categories = FS_Manual_Income::get_categories();
        
        // Calculate total
        $total = 0;
        foreach ($income_entries as $entry) {
            $total += floatval($entry->amount);
        }
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/income-list.php';
    }

    /**
     * Add new manual income form
     */
    public function add_income() {
        $income = null;
        $categories = FS_Manual_Income::get_categories();
        $is_new = true;
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/income-form.php';
    }

    /**
     * Edit manual income form
     */
    public function edit_income($id) {
        $income = FS_Manual_Income::get($id);
        
        if (!$income) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&message=not_found'));
            exit;
        }
        
        $categories = FS_Manual_Income::get_categories();
        $is_new = false;
        $message = isset($_GET['message']) ? sanitize_text_field($_GET['message']) : '';
        
        include JEEB_FS_PLUGIN_DIR . 'includes/views/invoices/income-form.php';
    }

    /**
     * Duplicate an other income entry and redirect to its edit page
     */
    public function duplicate_income($id) {
        $income = FS_Manual_Income::get($id);

        if (!$income) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&message=not_found'));
            exit;
        }

        $new_id = FS_Manual_Income::create(array(
            'source'      => $income->source . ' ' . __('(copy)', 'freelance-studio'),
            'description' => $income->description,
            'amount'      => $income->amount,
            'income_date' => current_time('Y-m-d'),
            'category'    => $income->category,
            'reference'   => $income->reference,
            'notes'       => $income->notes,
        ));

        if ($new_id) {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&action=edit&id=' . $new_id . '&message=duplicated'));
        } else {
            FS_Admin::safe_redirect(admin_url('admin.php?page=fs-finances&tab=income&message=error'));
        }
        exit;
    }
}
