<?php
declare(strict_types=1);

namespace Instinctor\Frontend;

use Instinctor\Core\Config;
use Instinctor\Services\InvoiceService;

if (!defined('ABSPATH')) exit;

/**
 * InvoiceController — own-branded public invoice pay page at /invoice/{token}.
 *
 * GET renders the invoice + a Stripe Payment Element for the total; POST (from
 * the page's own JS, after Stripe confirms the payment) verifies the
 * PaymentIntent and marks the invoice paid. No login: the random token is the
 * capability. Self-contained HTML so it never depends on the page builder.
 */
class InvoiceController
{
    public static function showInvoice(array $p): void
    {
        $token = (string) ($p['token'] ?? '');
        $order = InvoiceService::getByToken($token);
        if (!$order) {
            if (!headers_sent()) http_response_code(404);           // a real 404, not a soft one
            self::renderShell('Invoice not found', '<p style="color:#666">This invoice link is invalid or has expired.</p>');
            return;
        }

        $meta   = $order->invoice_meta ?? [];
        $items  = (array) ($meta['items'] ?? []);
        $biz    = (string) (Config::get('instinctor_store_name', '') ?: 'Invoice');
        $bizPh  = (string) Config::get('instinctor_store_phone', '');
        $bizEm  = (string) Config::get('instinctor_store_email', '');
        $cur    = (string) (function_exists('get_option') ? get_option('instinctor_currency_symbol', '$') : '$');
        $total  = number_format((float) $order->total, 2);
        $isPaid = (($order->payment_status ?? '') === 'paid');

        ob_start();
        // Header
        echo '<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;margin-bottom:24px">';
        echo '<div><div style="font-size:20px;font-weight:700;color:#1d2327">' . esc_html($biz) . '</div>';
        if ($bizPh !== '') echo '<div style="font-size:13px;color:#666;margin-top:2px">' . esc_html($bizPh) . '</div>';
        if ($bizEm !== '') echo '<div style="font-size:13px;color:#666">' . esc_html($bizEm) . '</div>';
        echo '</div>';
        echo '<div style="text-align:right"><div style="font-size:13px;color:#999;text-transform:uppercase;letter-spacing:1px">Invoice</div>';
        echo '<div style="font-size:15px;font-weight:600;color:#1d2327">' . esc_html((string) $order->order_number) . '</div>';
        if ($isPaid) echo '<div style="display:inline-block;margin-top:6px;background:#d4edda;color:#155724;font-size:12px;font-weight:600;padding:3px 10px;border-radius:4px">PAID</div>';
        echo '</div></div>';

        // Line items
        echo '<table style="width:100%;border-collapse:collapse;margin-bottom:8px">';
        echo '<thead><tr><th style="text-align:left;padding:10px 0;border-bottom:2px solid #eee;font-size:12px;text-transform:uppercase;color:#999">Description</th><th style="text-align:right;padding:10px 0;border-bottom:2px solid #eee;font-size:12px;text-transform:uppercase;color:#999">Amount</th></tr></thead><tbody>';
        foreach ($items as $it) {
            echo '<tr><td style="padding:12px 0;border-bottom:1px solid #f0f0f0;color:#1d2327">' . esc_html((string) ($it['desc'] ?? '')) . '</td>'
               . '<td style="padding:12px 0;border-bottom:1px solid #f0f0f0;text-align:right;color:#1d2327;white-space:nowrap">' . esc_html($cur . number_format((float) ($it['amount'] ?? 0), 2)) . '</td></tr>';
        }
        echo '</tbody></table>';
        echo '<div style="display:flex;justify-content:flex-end;margin:14px 0 28px"><div style="min-width:200px"><div style="display:flex;justify-content:space-between;font-size:18px;font-weight:700;color:#1d2327;border-top:2px solid #1d2327;padding-top:10px"><span>Total</span><span>' . esc_html($cur . $total) . '</span></div></div></div>';

        if ($isPaid) {
            echo '<div style="text-align:center;color:#155724;background:#d4edda;border-radius:8px;padding:16px;font-weight:600">This invoice has been paid. Thank you.</div>';
        } else {
            $intent = InvoiceService::createPayIntent($order);
            if (empty($intent['success'])) {
                echo '<div class="np" style="text-align:center;color:#856404;background:#fff3cd;border-radius:8px;padding:16px">Online payment is not available for this invoice. Please contact ' . esc_html($biz) . '.</div>';
            } else {
                echo '<div id="inv-pay" class="np">';
                echo '<div id="inv-pay-element" style="margin-bottom:16px"></div>';
                echo '<div id="inv-error" style="display:none;color:#b91c1c;font-size:13px;margin-bottom:12px"></div>';
                echo '<button id="inv-pay-btn" style="width:100%;padding:14px;background:#1d2327;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer">Pay ' . esc_html($cur . $total) . '</button>';
                echo '</div>';
                // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- standalone-only invoice pay page; wp_enqueue_script is not available in this rendering context
                echo '<script src="https://js.stripe.com/v3/"></script>';
                // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- standalone-only; wp_enqueue_script unavailable in this rendering context
                echo '<script>(function(){'
                   . 'var pk=' . wp_json_encode((string) $intent['pk']) . ',cs=' . wp_json_encode((string) $intent['client_secret']) . ',piId=' . wp_json_encode((string) $intent['payment_intent_id']) . ';'
                   . 'var stripe=Stripe(pk),elements=stripe.elements({clientSecret:cs});elements.create("payment").mount("#inv-pay-element");'
                   . 'var btn=document.getElementById("inv-pay-btn"),err=document.getElementById("inv-error");'
                   . 'function showErr(m){err.textContent=m;err.style.display="block";btn.disabled=false;btn.textContent=' . wp_json_encode('Pay ' . $cur . $total) . ';}'
                   . 'btn.addEventListener("click",function(){btn.disabled=true;btn.textContent="Processing…";err.style.display="none";'
                   . 'stripe.confirmPayment({elements:elements,redirect:"if_required"}).then(function(r){'
                   . 'if(r.error){showErr(r.error.message||"Payment failed.");return;}'
                   . 'var fd=new URLSearchParams();fd.append("payment_intent_id",piId);'
                   . 'fetch(location.pathname,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:fd.toString()}).then(function(x){return x.json();}).then(function(res){if(res&&res.success){location.reload();}else{showErr((res&&res.error)||"Could not confirm payment.");}}).catch(function(){showErr("Network error.");});'
                   . '});});'
                   . '})();</script>';
            }
        }

        self::renderShell('Invoice ' . (string) $order->order_number . ', ' . $biz, (string) ob_get_clean(), true);
    }

    public static function handlePay(array $p): void
    {
        header('Content-Type: application/json');
        $token = (string) ($p['token'] ?? '');
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- capability is the unguessable invoice token in the URL; the amount + payment are re-verified server-side against Stripe in markPaidByToken().
        $piId  = isset($_POST['payment_intent_id']) ? sanitize_text_field(wp_unslash($_POST['payment_intent_id'])) : '';
        $res   = InvoiceService::markPaidByToken($token, $piId);
        echo wp_json_encode($res);
        exit;
    }

    /** Minimal branded HTML shell for the pay page. */
    private static function renderShell(string $title, string $inner, bool $printable = false): void
    {
        if (!headers_sent()) header('Content-Type: text/html; charset=utf-8');
        echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">';
        echo '<meta name="robots" content="noindex,nofollow"><title>' . esc_html($title) . '</title>';
        // An invoice for the books. Same approach as the estimate page: no PDF library,
        // because every browser has "Save as PDF" in its own print dialog. A PAID invoice
        // printed this way IS the receipt, so the paid banner deliberately survives.
        echo '<style>'
            . '@media print{'
            . 'body{background:#fff!important}'
            . '.np{display:none!important}'            // the card field, and anything you cannot use on paper
            . '.sheet{box-shadow:none!important;border-radius:0!important;padding:0!important;max-width:none!important}'
            . '.wrap{padding:0!important;max-width:none!important}'
            . 'a{text-decoration:none;color:#1d2327}'
            . '@page{margin:16mm}'
            . '}'
            . '.pbtn{display:block;width:100%;margin-top:10px;padding:12px;background:transparent;color:#555;'
            . 'border:1px solid #e0e0e0;border-radius:8px;font-size:13px;cursor:pointer;font-family:inherit;'
            . 'transition:background .15s ease-in-out}'
            . '.pbtn:hover{background:#f5f5f5}'
            . '</style></head>';
        echo '<body style="margin:0;background:#f5f5f7;font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,sans-serif;color:#1d2327">';
        echo '<div class="wrap" style="max-width:560px;margin:0 auto;padding:32px 16px">';
        echo '<div class="sheet" style="background:#fff;border-radius:14px;box-shadow:0 6px 30px rgba(0,0,0,0.08);padding:28px">';
        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $inner is composed above from esc_html()'d values + a curated Stripe script; not user input.
        echo $inner;
        if ($printable) {
            echo '<button type="button" class="pbtn np" onclick="window.print()">Print or save as PDF</button>';
        }
        echo '</div>';
        echo '<div class="np" style="text-align:center;margin-top:16px;font-size:12px;color:#999">Secured by Stripe</div>';
        echo '</div></body></html>';
        exit;
    }
}
