<?php

declare(strict_types=1);

namespace Budget;

use DateTimeImmutable;
use PDO;

final class ReminderService
{
    public function __construct(private PDO $pdo, private Settings $settings, private Mailgun $mailgun)
    {
    }

    public function run(bool $force = false): array
    {
        if (!(bool) $this->settings->get('modules.alerts', true)) {
            return ['sent' => 0, 'skipped' => 0, 'errors' => ['Alerts module is disabled.']];
        }
        $times = (array) $this->settings->get('alerts.times', ['09:00']);
        if (!$force && !in_array(date('H:i'), $times, true)) {
            return ['sent' => 0, 'skipped' => 0, 'errors' => []];
        }
        $recipient = (string) $this->settings->get('alerts.email', '');
        $windows = array_map('intval', (array) $this->settings->get('alerts.windows', [7, 3, 1]));
        $today = new DateTimeImmutable('today');
        $sent = 0;
        $skipped = 0;
        $errors = [];

        foreach ($windows as $window) {
            $target = $today->modify('+' . max(0, $window) . ' days')->format('Y-m-d');
            $statement = $this->pdo->prepare(
                'SELECT bi.*, c.name AS category_name
                 FROM budget_items bi
                 LEFT JOIN categories c ON c.id = bi.category_id
                 WHERE bi.due_date = :due_date AND bi.status = :status'
            );
            $statement->execute(['due_date' => $target, 'status' => 'active']);
            foreach ($statement->fetchAll() as $item) {
                if ($this->alreadyLogged((int) $item['id'], $today->format('Y-m-d'), $window, $recipient)) {
                    $skipped++;
                    continue;
                }
                try {
                    $subject = sprintf('%s due in %d day%s', $item['name'], $window, $window === 1 ? '' : 's');
                    $html = sprintf(
                        '<h1>%s</h1><p>Your %s budget item is due on <strong>%s</strong>.</p><p>Planned amount: <strong>%s%.2f</strong></p>',
                        htmlspecialchars($subject, ENT_QUOTES),
                        htmlspecialchars((string) ($item['category_name'] ?: 'uncategorized'), ENT_QUOTES),
                        htmlspecialchars((string) $item['due_date'], ENT_QUOTES),
                        htmlspecialchars((string) $this->settings->get('app.currency_symbol', '$'), ENT_QUOTES),
                        (float) $item['planned_amount']
                    );
                    $response = $this->mailgun->send($recipient, $subject, $html);
                    $this->log((int) $item['id'], $today->format('Y-m-d'), $window, $recipient, 'sent', (string) $response['body']);
                    $sent++;
                } catch (\Throwable $exception) {
                    $this->log((int) $item['id'], $today->format('Y-m-d'), $window, $recipient, 'failed', $exception->getMessage());
                    $errors[] = $item['name'] . ': ' . $exception->getMessage();
                }
            }
        }
        return ['sent' => $sent, 'skipped' => $skipped, 'errors' => $errors];
    }

    private function alreadyLogged(int $itemId, string $date, int $window, string $recipient): bool
    {
        $statement = $this->pdo->prepare(
            'SELECT COUNT(*) FROM alert_logs
             WHERE budget_item_id = :item_id AND alert_date = :alert_date
               AND window_days = :window_days AND recipient = :recipient'
        );
        $statement->execute([
            'item_id' => $itemId,
            'alert_date' => $date,
            'window_days' => $window,
            'recipient' => $recipient,
        ]);
        return (int) $statement->fetchColumn() > 0;
    }

    private function log(int $itemId, string $date, int $window, string $recipient, string $status, string $response): void
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO alert_logs (budget_item_id, alert_date, window_days, recipient, status, response)
             VALUES (:item_id, :alert_date, :window_days, :recipient, :status, :response)'
        );
        $statement->execute([
            'item_id' => $itemId,
            'alert_date' => $date,
            'window_days' => $window,
            'recipient' => $recipient,
            'status' => $status,
            'response' => $response,
        ]);
    }
}
