<?php

namespace App\Support;

use Carbon\Carbon;

/**
 * Mirrors inferSubscriptionInput from bugets-smart-money/src/lib/smart-form.ts
 *
 * @return array{
 *     categoryId?: string,
 *     billingCycle?: string,
 *     status?: string,
 *     shared?: bool,
 *     nextRenewal?: string,
 *     suggestions: list<array{id: string, label: string, detail?: string, tone?: string}>
 * }
 */
final class SubscriptionInference
{
    private static function isoToday(): string
    {
        return date('Y-m-d');
    }

    private static function addMonths(string $date, int $months): string
    {
        return Carbon::parse($date)->addMonths($months)->format('Y-m-d');
    }

    private static function addDays(string $date, int $days): string
    {
        return Carbon::parse($date)->addDays($days)->format('Y-m-d');
    }

    /**
     * @return array<string, mixed>
     */
    public static function infer(string $name, array $history = []): array
    {
        $input = mb_strtolower(trim($name));
        $suggestions = [];
        $result = ['suggestions' => $suggestions, 'status' => 'active'];

        $detected = CategoryDetector::detect($name);
        if ($detected !== null && $detected !== 'income') {
            $cat = CategoryCatalog::get($detected);
            $result['categoryId'] = $detected;
            $suggestions[] = [
                'id' => 'category',
                'label' => (string) __('inference.subscription.assigned_category', ['category' => $cat['name']]),
                'tone' => 'positive',
            ];
        }

        $subscriptionCatalog = [
            ['match' => ['netflix', 'spotify', 'icloud', 'adobe', 'apple music', 'disney', 'youtube premium', 'notion', 'chatgpt'], 'billingCycle' => 'monthly'],
            ['match' => ['amazon prime annual', 'prime annual'], 'billingCycle' => 'yearly'],
            ['match' => ['vpn', 'dropbox', 'figma'], 'billingCycle' => 'monthly'],
        ];

        foreach ($subscriptionCatalog as $rule) {
            $hit = false;
            foreach ($rule['match'] as $term) {
                if ($term !== '' && str_contains($input, $term)) {
                    $hit = true;
                    break;
                }
            }
            if (! $hit) {
                continue;
            }
            $cycle = $rule['billingCycle'];
            $result['billingCycle'] = $cycle;
            $result['nextRenewal'] = $cycle === 'yearly'
                ? self::addMonths(self::isoToday(), 12)
                : self::addMonths(self::isoToday(), 1);
            break;
        }

        if (str_contains($input, 'family') || str_contains($input, 'shared')) {
            $result['shared'] = true;
            $suggestions[] = [
                'id' => 'shared',
                'label' => (string) __('inference.subscription.shared_label'),
                'detail' => (string) __('inference.subscription.shared_detail'),
                'tone' => 'positive',
            ];
        }

        if (str_contains($input, 'trial')) {
            $result['status'] = 'trial';
            $result['nextRenewal'] = self::addDays(self::isoToday(), 14);
            $suggestions[] = [
                'id' => 'trial',
                'label' => (string) __('inference.subscription.trial_label'),
                'detail' => (string) __('inference.subscription.trial_detail'),
                'tone' => 'warning',
            ];
        }

        if (str_contains($input, 'adobe') || str_contains($input, 'vpn')) {
            $suggestions[] = [
                'id' => 'review',
                'label' => (string) __('inference.subscription.review_label'),
                'detail' => (string) __('inference.subscription.review_detail'),
                'tone' => 'warning',
            ];
        }

        // Predict from similar existing subscription merchants.
        $nameTokens = array_values(array_filter(preg_split('/[^a-z0-9]+/i', $input ?: '')));
        $bestScore = 0;
        $bestRow = null;
        foreach ($history as $row) {
            if (! is_array($row)) {
                continue;
            }
            $rowName = mb_strtolower((string) ($row['name'] ?? ''));
            if ($rowName === '') {
                continue;
            }
            $score = 0;
            foreach ($nameTokens as $token) {
                if (mb_strlen($token) < 3) {
                    continue;
                }
                if (str_contains($rowName, $token)) {
                    $score++;
                }
            }
            if ($score > $bestScore) {
                $bestScore = $score;
                $bestRow = $row;
            }
        }
        if ($bestRow !== null && $bestScore > 0) {
            if (empty($result['billingCycle']) && ! empty($bestRow['billingCycle'])) {
                $result['billingCycle'] = (string) $bestRow['billingCycle'];
            }
            if (empty($result['categoryId']) && ! empty($bestRow['categoryId'])) {
                $result['categoryId'] = (string) $bestRow['categoryId'];
            }
            if (! isset($result['shared']) && isset($bestRow['shared'])) {
                $result['shared'] = (bool) $bestRow['shared'];
            }
            $suggestions[] = [
                'id' => 'history-sub',
                'label' => (string) __('inference.subscription.history_sub_label'),
                'detail' => (string) __('inference.subscription.history_sub_detail'),
                'tone' => 'positive',
            ];
        }

        $result['suggestions'] = $suggestions;

        return $result;
    }
}
