<?php
/**
 * Invoice Service Class
 *
 * @package     EasyInvoice
 * @author      Your Name
 * @copyright   Copyright (c) 2023, Your Company
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       1.0.0
 */

namespace EasyInvoice\Services;

use EasyInvoice\Models\Invoice;
use EasyInvoice\Repositories\InvoiceRepository;
use EasyInvoice\Interfaces\InvoiceRepositoryInterface;

/**
 * Invoice Service Class
 *
 * Handles business logic for invoices and provides extension points for plugins.
 *
 * @since 1.0.0
 */
class InvoiceService extends BaseService {
    
    /**
     * Invoice repository
     *
     * @var InvoiceRepositoryInterface
     */
    private $repository;
    
    /**
     * Constructor
     *
     * @since 1.0.0
     * @param InvoiceRepositoryInterface $repository The invoice repository
     */
    public function __construct(InvoiceRepositoryInterface $repository) {
        parent::__construct('InvoiceService');
        $this->repository = $repository;
    }
    
    /**
     * Create a new invoice
     *
     * @since 1.0.0
     * @param array $data The invoice data
     * @return Invoice|false The created invoice or false on failure
     */
    public function createInvoice(array $data) {
        // Allow plugins to modify data before creation
        $data = apply_filters('easy_invoice_service_invoice_create_data', $data);
        
        // Validate required fields
        $required_fields = ['title'];
        $errors = $this->validateRequiredFields($data, $required_fields);
        
        if (!empty($errors)) {
            $this->log('Invoice creation failed: ' . implode(', ', $errors), 'error');
            return false;
        }
        
        // Sanitize data
        $sanitized_data = $this->sanitizeInvoiceData($data);
        
        // Allow plugins to perform actions before creation
        do_action('easy_invoice_service_invoice_before_create', $sanitized_data);
        
        // Create the invoice
        $invoice = $this->repository->create($sanitized_data);
        
        if ($invoice) {
            $this->log('Invoice created successfully: ' . $invoice->getId());
            
            // Allow plugins to perform actions after creation
            do_action('easy_invoice_service_invoice_created', $invoice, $sanitized_data);
            
            return $invoice;
        }
        
        $this->log('Invoice creation failed', 'error');
        return false;
    }
    
    /**
     * Update an existing invoice
     *
     * @since 1.0.0
     * @param int $id The invoice ID
     * @param array $data The invoice data
     * @return Invoice|null The updated invoice or null on failure
     */
    public function updateInvoice(int $id, array $data) {
        // Allow plugins to modify data before update
        $data = apply_filters('easy_invoice_service_invoice_update_data', $data, $id);
        
        // Get the existing invoice
        $invoice = $this->repository->find($id);
        if (!$invoice) {
            $this->log('Invoice not found for update: ' . $id, 'error');
            return null;
        }
        
        // Sanitize data
        $sanitized_data = $this->sanitizeInvoiceData($data);
        
        // Allow plugins to perform actions before update
        do_action('easy_invoice_service_invoice_before_update', $invoice, $sanitized_data);
        
        // Update the invoice
        $updated_invoice = $this->repository->update($id, $sanitized_data);
        
        if ($updated_invoice) {
            $this->log('Invoice updated successfully: ' . $id);
            
            // Allow plugins to perform actions after update
            do_action('easy_invoice_service_invoice_updated', $updated_invoice, $sanitized_data);
            
            return $updated_invoice;
        }
        
        $this->log('Invoice update failed: ' . $id, 'error');
        return null;
    }
    
    /**
     * Delete an invoice
     *
     * @since 1.0.0
     * @param int $id The invoice ID
     * @return bool True if deleted successfully
     */
    public function deleteInvoice(int $id): bool {
        // Get the invoice before deletion
        $invoice = $this->repository->find($id);
        if (!$invoice) {
            $this->log('Invoice not found for deletion: ' . $id, 'error');
            return false;
        }
        
        // Allow plugins to perform actions before deletion
        do_action('easy_invoice_service_invoice_before_delete', $invoice);
        
        // Delete the invoice
        $deleted = $this->repository->delete($id);
        
        if ($deleted) {
            $this->log('Invoice deleted successfully: ' . $id);
            
            // Allow plugins to perform actions after deletion
            do_action('easy_invoice_service_invoice_deleted', $id);
            
            return true;
        }
        
        $this->log('Invoice deletion failed: ' . $id, 'error');
        return false;
    }
    
    /**
     * Get invoice by ID
     *
     * @since 1.0.0
     * @param int $id The invoice ID
     * @return Invoice|null The invoice or null if not found
     */
    public function getInvoice(int $id) {
        $invoice = $this->repository->find($id);
        
        // Allow plugins to modify the found invoice
        return apply_filters('easy_invoice_service_invoice_found', $invoice, $id);
    }
    
    /**
     * Get all invoices
     *
     * @since 1.0.0
     * @param array $args Optional arguments to filter the results
     * @return array Array of Invoice models
     */
    public function getAllInvoices(array $args = []): array {
        $invoices = $this->repository->all($args);
        
        // Allow plugins to modify the invoices list
        return apply_filters('easy_invoice_service_invoices_found', $invoices, $args);
    }
    
    /**
     * Get invoices by customer
     *
     * @since 1.0.0
     * @param int $customer_id The customer ID
     * @return array Array of Invoice models
     */
    public function getInvoicesByCustomer(int $customer_id): array {
        $invoices = $this->repository->findByCustomer($customer_id);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_service_invoices_by_customer', $invoices, $customer_id);
    }
    
    /**
     * Get invoices by status
     *
     * @since 1.0.0
     * @param string $status The invoice status
     * @return array Array of Invoice models
     */
    public function getInvoicesByStatus(string $status): array {
        $invoices = $this->repository->findByStatus($status);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_service_invoices_by_status', $invoices, $status);
    }
    
    /**
     * Get invoices due within a date range
     *
     * @since 1.0.0
     * @param string $start_date The start date in 'Y-m-d' format
     * @param string $end_date The end date in 'Y-m-d' format
     * @return array Array of Invoice models
     */
    public function getInvoicesByDueDate(string $start_date, ?string $end_date = null): array {
        $invoices = $this->repository->findByDueDate($start_date, $end_date);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_service_invoices_by_due_date', $invoices, $start_date, $end_date);
    }
    
    /**
     * Count invoices
     *
     * @since 1.0.0
     * @param array $args Optional arguments to filter the results
     * @return int Number of invoices
     */
    public function countInvoices(array $args = []): int {
        $count = $this->repository->count($args);
        
        // Allow plugins to modify the count
        return apply_filters('easy_invoice_service_invoice_count', $count, $args);
    }
    
    /**
     * Calculate invoice total
     *
     * @since 1.0.0
     * @param Invoice $invoice The invoice
     * @return float The calculated total
     */
    public function calculateInvoiceTotal(Invoice $invoice): float {
        $items = $invoice->getItems();
        $subtotal = 0;
        
        // Calculate subtotal from items
        foreach ($items as $item) {
            $quantity = floatval($item['quantity'] ?? 0);
            $price = floatval($item['price'] ?? 0);
            $subtotal += $quantity * $price;
        }
        
        // Apply discount
        $discount_type = $invoice->getDiscountType();
        $discount_value = floatval($invoice->getDiscountValue() ?? 0);
        
        if ($discount_type === 'percentage' && $discount_value > 0) {
            $discount_amount = $subtotal * ($discount_value / 100);
            $subtotal -= $discount_amount;
        } elseif ($discount_type === 'fixed' && $discount_value > 0) {
            $subtotal -= $discount_value;
        }
        
        // Apply tax
        $tax_rate = floatval($invoice->getTaxRate() ?? 0);
        $prices_include_tax = $invoice->getPricesIncludeTax();
        
        if ($tax_rate > 0) {
            if ($prices_include_tax) {
                // Tax is already included in prices
                $total = $subtotal;
            } else {
                // Add tax to subtotal
                $tax_amount = $subtotal * ($tax_rate / 100);
                $total = $subtotal + $tax_amount;
            }
        } else {
            $total = $subtotal;
        }
        
        // Allow plugins to modify the calculated total
        return apply_filters('easy_invoice_service_invoice_total', $total, $invoice, $subtotal);
    }
    
    /**
     * Send invoice to customer
     *
     * @since 1.0.0
     * @param Invoice $invoice The invoice
     * @param string $email The customer email
     * @return bool True if email was sent successfully
     */
    public function sendInvoiceToCustomer(Invoice $invoice, string $email): bool {
        // Allow plugins to modify email data
        $email_data = apply_filters('easy_invoice_service_invoice_email_data', [
            'to' => $email,
            'subject' => sprintf(__('Invoice #%s from %s', 'easy-invoice'), $invoice->getNumber(), get_bloginfo('name')),
            'message' => $this->generateInvoiceEmailMessage($invoice),
            'headers' => ['Content-Type: text/html; charset=UTF-8']
        ], $invoice);
        
        // Allow plugins to handle email sending
        $sent = apply_filters('easy_invoice_service_invoice_email_send', null, $email_data, $invoice);
        
        if ($sent === null) {
            $sent = $this->sendEmail(
                $email_data['to'],
                $email_data['subject'],
                $email_data['message'],
                $email_data['headers']
            );
        }
        
        if ($sent) {
            // Allow plugins to perform actions after email sent
            do_action('easy_invoice_service_invoice_email_sent', $invoice, $email, $sent);
        }
        
        return $sent;
    }
    
    /**
     * Generate invoice email message
     *
     * @since 1.0.0
     * @param Invoice $invoice The invoice
     * @return string The email message
     */
    protected function generateInvoiceEmailMessage(Invoice $invoice): string {
        $message = sprintf(
            '<p>%s</p>',
            __('Please find attached your invoice.', 'easy-invoice')
        );
        
        $message .= sprintf(
            '<p><strong>%s:</strong> %s</p>',
            __('Invoice Number', 'easy-invoice'),
            $invoice->getNumber()
        );
        
        $message .= sprintf(
            '<p><strong>%s:</strong> %s</p>',
            __('Amount', 'easy-invoice'),
            $this->formatCurrency($this->calculateInvoiceTotal($invoice), $invoice->getCurrencyCode(), $invoice->getCurrencyPosition())
        );
        
        $message .= sprintf(
            '<p><strong>%s:</strong> %s</p>',
            __('Due Date', 'easy-invoice'),
            $invoice->getDueDate()
        );
        
        // Allow plugins to modify the email message
        return apply_filters('easy_invoice_service_invoice_email_message', $message, $invoice);
    }
    
    /**
     * Sanitize invoice data
     *
     * @since 1.0.0
     * @param array $data The invoice data
     * @return array The sanitized data
     */
    protected function sanitizeInvoiceData(array $data): array {
        $sanitization_rules = [
            'title' => 'text_field',
            'description' => 'textarea',
            'number' => 'text_field',
            'issue_date' => 'text_field',
            'due_date' => 'text_field',
            'status' => 'text_field',
            'notes' => 'textarea',
            'terms' => 'html',
            'internal_notes' => 'textarea',
            'payment_instructions' => 'textarea',
            'payment_gateways' => 'array',
            'invoice_template' => 'text_field',
            'customer_name' => 'text_field',
            'customer_address' => 'textarea',
            'customer_email' => 'email',
            'shipping_name' => 'text_field',
            'shipping_address' => 'textarea',
            'discount_type' => 'text_field',
            'discount_value' => 'float',
            'tax_rate' => 'float',
            'calculation_method' => 'text_field',
            'prices_include_tax' => 'int',
            'currency_code' => 'text_field',
            'currency_position' => 'text_field',
            'footer_text' => 'textarea',
            'items' => 'array',
            'custom_fields' => 'array'
        ];
        
        return $this->sanitizeData($data, $sanitization_rules);
    }
} 