<?php
/**
 * Invoice Repository Class
 *
 * @package Easy_Invoice
 * @subpackage Repositories
 */

namespace EasyInvoice\Repositories;

use EasyInvoice\Interfaces\InvoiceRepositoryInterface;
use EasyInvoice\Models\Invoice;
use WP_Post;
use WP_Query;

/**
 * InvoiceRepository Class
 * 
 * Handles data access for invoice objects with extensible architecture for pro features.
 */
class InvoiceRepository implements InvoiceRepositoryInterface {
    
    /**
     * The post type name
     *
     * @var string
     */
    protected $post_type = \EasyInvoice\Constants\PostTypes::EASY_INVOICE_POST_TYPE;

    /**
     * Find an invoice by ID
     *
     * @param int $id The invoice ID
     * @return Invoice|null The invoice model or null if not found
     */
    public function find($id) {
        $post = get_post($id);
        
        if (!$post || $post->post_type !== $this->post_type) {
            return null;
        }
        
        $invoice = new Invoice($post);
        
        // Ensure invoice has proper slug for pretty URLs
        $invoice->ensureProperSlug();
        
        // Allow plugins to modify the found invoice
        return apply_filters('easy_invoice_invoice_found', $invoice, $id);
    }

    /**
     * Get all invoices
     *
     * @param array $args Optional arguments to filter the results
     * @return array Array of Invoice models
     */
    public function all($args = []) {
        $default_args = [
            'post_type' => $this->post_type,
            'posts_per_page' => -1,
            'post_status' => 'publish',
            'orderby' => 'date',
            'order' => 'DESC',
        ];
        
        $query_args = wp_parse_args($args, $default_args);
        
        // Allow plugins to modify query arguments
        $query_args = apply_filters('easy_invoice_invoice_query_args', $query_args);
    
        $query = new WP_Query($query_args);
        $invoices = [];
        
        if ($query->have_posts()) {
            foreach ($query->posts as $post) {
                $invoice = new Invoice($post);
                
                // Ensure invoice has proper slug for pretty URLs
                $invoice->ensureProperSlug();
                
                $invoices[] = $invoice;
            }
        }
        
        // Allow plugins to modify the results
        return apply_filters('easy_invoice_invoices_found', $invoices, $query_args);
    }

    /**
     * Create a new invoice
     *
     * @param array $data Invoice data
     * @return Invoice|false The created invoice or false on failure
     */
    public function create($data) {
        // Allow plugins to modify the data before creation
        $data = apply_filters('easy_invoice_invoice_create_data', $data);
        
        // Create a new invoice object
        $invoice = new Invoice();
        
        // Set basic data
        $invoice->setTitle($data['title'] ?? 'New Invoice');
        
        // Auto-generate invoice number if not provided
        if ((!isset($data['number']) || empty($data['number'])) && 
            (!isset($data['invoice_number']) || empty($data['invoice_number']))) {
            $invoice_number_service = easy_invoice_get_invoice_number_service();
            $data['number'] = $invoice_number_service->generateUniqueNumber();
        }
        
        // Set invoice data
        $this->setInvoiceData($invoice, $data, false);
        
        // Allow plugins to modify the invoice before saving
        do_action('easy_invoice_invoice_before_create', $invoice, $data);
        
        // Save the invoice (this will create the post and ensure proper permalinks)
        if ($invoice->save()) {
            // Allow plugins to perform actions after creation
            do_action('easy_invoice_invoice_created', $invoice, $data);
            return $invoice;
        }
        
        return false;
    }

    /**
     * Update an existing invoice
     *
     * @param int $id The invoice ID
     * @param array $data The invoice data
     * @return Invoice|null The updated invoice model or null if not found
     */
    public function update($id, $data) {
        try {
            $invoice = $this->find($id);
            
            if (!$invoice) {
                return null;
            }
            
            // Set invoice data
            $this->setInvoiceData($invoice, $data);
            
            // Save the invoice
            $result = $invoice->save();
            
            return $result ? $invoice : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    /**
     * Delete an invoice
     *
     * @param int $id The invoice ID
     * @return bool True if successful, false otherwise
     */
    public function delete($id) {
        $invoice = $this->find($id);
        
        if (!$invoice) {
            return false;
        }
        
        // Allow plugins to perform actions before deletion
        do_action('easy_invoice_invoice_before_delete', $invoice);
        
        $result = wp_delete_post($id, true);
        
        if ($result) {
            // Allow plugins to perform actions after deletion
            do_action('easy_invoice_invoice_deleted', $id);
        }
        
        return $result instanceof WP_Post;
    }

    /**
     * Find invoices by customer ID
     *
     * @param int $customer_id The customer ID
     * @return array Array of Invoice models
     */
    public function findByCustomer($customer_id) {
        $args = [
            'meta_query' => [
                [
                    'key' => '_easy_invoice_customer_id',
                    'value' => $customer_id,
                    'compare' => '=',
                ],
            ],
        ];
        
        $invoices = $this->all($args);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_invoices_by_customer', $invoices, $customer_id);
    }

    /**
     * Find invoices by status
     *
     * @param string $status The invoice status
     * @return array Array of Invoice models
     */
    public function findByStatus($status) {
        $args = [
            'meta_query' => [
                [
                    'key' => '_easy_invoice_status',
                    'value' => $status,
                    'compare' => '=',
                ],
            ],
        ];
        
        $invoices = $this->all($args);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_invoices_by_status', $invoices, $status);
    }

    /**
     * Find invoices due within a date range
     *
     * @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 findByDueDate($start_date, $end_date = null) {
        $meta_query = [
            [
                'key' => '_easy_invoice_due_date',
                'value' => $start_date,
                'compare' => '>=',
                'type' => 'DATE',
            ],
        ];
        
        if ($end_date) {
            $meta_query[] = [
                'key' => '_easy_invoice_due_date',
                'value' => $end_date,
                'compare' => '<=',
                'type' => 'DATE',
            ];
        }
        
        $args = [
            'meta_query' => $meta_query,
        ];
        
        $invoices = $this->all($args);
        
        // Allow plugins to modify the filtered results
        return apply_filters('easy_invoice_invoices_by_due_date', $invoices, $start_date, $end_date);
    }

    /**
     * Count invoices
     *
     * @param array $args Optional arguments to filter the results
     * @return int Number of invoices
     */
    public function count($args = []) {
        $default_args = [
            'post_type' => $this->post_type,
            'post_status' => 'publish',
        ];
        
        $query_args = wp_parse_args($args, $default_args);
        
        // Allow plugins to modify query arguments
        $query_args = apply_filters('easy_invoice_invoice_count_query_args', $query_args);
        
        $query = new WP_Query($query_args);
        
        $count = $query->found_posts;
        
        // Allow plugins to modify the count
        return apply_filters('easy_invoice_invoice_count', $count, $query_args);
    }

    /**
     * Find an invoice by its invoice number (stored in post meta)
     *
     * @param string $number Invoice number (exact match)
     * @return Invoice|null
     */
    public function findByNumber(string $number) {
        $args = [
            'post_type' => $this->post_type,
            'post_status' => ['publish', 'draft', 'private', 'pending'],
            'posts_per_page' => 1,
            'meta_query' => [
                [
                    'key' => \EasyInvoice\Constants\InvoiceMetaKeys::NUMBER,
                    'value' => $number,
                    'compare' => '=',
                ],
            ],
            'orderby' => 'date',
            'order' => 'DESC',
        ];

        $query = new WP_Query($args);
        if (!empty($query->posts)) {
            $post = $query->posts[0];
            if ($post && $post->post_type === $this->post_type) {
                $invoice = new Invoice($post);
                $invoice->ensureProperSlug();
                return apply_filters('easy_invoice_invoice_found_by_number', $invoice, $number);
            }
        }
        return null;
    }

    /**
     * Find published invoice by ID
     *
     * @param int $id The invoice ID
     * @return Invoice|null The invoice model or null if not found
     */
    public function findPublished(int $id) {
        $post = get_post($id);
        
        if (!$post || $post->post_type !== $this->post_type || $post->post_status !== 'publish') {
            return null;
        }
        
        $invoice = new Invoice($post);
        
        // Ensure invoice has proper slug for pretty URLs
        $invoice->ensureProperSlug();
        
        return $invoice;
    }

    /**
     * Update all existing draft invoices to published status for proper permalinks
     *
     * @return int Number of invoices updated
     */
    public function updateAllExistingInvoices(): int {
        global $wpdb;
        
        // Find all draft invoices
        $draft_invoices = $wpdb->get_col($wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts} 
            WHERE post_type = %s 
            AND post_status = 'draft'",
            $this->post_type
        ));
        
        $updated_count = 0;
        
        foreach ($draft_invoices as $invoice_id) {
            $invoice = $this->find($invoice_id);
            if ($invoice) {
                // Save the invoice (this will publish it and ensure proper permalinks)
                if ($invoice->save()) {
                    $updated_count++;
                }
            }
        }
        
        // Allow plugins to perform actions after bulk update
        do_action('easy_invoice_invoices_bulk_updated', $updated_count);
        
        return $updated_count;
    }

    /**
     * Set invoice data from array
     *
     * @param Invoice $invoice
     * @param array $data
     * @param bool $preserve_number
     * @return void
     */
    protected function setInvoiceData(Invoice $invoice, array $data, $preserve_number = false) {
        // Allow plugins to modify the data setting process
        do_action('easy_invoice_invoice_set_data_before', $invoice, $data);
        
        // Set basic invoice fields (core fields that have dedicated methods)
        if (isset($data['title'])) {
            $invoice->setTitle($data['title']);
        }
        
        if (isset($data['invoice_title'])) {
            $invoice->setTitle($data['invoice_title']);
        }
        
        // Always update description if it exists in data (even if empty, to allow clearing)
        if (array_key_exists('description', $data)) {
            $invoice->setDescription($data['description'] ?? '');
        }
        
        if (array_key_exists('invoice_description', $data)) {
            $invoice->setDescription($data['invoice_description'] ?? '');
        }
        
        // Set invoice number (only if not preserving existing)
        if (isset($data['invoice_number']) && !empty($data['invoice_number'])) {
            $invoice->setNumber($data['invoice_number']);
        } elseif (isset($data['number']) && !empty($data['number'])) {
            $invoice->setNumber($data['number']);
        }
        
        // Set dates
        if (isset($data['issue_date'])) {
            $invoice->setIssueDate($data['issue_date']);
        }
        
        if (isset($data['invoice_date'])) {
            $invoice->setIssueDate($data['invoice_date']);
        }
        
        if (isset($data['issue-date'])) {
            $invoice->setIssueDate($data['issue-date']);
        }
        
        if (isset($data['due_date'])) {
            $invoice->setDueDate($data['due_date']);
        }
        
        if (isset($data['due-date'])) {
            $invoice->setDueDate($data['due-date']);
        }
        
        // Set status
        if (isset($data['status'])) {
            $invoice->setStatus($data['status']);
        }
        
        if (isset($data['invoice_status'])) {
            $invoice->setStatus($data['invoice_status']);
        }
        
        if (isset($data['payment_status'])) {
            $invoice->setStatus($data['payment_status']);
        }
        
        // Set notes and terms
        if (isset($data['notes'])) {
            $invoice->setNotes($data['notes']);
        }
        
        if (isset($data['terms_and_conditions'])) {
            $invoice->setTerms($data['terms_and_conditions']);
        }
        
        if (isset($data['internal_notes'])) {
            $invoice->setInternalNotes($data['internal_notes']);
        }
        
        if (isset($data['payment_instructions'])) {
            $invoice->setPaymentInstructions($data['payment_instructions']);
        }
        
        // Set payment gateways
        if (isset($data['payment_gateways']) && is_array($data['payment_gateways'])) {
            $invoice->setPaymentGateways($data['payment_gateways']);
        } elseif (isset($data['payment_gateways']) && is_string($data['payment_gateways'])) {
            $gateways = array_filter(explode(',', $data['payment_gateways']));
            $invoice->setPaymentGateways($gateways);
        } else {
            $invoice->setPaymentGateways([]);
        }
        
        // Set payment gateways toggle state
        if (isset($data['payment_gateways_toggle_state'])) {
            $invoice->setPaymentGatewaysToggleState((string)$data['payment_gateways_toggle_state']);
        }
        
        // Set template
        if (isset($data['invoice_template'])) {
            $invoice->setTemplate($data['invoice_template']);
        }
        
        // Set client information
        if (isset($data['customer_name'])) {
            $invoice->setCustomerName($data['customer_name']);
        }
        
        if (isset($data['customer_address'])) {
            $invoice->setCustomerAddress($data['customer_address']);
        }
        
        if (isset($data['customer_email'])) {
            $invoice->setCustomerEmail($data['customer_email']);
        }
        
        // Set discount and tax settings
        if (isset($data['discount_type'])) {
            $invoice->setDiscountType($data['discount_type']);
        }
        
        if (isset($data['discount_value'])) {
            $invoice->setDiscountValue($data['discount_value']);
        }
        
        if (isset($data['discount'])) {
            $invoice->setDiscountValue($data['discount']);
        }
        
        if (isset($data['tax_rate'])) {
            $invoice->setTaxRate($data['tax_rate']);
        }
        
        if (isset($data['discount_calculation_method'])) {
            $invoice->setDiscountCalculationMethod($data['discount_calculation_method']);
        } elseif (isset($data['calculation_method'])) {
            $invoice->setDiscountCalculationMethod($data['calculation_method']);
        }
        
        if (isset($data['prices_include_tax'])) {
            $invoice->setPricesIncludeTax($data['prices_include_tax']);
        }
        
        // Set currency settings
        if (isset($data['currency_code'])) {
            $invoice->setCurrencyCode($data['currency_code']);
        }
        
        if (isset($data['currency_position'])) {
            $invoice->setCurrencyPosition($data['currency_position']);
        }
        
        // Set footer text
        if (isset($data['footer_text'])) {
            $invoice->setFooterText($data['footer_text']);
        }
        
        // Set client ID
        if (isset($data['client_id']) && !empty($data['client_id'])) {
            $invoice->setClientId((int) $data['client_id']);
        }
        
        // Set items
        if (isset($data['items']) && is_array($data['items'])) {
            $invoice->setItems($data['items']);
        }
        
        // Set custom fields
        if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
            $invoice->setCustomFields($data['custom_fields']);
        }
        
        // Set recurring fields
        if (isset($data['recurring_enabled'])) {
            $invoice->setRecurringEnabled((bool) $data['recurring_enabled']);
        }
        
        if (isset($data['recurring_frequency'])) {
            $invoice->setRecurringFrequency($data['recurring_frequency']);
        }
        
        if (isset($data['recurring_interval'])) {
            $invoice->setRecurringInterval((int) $data['recurring_interval']);
        }
        
        if (isset($data['recurring_start_date'])) {
            $invoice->setRecurringStartDate($data['recurring_start_date']);
        }
        
        // Use configuration-driven approach for all other fields
        $form_processor = new \EasyInvoice\Forms\FormProcessor();
        $field_registration = new \EasyInvoice\Forms\Invoice\InvoiceFieldRegistration();
        
        // Get all field definitions from all tabs
        $field_definitions = [];
        $tabs = $field_registration->getTabs();
        foreach ($tabs as $tab_id => $tab) {
            $tab_fields = $field_registration->getFields($tab_id);
            foreach ($tab_fields as $field) {
                $field_definitions[] = $field;
            }
        }
        
        // Save form data using field configuration
        $form_processor->saveFormDataToDatabase($data, $field_definitions, $invoice);
        
        // Allow plugins to modify the data setting process
        do_action('easy_invoice_invoice_set_data_after', $invoice, $data);
    }
} 