<?php
namespace ComptaFlowUK\Services;

defined('ABSPATH') || exit;

class InvoiceService {

    private static function prefix(): string {
        global $wpdb;
        return $wpdb->prefix . 'cfuk_';
    }

    public static function create(array $data, array $lines): int {
        global $wpdb;
        $p = self::prefix();

        $data['uuid'] = wp_generate_uuid4();
        $data['invoice_type'] = 'INVOICE';
        $data['created_by'] = get_current_user_id();
        $data['created_at'] = current_time('mysql');

        if (empty($data['invoice_number'])) {
            $data['invoice_number'] = NumberingService::nextInvoiceNumber();
        }

        $totals = self::calculateTotals($lines);
        $data['subtotal'] = $totals['subtotal'];
        $data['total_vat'] = $totals['total_vat'];
        $data['total'] = $totals['total'];

        $wpdb->insert($p . 'invoices', $data);
        $invoice_id = $wpdb->insert_id;

        self::saveLines($invoice_id, $lines);

        return $invoice_id;
    }

    public static function update(int $id, array $data, array $lines): bool {
        global $wpdb;
        $p = self::prefix();

        $totals = self::calculateTotals($lines);
        $data['subtotal'] = $totals['subtotal'];
        $data['total_vat'] = $totals['total_vat'];
        $data['total'] = $totals['total'];
        $data['updated_at'] = current_time('mysql');

        $wpdb->update($p . 'invoices', $data, ['id' => $id]);
        $wpdb->delete($p . 'invoice_lines', ['invoice_id' => $id]);
        self::saveLines($id, $lines);

        return true;
    }

    public static function delete(int $id): bool {
        global $wpdb;
        $p = self::prefix();
        $wpdb->delete($p . 'invoice_lines', ['invoice_id' => $id]);
        return $wpdb->delete($p . 'invoices', ['id' => $id]) !== false;
    }

    public static function getById(int $id): ?array {
        global $wpdb;
        $p = self::prefix();
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$p}invoices WHERE id = %d",
            $id
        ), ARRAY_A);
        return $row ?: null;
    }

    public static function getLines(int $invoice_id): array {
        global $wpdb;
        $p = self::prefix();
        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$p}invoice_lines WHERE invoice_id = %d ORDER BY line_order ASC",
            $invoice_id
        ), ARRAY_A) ?: [];
    }

    public static function getAll(string $type = 'INVOICE', string $search = '', string $status = '', int $limit = 50, int $offset = 0): array {
        global $wpdb;
        $p = self::prefix();
        $uid = get_current_user_id();

        $where = $wpdb->prepare("WHERE i.invoice_type = %s AND i.created_by = %d", $type, $uid);
        if ($status) {
            $where .= $wpdb->prepare(" AND i.status = %s", $status);
        }
        if ($search) {
            $like = '%' . $wpdb->esc_like($search) . '%';
            $where .= $wpdb->prepare(
                " AND (i.invoice_number LIKE %s OR c.company_name LIKE %s OR c.contact_name LIKE %s)",
                $like, $like, $like
            );
        }

        return $wpdb->get_results(
            "SELECT i.*, c.company_name, c.contact_name
             FROM {$p}invoices i
             LEFT JOIN {$p}clients c ON i.client_id = c.id
             {$where}
             ORDER BY i.issue_date DESC, i.id DESC
             LIMIT {$limit} OFFSET {$offset}",
            ARRAY_A
        ) ?: [];
    }

    public static function count(string $type = 'INVOICE', string $search = '', string $status = ''): int {
        global $wpdb;
        $p = self::prefix();
        $uid = get_current_user_id();

        $where = $wpdb->prepare("WHERE i.invoice_type = %s AND i.created_by = %d", $type, $uid);
        if ($status) {
            $where .= $wpdb->prepare(" AND i.status = %s", $status);
        }
        if ($search) {
            $like = '%' . $wpdb->esc_like($search) . '%';
            $where .= $wpdb->prepare(
                " AND (i.invoice_number LIKE %s OR c.company_name LIKE %s OR c.contact_name LIKE %s)",
                $like, $like, $like
            );
        }

        return (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$p}invoices i LEFT JOIN {$p}clients c ON i.client_id = c.id {$where}"
        );
    }

    public static function markAsSent(int $id): void {
        global $wpdb;
        $p = self::prefix();
        $wpdb->update($p . 'invoices', ['status' => 'SENT', 'updated_at' => current_time('mysql')], ['id' => $id]);
    }

    public static function markAsPaid(int $id, float $amount = 0): void {
        global $wpdb;
        $p = self::prefix();

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

        $paid = $amount > 0 ? $amount : floatval($invoice['total']);
        $wpdb->update($p . 'invoices', [
            'status' => 'PAID',
            'amount_paid' => $paid,
            'updated_at' => current_time('mysql'),
        ], ['id' => $id]);

        JournalEntryGenerator::generateInvoicePaymentEntries($invoice);
    }

    public static function markAsCancelled(int $id): void {
        global $wpdb;
        $p = self::prefix();
        $wpdb->update($p . 'invoices', ['status' => 'CANCELLED', 'updated_at' => current_time('mysql')], ['id' => $id]);
    }

    public static function checkOverdue(): void {
        global $wpdb;
        $p = self::prefix();
        $today = gmdate('Y-m-d');

        $wpdb->query($wpdb->prepare(
            "UPDATE {$p}invoices SET status = 'OVERDUE', updated_at = %s
             WHERE invoice_type = 'INVOICE' AND status = 'SENT' AND due_date < %s",
            current_time('mysql'), $today
        ));
    }

    private static function saveLines(int $invoice_id, array $lines): void {
        global $wpdb;
        $p = self::prefix();

        foreach ($lines as $i => $line) {
            $line_total = round(floatval($line['quantity']) * floatval($line['unit_price']), 2);
            $vat_amount = round($line_total * floatval($line['vat_rate']) / 100, 2);
            $gross_total = $line_total + $vat_amount;

            $wpdb->insert($p . 'invoice_lines', [
                'invoice_id' => $invoice_id,
                'line_order' => $i,
                'description' => sanitize_text_field($line['description']),
                'quantity' => floatval($line['quantity']),
                'unit_price' => floatval($line['unit_price']),
                'vat_rate' => floatval($line['vat_rate']),
                'vat_code' => sanitize_text_field($line['vat_code'] ?? 'GB-20'),
                'account_code' => sanitize_text_field($line['account_code'] ?? '4010'),
                'line_total' => $line_total,
                'vat_amount' => $vat_amount,
                'gross_total' => $gross_total,
            ]);
        }
    }

    private static function calculateTotals(array $lines): array {
        $subtotal = 0;
        $total_vat = 0;

        foreach ($lines as $line) {
            $line_total = round(floatval($line['quantity']) * floatval($line['unit_price']), 2);
            $vat_amount = round($line_total * floatval($line['vat_rate']) / 100, 2);
            $subtotal += $line_total;
            $total_vat += $vat_amount;
        }

        return [
            'subtotal' => $subtotal,
            'total_vat' => $total_vat,
            'total' => $subtotal + $total_vat,
        ];
    }
}
