<?php

namespace App\Services\Whmcs;

use App\Models\WhmcsPackage;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

/**
 * Syncs the full WHMCS product catalogue into local whmcs_packages.
 * Idempotent on whmcs_pid — re-running upserts in place. Catalogue
 * doesn't change often (admin adds a new plan once a month?), so the
 * scheduler can run this nightly; ad-hoc re-sync also exposed via the
 * Settings UI.
 */
class WhmcsPackageSyncer
{
    public function __construct(private WhmcsClient $whmcs) {}

    public function sync(): array
    {
        $stats = ['fetched' => 0, 'upserted' => 0, 'errors' => []];

        $r = $this->whmcs->call('GetProducts');
        if (! $r) {
            $stats['errors'][] = 'GetProducts returned null';
            return $stats;
        }
        $products = $r['products']['product'] ?? [];
        $stats['fetched'] = count($products);

        foreach ($products as $p) {
            $pid = (int) ($p['pid'] ?? 0);
            if (! $pid) continue;
            try {
                $name      = mb_substr((string) ($p['name'] ?? ''), 0, 128, 'UTF-8');
                $module    = mb_substr((string) ($p['module'] ?? ''), 0, 32, 'UTF-8');
                $type      = mb_substr((string) ($p['type'] ?? ''), 0, 32, 'UTF-8');
                $groupName = mb_substr((string) ($p['group']['name'] ?? ''), 0, 128, 'UTF-8') ?: null;
                $productUrl= mb_substr((string) ($p['product_url'] ?? ''), 0, 500, 'UTF-8') ?: null;
                $brand     = $this->classifyBrand($name, $groupName, $module, $type, $productUrl);
                [$renewalBdt, $cycle] = $this->extractRenewalFee($p['pricing'] ?? null);

                WhmcsPackage::updateOrCreate(
                    ['whmcs_pid' => $pid],
                    [
                        'name'           => $name,
                        'slug'           => mb_substr((string) ($p['slug'] ?? ''), 0, 128, 'UTF-8'),
                        'module'         => $module,
                        'product_type'   => $type,
                        'group_id'       => isset($p['gid']) ? (int) $p['gid'] : null,
                        'group_name'     => $groupName,
                        'hosting_brand'  => $brand,
                        'product_url'    => $productUrl,
                        'description'    => $this->cleanDescription((string) ($p['description'] ?? '')),
                        'pricing'        => $p['pricing']      ?? null,
                        'renewal_fee_bdt'=> $renewalBdt,
                        'billing_cycle'  => $cycle,
                        'custom_fields'  => $p['customfields'] ?? null,
                        'config_options' => $p['configoptions'] ?? null,
                        'enabled'        => true,
                        'last_synced_at' => now(),
                    ],
                );
                $stats['upserted']++;
            } catch (\Throwable $e) {
                $stats['errors'][] = "pid={$pid}: " . $e->getMessage();
                Log::warning('hsap.packages.sync_error', [
                    'pid' => $pid, 'err' => $e->getMessage(),
                ]);
            }
        }

        // Bust the per-process catalogue cache so the next ticket draft
        // picks up freshly-synced features without a service restart.
        Cache::forget('hsap.whmcs.packages_by_pid');

        return $stats;
    }

    /**
     * Auto-classify a product into one of WhmcsPackage::HOSTING_BRANDS.
     * Priority order (first match wins):
     *   1. product_type == 'domainregister' → 'domain'
     *   2. product_type == 'server'         → 'dedicated'
     *   3. module hints (windowsvps + plesk → 'windows_plesk', webuzo → 'mern_webuzo')
     *   4. group_name keyword scan (BDIX / USA / Premium / Budget / Reseller)
     *   5. name keyword scan as final fallback
     *   6. Default to null (admin can fill in manually later)
     */
    private function classifyBrand(string $name, ?string $group, string $module, string $type, ?string $url): ?string
    {
        $needle = strtolower($name . ' ' . ($group ?? '') . ' ' . ($url ?? ''));
        $m = strtolower($module);
        $t = strtolower($type);

        // Type-based — most reliable signals.
        if ($t === 'domainregister' || str_contains($needle, 'domain')) return 'domain';
        if ($t === 'server')        return 'dedicated';
        if ($t === 'addon')         return 'addon';
        if (str_contains($needle, 'reseller') || str_contains($needle, ' whm')) return 'reseller';
        if (str_contains($m, 'virtualizor') || str_contains($needle, ' vps')) return 'vps';
        if (str_contains($m, 'plesk')  || str_contains($needle, 'windows shared')) return 'windows_plesk';
        if (str_contains($m, 'webuzo') || str_contains($needle, 'mern')) return 'mern_webuzo';

        // cPanel — narrow by location + tier keywords.
        $isBdix    = str_contains($needle, 'bdix');
        $isUsa     = str_contains($needle, 'usa') || str_contains($needle, 'us ');
        $isPremium = str_contains($needle, 'premium');
        $isBudget  = str_contains($needle, 'budget') || str_contains($needle, 'starter');

        if ($isBdix && $isPremium) return 'premium_bdix';
        if ($isBdix)               return 'budget_bdix';
        if ($isUsa  && $isPremium) return 'usa_premium';
        if ($isUsa)                return 'usa_budget';
        // Plain "Premium Hosting" / "Budget Hosting" with no BD/USA hint —
        // default to BDIX since most HostSeba customers are BD.
        if ($isPremium)            return 'premium_bdix';
        if ($isBudget)             return 'budget_bdix';

        return null;
    }

    /**
     * Extract the renewal price + billing cycle from WHMCS's pricing
     * JSON. WHMCS structures pricing per-currency, per-cycle. We prefer
     * BDT and pick the LONGEST cycle that's > 0 (annual usually) since
     * that's the "renewal" the customer sees most often.
     *
     * @return array{0: ?float, 1: ?string}
     */
    private function extractRenewalFee(?array $pricing): array
    {
        if (! is_array($pricing)) return [null, null];

        // WHMCS pricing shape: { "USD": { "monthly": "9.95", "annually": ... }, "BDT": { ... } }
        $bdt = $pricing['BDT'] ?? null;
        if (! is_array($bdt)) return [null, null];

        // Preferred cycles in priority order — annual first, then quarterly, etc.
        $cycles = [
            'annually'      => 'year',
            'semiannually'  => '6 months',
            'quarterly'     => '3 months',
            'monthly'       => 'month',
            'biennially'    => '2 years',
            'triennially'   => '3 years',
        ];
        foreach ($cycles as $key => $label) {
            $v = $bdt[$key] ?? null;
            if ($v !== null && $v !== '' && (float) $v > 0) {
                return [(float) $v, $label];
            }
        }
        return [null, null];
    }

    /**
     * Strip the HTML tags WHMCS sometimes wraps around the description
     * (admins can edit it in the WHMCS admin's WYSIWYG). Keep newlines
     * for the per-feature line breaks.
     */
    private function cleanDescription(string $s): string
    {
        $s = str_replace(['<br>', '<br/>', '<br />'], "\n", $s);
        $s = strip_tags($s);
        $s = html_entity_decode($s, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $s = preg_replace("/\r\n|\r/", "\n", $s);
        $s = preg_replace("/\n{3,}/", "\n\n", $s);
        return trim($s);
    }
}
