<?php

declare(strict_types=1);

namespace FatBottom\Services;

use FatBottom\Core\Config;
use FatBottom\Core\Database;
use RuntimeException;

final class OrderStatusService
{
    public const STATUSES = ['pending', 'accepted', 'declined', 'processing', 'ready', 'completed'];

    public function __construct(
        private readonly Config $config,
        private readonly Database $db,
        private readonly Mailer $mailer
    ) {
    }

    public function update(
        int $orderId,
        string $status,
        string $note,
        bool $customerVisible,
        bool $sendEmail,
        int $staffUserId
    ): array {
        if (!in_array($status, self::STATUSES, true) || $status === 'pending') {
            throw new RuntimeException('Choose a valid order status.');
        }
        $order = $this->db->fetch('SELECT * FROM orders WHERE id = ?', [$orderId]);
        if ($order === null) {
            throw new RuntimeException('Order not found.');
        }

        $sendEmail = $sendEmail || $status === 'accepted';
        $pdo = $this->db->pdo();
        $pdo->beginTransaction();
        try {
            $this->db->execute(
                'UPDATE orders SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
                [$status, $orderId]
            );
            $this->db->execute(
                'INSERT INTO order_events
                    (order_id, user_id, event_type, details, status, note, customer_visible, email_sent)
                 VALUES (?, ?, ?, ?, ?, ?, ?, 0)',
                [
                    $orderId,
                    $staffUserId,
                    'status_changed',
                    'Status changed to ' . order_status_label($status) . ($note !== '' ? '. ' . $note : '.'),
                    $status,
                    $note,
                    $customerVisible && $note !== '' ? 1 : 0,
                ]
            );
            $eventId = (int) $pdo->lastInsertId();
            $pdo->commit();
        } catch (\Throwable $error) {
            $pdo->rollBack();
            throw $error;
        }

        $emailError = null;
        if ($sendEmail) {
            $order['status'] = $status;
            try {
                $this->sendStatusEmail($order, $status, $customerVisible ? $note : '');
                $this->db->execute('UPDATE order_events SET email_sent = 1 WHERE id = ?', [$eventId]);
            } catch (\Throwable $error) {
                $emailError = $error->getMessage();
            }
        }

        return ['email_requested' => $sendEmail, 'email_error' => $emailError];
    }

    public function sendStatusEmail(array $order, string $status, string $visibleNote = ''): void
    {
        $subjectTemplate = (string) $this->config->get(
            'order_emails.' . $status . '.subject',
            '{{business_name}} order {{order_number}}: {{status_label}}'
        );
        $bodyTemplate = (string) $this->config->get(
            'order_emails.' . $status . '.body',
            "Hi {{customer_name}},\n\nYour order {{order_number}} is now {{status_label}}.\n\n{{note}}\n\n{{tracking_url}}"
        );
        $values = [
            '{{business_name}}' => (string) $this->config->get('business.name', 'Fat Bottom Grille'),
            '{{customer_name}}' => (string) $order['customer_name'],
            '{{order_number}}' => (string) $order['order_number'],
            '{{status_label}}' => order_status_label($status),
            '{{note}}' => $visibleNote,
            '{{rejection_reason}}' => $status === 'declined' ? $visibleNote : '',
            '{{tracking_url}}' => $this->trackingUrl($order),
        ];
        $subject = trim(strip_tags(strtr($subjectTemplate, $values)));

        $html = nl2br(strtr(htmlspecialchars($bodyTemplate, ENT_QUOTES, 'UTF-8'), [
            '{{business_name}}' => e($values['{{business_name}}']),
            '{{customer_name}}' => e($values['{{customer_name}}']),
            '{{order_number}}' => e($values['{{order_number}}']),
            '{{status_label}}' => e($values['{{status_label}}']),
            '{{note}}' => e($values['{{note}}']),
            '{{rejection_reason}}' => e($values['{{rejection_reason}}']),
            '{{tracking_url}}' => '<a href="' . e($values['{{tracking_url}}']) . '">View your order tracker</a>',
        ]));

        $this->mailer->send((string) $order['customer_email'], $subject, '<p>' . $html . '</p>');
    }

    public function trackingUrl(array $order): string
    {
        return rtrim((string) $this->config->get('app.url'), '/')
            . '/order/track?order=' . rawurlencode((string) $order['order_number'])
            . '&token=' . rawurlencode((string) $order['tracking_token']);
    }
}
