<?php
/**
 * ComptaFlow - Service de gestion des factures
 * CRUD complet pour factures et lignes de facture
 */

namespace ComptaFlowPro\Services;

defined('ABSPATH') || exit;

class InvoiceService {

    /**
     * Creer une nouvelle facture
     */
    public static function create(array $data, array $lines = []): int|false {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $uuid = wp_generate_uuid4();
        $invoice_number = NumberingService::getNextInvoiceNumber();

        $totals = self::calculateTotals($lines);

        $settings = get_option('comptaflow_company_settings', []);
        $default_delay = $settings['default_payment_delay'] ?? 30;
        $date_invoice = $data['date_invoice'] ?? gmdate('Y-m-d');
        $date_due = $data['date_due'] ?? gmdate('Y-m-d', strtotime($date_invoice . ' + ' . $default_delay . ' days'));

        $insert_data = [
            'uuid' => $uuid,
            'invoice_number' => $invoice_number,
            'client_id' => absint($data['client_id'] ?? 0),
            'date_invoice' => $date_invoice,
            'date_due' => $date_due,
            'status' => 'DRAFT',
            'total_ht' => $totals['total_ht'],
            'total_tva' => $totals['total_tva'],
            'total_ttc' => $totals['total_ttc'],
            'notes' => sanitize_textarea_field($data['notes'] ?? ''),
            'created_at' => current_time('mysql'),
            'created_by' => get_current_user_id(),
        ];

        if (empty($insert_data['client_id'])) {
            return false;
        }

        $result = $wpdb->insert($table, $insert_data);

        if ($result === false) {
            return false;
        }

        $invoice_id = $wpdb->insert_id;

        if (!empty($lines)) {
            self::saveLines($invoice_id, $lines);
        }

        return $invoice_id;
    }

    /**
     * Mettre a jour une facture
     */
    public static function update(int $id, array $data, ?array $lines = null): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $invoice = self::getById($id);
        if (!$invoice) {
            return false;
        }

        // Ne pas modifier une facture validee (sauf statut paiement)
        if ($invoice['status'] !== 'DRAFT' && !isset($data['force_update'])) {
            $allowed_fields = ['status', 'payment_date', 'payment_method', 'notes'];
            $data = array_intersect_key($data, array_flip($allowed_fields));
        }

        if ($lines !== null) {
            $totals = self::calculateTotals($lines);
            $data['total_ht'] = $totals['total_ht'];
            $data['total_tva'] = $totals['total_tva'];
            $data['total_ttc'] = $totals['total_ttc'];
        }

        $update_data = [];

        $fields = [
            'client_id' => 'absint',
            'date_invoice' => 'sanitize_text_field',
            'date_due' => 'sanitize_text_field',
            'status' => 'sanitize_text_field',
            'payment_date' => 'sanitize_text_field',
            'payment_method' => 'sanitize_text_field',
            'total_ht' => 'floatval',
            'total_tva' => 'floatval',
            'total_ttc' => 'floatval',
            'notes' => 'sanitize_textarea_field',
        ];

        foreach ($fields as $field => $sanitizer) {
            if (isset($data[$field])) {
                $value = $data[$field];
                if ($value === '' && in_array($field, ['payment_date', 'payment_method'])) {
                    $update_data[$field] = null;
                } else {
                    $update_data[$field] = $sanitizer($value);
                }
            }
        }

        if (empty($update_data) && $lines === null) {
            return true;
        }

        if (!empty($update_data)) {
            $result = $wpdb->update($table, $update_data, ['id' => $id]);
            if ($result === false) {
                return false;
            }
        }

        if ($lines !== null) {
            self::deleteLines($id);
            self::saveLines($id, $lines);
        }

        return true;
    }

    /**
     * Supprimer une facture (brouillon uniquement)
     */
    public static function delete(int $id): bool|string {
        global $wpdb;

        $invoice = self::getById($id);
        if (!$invoice) {
            return 'Facture introuvable.';
        }

        if ($invoice['status'] !== 'DRAFT') {
            return 'Impossible de supprimer une facture validee.';
        }

        self::deleteLines($id);

        $result = $wpdb->delete($wpdb->prefix . 'cfp_invoices', ['id' => $id]);

        return $result !== false;
    }

    /**
     * Obtenir une facture par ID
     */
    public static function getById(int $id, bool $with_lines = false): ?array {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT i.*, c.name as client_name, c.email as client_email
             FROM {$table} i
             LEFT JOIN {$wpdb->prefix}cfp_clients c ON i.client_id = c.id
             WHERE i.id = %d",
            $id
        ), ARRAY_A);

        if (!$invoice) {
            return null;
        }

        if ($with_lines) {
            $invoice['lines'] = self::getLines($id);
        }

        return $invoice;
    }

    /**
     * Obtenir toutes les factures avec filtres
     */
    public static function getAll(array $args = []): array {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';
        $clients_table = $wpdb->prefix . 'cfp_clients';

        $where = ['1=1'];
        $params = [];

        if (!empty($args['client_id'])) {
            $where[] = 'i.client_id = %d';
            $params[] = absint($args['client_id']);
        }

        if (!empty($args['status'])) {
            if (is_array($args['status'])) {
                $placeholders = implode(',', array_fill(0, count($args['status']), '%s'));
                $where[] = "i.status IN ($placeholders)";
                $params = array_merge($params, $args['status']);
            } else {
                $where[] = 'i.status = %s';
                $params[] = $args['status'];
            }
        }

        if (!empty($args['date_from'])) {
            $where[] = 'i.date_invoice >= %s';
            $params[] = $args['date_from'];
        }
        if (!empty($args['date_to'])) {
            $where[] = 'i.date_invoice <= %s';
            $params[] = $args['date_to'];
        }

        if (!empty($args['search'])) {
            $search = '%' . $wpdb->esc_like($args['search']) . '%';
            $where[] = $wpdb->prepare(
                "(i.invoice_number LIKE %s OR c.name LIKE %s)",
                $search, $search
            );
        }

        $where_clause = implode(' AND ', $where);

        $orderby = in_array($args['orderby'] ?? '', ['date_invoice', 'date_due', 'total_ttc', 'invoice_number', 'status'])
            ? 'i.' . $args['orderby']
            : 'i.date_invoice';
        $order = strtoupper($args['order'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';

        $limit = isset($args['limit']) ? absint($args['limit']) : 50;
        $offset = isset($args['offset']) ? absint($args['offset']) : 0;

        $sql = "SELECT i.*, c.name as client_name, c.email as client_email
                FROM {$table} i
                LEFT JOIN {$clients_table} c ON i.client_id = c.id
                WHERE {$where_clause}
                ORDER BY {$orderby} {$order}
                LIMIT %d OFFSET %d";

        $params[] = $limit;
        $params[] = $offset;

        if (!empty($params)) {
            $sql = $wpdb->prepare($sql, $params);
        }

        return $wpdb->get_results($sql, ARRAY_A);
    }

    /**
     * Compter les factures
     */
    public static function count(array $args = []): int {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $where = ['1=1'];
        $params = [];

        if (!empty($args['status'])) {
            if (is_array($args['status'])) {
                $placeholders = implode(',', array_fill(0, count($args['status']), '%s'));
                $where[] = "status IN ($placeholders)";
                $params = array_merge($params, $args['status']);
            } else {
                $where[] = 'status = %s';
                $params[] = $args['status'];
            }
        }

        $where_clause = implode(' AND ', $where);
        $sql = "SELECT COUNT(*) FROM {$table} WHERE {$where_clause}";

        if (!empty($params)) {
            $sql = $wpdb->prepare($sql, $params);
        }

        return (int) $wpdb->get_var($sql);
    }

    /**
     * Obtenir les lignes d'une facture
     */
    public static function getLines(int $invoice_id): array {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoice_lines';

        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table} WHERE invoice_id = %d ORDER BY sort_order ASC, id ASC",
            $invoice_id
        ), ARRAY_A);
    }

    /**
     * Sauvegarder les lignes d'une facture
     */
    public static function saveLines(int $invoice_id, array $lines): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoice_lines';

        $sort_order = 0;
        foreach ($lines as $line) {
            if (empty($line['description'])) {
                continue;
            }

            $quantity = floatval($line['quantity'] ?? 1);
            $unit_price_ht = floatval($line['unit_price_ht'] ?? 0);
            $vat_rate = floatval($line['vat_rate'] ?? 20);

            $total_ht = round($quantity * $unit_price_ht, 2);
            $total_tva = round($total_ht * ($vat_rate / 100), 2);
            $total_ttc = round($total_ht + $total_tva, 2);

            $account_code = sanitize_text_field($line['account_code'] ?? '706');

            $wpdb->insert($table, [
                'invoice_id' => $invoice_id,
                'description' => sanitize_text_field($line['description']),
                'account_code' => $account_code,
                'quantity' => $quantity,
                'unit_price_ht' => $unit_price_ht,
                'vat_rate' => $vat_rate,
                'total_ht' => $total_ht,
                'total_tva' => $total_tva,
                'total_ttc' => $total_ttc,
                'sort_order' => $sort_order++,
            ]);
        }

        return true;
    }

    /**
     * Supprimer les lignes d'une facture
     */
    public static function deleteLines(int $invoice_id): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoice_lines';

        $wpdb->delete($table, ['invoice_id' => $invoice_id]);

        return true;
    }

    /**
     * Calculer les totaux depuis les lignes
     */
    public static function calculateTotals(array $lines): array {
        $total_ht = 0;
        $total_tva = 0;

        foreach ($lines as $line) {
            if (empty($line['description'])) {
                continue;
            }

            $quantity = floatval($line['quantity'] ?? 1);
            $unit_price_ht = floatval($line['unit_price_ht'] ?? 0);
            $vat_rate = floatval($line['vat_rate'] ?? 20);

            $line_ht = $quantity * $unit_price_ht;
            $line_tva = $line_ht * ($vat_rate / 100);

            $total_ht += $line_ht;
            $total_tva += $line_tva;
        }

        return [
            'total_ht' => round($total_ht, 2),
            'total_tva' => round($total_tva, 2),
            'total_ttc' => round($total_ht + $total_tva, 2),
        ];
    }

    /**
     * Valider une facture (DRAFT -> PENDING)
     */
    public static function validate(int $id): bool {
        $invoice = self::getById($id);

        if (!$invoice || $invoice['status'] !== 'DRAFT') {
            return false;
        }

        return self::update($id, [
            'status' => 'PENDING',
            'force_update' => true,
        ]);
    }

    /**
     * Marquer une facture comme payee
     */
    public static function markAsPaid(int $id, ?string $payment_date = null, ?string $payment_method = null): bool {
        return self::update($id, [
            'status' => 'PAID',
            'payment_date' => $payment_date ?? gmdate('Y-m-d'),
            'payment_method' => $payment_method,
            'force_update' => true,
        ]);
    }

    /**
     * Mettre a jour les factures en retard
     */
    public static function updateOverdueStatus(): int {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $result = $wpdb->query($wpdb->prepare(
            "UPDATE {$table}
             SET status = 'OVERDUE'
             WHERE status = 'PENDING'
             AND date_due < %s",
            gmdate('Y-m-d')
        ));

        return (int) $result;
    }

    /**
     * Enregistrer une facture en comptabilite
     * Cree une transaction dans cfp_transactions + ecritures dans cfp_journal_entries
     */
    public static function registerToAccounting(int $id, array $options = []): int|false {
        global $wpdb;

        $invoice = self::getById($id, true);

        if (!$invoice) {
            return false;
        }

        if (!empty($invoice['transaction_id'])) {
            return false;
        }

        if ($invoice['status'] === 'DRAFT') {
            return false;
        }

        $client = ClientService::getById($invoice['client_id']);
        $client_name = $client ? $client['name'] : 'Client';

        $account_code = $options['account_code'] ?? '706';

        // Calculer le taux TVA moyen pondere depuis les lignes
        $vat_rate = 20.0;
        if (!empty($invoice['lines'])) {
            $total_ht = 0;
            $weighted_rate = 0;
            foreach ($invoice['lines'] as $line) {
                $line_ht = floatval($line['total_ht']);
                $total_ht += $line_ht;
                $weighted_rate += $line_ht * floatval($line['vat_rate']);
            }
            if ($total_ht > 0) {
                $vat_rate = $weighted_rate / $total_ht;
            }
        }

        $transaction_data = [
            'uuid' => wp_generate_uuid4(),
            'type' => 'SALE',
            'date_txn' => $invoice['date_invoice'],
            'label' => 'Facture ' . $invoice['invoice_number'] . ' - ' . $client_name,
            'account_code' => $account_code,
            'amount_ht' => $invoice['total_ht'],
            'amount_tva' => $invoice['total_tva'],
            'amount_ttc' => $invoice['total_ttc'],
            'vat_rate' => $vat_rate,
            'vat_code' => 'FR-' . number_format($vat_rate, 0),
            'payment_method' => $invoice['payment_method'] ?? 'VIREMENT',
            'payment_status' => ($invoice['status'] === 'PAID') ? 'PAID' : 'PENDING',
            'payment_date' => $invoice['payment_date'],
            'partner_name' => $client_name,
            'partner_code' => $client['siret'] ?? '',
            'piece_ref' => $invoice['invoice_number'],
            'journal_code' => 'VE',
            'status' => 'POSTED',
            'created_at' => current_time('mysql'),
            'created_by' => get_current_user_id(),
        ];

        $prefix = $wpdb->prefix . 'cfp_';

        $wpdb->query('START TRANSACTION');

        try {
            $result = $wpdb->insert($prefix . 'transactions', $transaction_data);

            if ($result === false) {
                throw new \Exception('Erreur insertion transaction');
            }

            $transaction_id = $wpdb->insert_id;

            $sale_data = array_merge($transaction_data, [
                'id' => $transaction_id,
                'invoice_id' => $id,
            ]);
            JournalEntryGenerator::generateInvoiceEntries($sale_data, $transaction_id);

            $wpdb->update(
                $prefix . 'invoices',
                ['transaction_id' => $transaction_id],
                ['id' => $id]
            );

            $wpdb->query('COMMIT');

            return $transaction_id;

        } catch (\Exception $e) {
            $wpdb->query('ROLLBACK');
            return false;
        }
    }

    /**
     * Enregistrer le paiement d'une facture en comptabilite
     */
    public static function registerPaymentToAccounting(int $id, string $payment_method = 'VIREMENT', ?string $payment_date = null): bool {
        global $wpdb;

        $invoice = self::getById($id);

        if (!$invoice || empty($invoice['transaction_id']) || $invoice['status'] === 'PAID') {
            return false;
        }

        $payment_date = $payment_date ?? gmdate('Y-m-d');
        $prefix = $wpdb->prefix . 'cfp_';

        $client = ClientService::getById($invoice['client_id']);
        $client_name = $client ? $client['name'] : 'Client';

        $wpdb->query('START TRANSACTION');

        try {
            $payment_data = [
                'invoice_id' => $id,
                'transaction_id' => $invoice['transaction_id'],
                'date_txn' => $payment_date,
                'amount_ttc' => $invoice['total_ttc'],
                'payment_method' => $payment_method,
                'partner_name' => $client_name,
                'piece_ref' => $invoice['invoice_number'],
            ];

            JournalEntryGenerator::generateInvoicePaymentEntries($payment_data, $invoice['transaction_id']);

            $wpdb->update(
                $prefix . 'invoices',
                [
                    'status' => 'PAID',
                    'payment_date' => $payment_date,
                    'payment_method' => $payment_method,
                ],
                ['id' => $id]
            );

            $wpdb->update(
                $prefix . 'transactions',
                [
                    'payment_status' => 'PAID',
                    'payment_date' => $payment_date,
                    'payment_method' => $payment_method,
                ],
                ['id' => $invoice['transaction_id']]
            );

            $wpdb->query('COMMIT');

            return true;

        } catch (\Exception $e) {
            $wpdb->query('ROLLBACK');
            return false;
        }
    }

    /**
     * Obtenir les statistiques des factures
     */
    public static function getStats(): array {
        global $wpdb;
        $table = $wpdb->prefix . 'cfp_invoices';

        $by_status = $wpdb->get_results(
            "SELECT status, COUNT(*) as count, SUM(total_ttc) as total
             FROM {$table}
             GROUP BY status",
            ARRAY_A
        );

        $stats = [
            'draft' => ['count' => 0, 'total' => 0],
            'pending' => ['count' => 0, 'total' => 0],
            'paid' => ['count' => 0, 'total' => 0],
            'overdue' => ['count' => 0, 'total' => 0],
            'cancelled' => ['count' => 0, 'total' => 0],
        ];

        foreach ($by_status as $row) {
            $key = strtolower($row['status']);
            if (isset($stats[$key])) {
                $stats[$key] = [
                    'count' => (int) $row['count'],
                    'total' => (float) $row['total'],
                ];
            }
        }

        $stats['unpaid'] = [
            'count' => $stats['pending']['count'] + $stats['overdue']['count'],
            'total' => $stats['pending']['total'] + $stats['overdue']['total'],
        ];

        return $stats;
    }

    /**
     * Dupliquer une facture
     */
    public static function duplicate(int $id): int|false {
        $invoice = self::getById($id, true);

        if (!$invoice) {
            return false;
        }

        $data = [
            'client_id' => $invoice['client_id'],
            'date_invoice' => gmdate('Y-m-d'),
            'notes' => $invoice['notes'],
        ];

        $lines = [];
        foreach ($invoice['lines'] as $line) {
            $lines[] = [
                'description' => $line['description'],
                'account_code' => $line['account_code'],
                'quantity' => $line['quantity'],
                'unit_price_ht' => $line['unit_price_ht'],
                'vat_rate' => $line['vat_rate'],
            ];
        }

        return self::create($data, $lines);
    }
}
