<?php

/**
 * JCOGS Image Pro - CpLicensePolicyService
 * ========================================
 * Calculates CP governance decisions from license status and runtime metadata.
 *
 * @category   ExpressionEngine Add-on
 * @package    JCOGS Image Pro
 * @author     JCOGS Design <contact@jcogs.net>
 * @copyright  Copyright (c) 2021 - 2026 JCOGS Design
 * @license    https://jcogs.net/add-ons/license/jcogs_img_pro
 * @version    2.0.6
 * @link       https://jcogs.net/
 * @since      2.0.0
 */

namespace JCOGSDesign\JCOGSImagePro\Service\License;

class CpLicensePolicyService implements CpLicensePolicyServiceInterface
{
    private CpLicenseStatusServiceInterface $statusService;
    private LicenseRuntimeRepositoryInterface $runtimeRepository;
    private int $noLicenseGraceDays;
    private int $expiryWarningDays;
    private int $expiryCriticalDays;

    public function __construct(
        CpLicenseStatusServiceInterface $statusService,
        LicenseRuntimeRepositoryInterface $runtimeRepository,
        int $noLicenseGraceDays = 7,
        int $expiryWarningDays = 30,
        int $expiryCriticalDays = 7
    ) {
        $this->statusService = $statusService;
        $this->runtimeRepository = $runtimeRepository;
        $this->noLicenseGraceDays = $noLicenseGraceDays;
        $this->expiryWarningDays = $expiryWarningDays;
        $this->expiryCriticalDays = $expiryCriticalDays;
    }

    public function getPolicyDecision(string $addonShortName, int $siteId, int $now): array
    {
        $rawStatus = $this->statusService->getRawStatus($addonShortName);
        $normalised = $this->statusService->getNormalisedStatus($addonShortName);

        $this->runtimeRepository->seedIfMissing($siteId, $addonShortName, $now);
        $runtime = $this->runtimeRepository->findForAddon($siteId, $addonShortName);

        $graceEndsAt = $runtime['grace_ends_at'] ?? null;
        $firstRestrictedAt = $runtime['first_restricted_at'] ?? null;

        if ($rawStatus === 'invalid' && $graceEndsAt === null) {
            $graceEndsAt = $now + ($this->noLicenseGraceDays * 86400);
        }

        $showInlineNotice = in_array($rawStatus, ['invalid', 'expired', 'trial'], true);
        $inlineSeverity = 'info';
        $canMutate = true;
        $readOnly = false;
        $daysRemaining = null;

        if ($rawStatus === 'invalid') {
            if ($graceEndsAt !== null) {
                $daysRemaining = (int) max(0, ceil(($graceEndsAt - $now) / 86400));
                $canMutate = $now <= $graceEndsAt;
                $readOnly = !$canMutate;
                $inlineSeverity = $canMutate ? 'warning' : 'error';
                if (!$canMutate && $firstRestrictedAt === null) {
                    $firstRestrictedAt = $now;
                }
            }
        } elseif ($rawStatus === 'expired') {
            $inlineSeverity = 'error';
            $canMutate = false;
            $readOnly = true;
            if ($firstRestrictedAt === null) {
                $firstRestrictedAt = $now;
            }
        } elseif ($rawStatus === 'trial') {
            $inlineSeverity = 'info';
        }

        $decision = [
            'raw_status' => $rawStatus,
            'normalised_status' => $normalised,
            'inline_severity' => $inlineSeverity,
            'show_inline_notice' => $showInlineNotice,
            'days_remaining' => $daysRemaining,
            'grace_ends_at' => $graceEndsAt,
            'can_mutate' => $canMutate,
            'read_only' => $readOnly,
            'dedupe_with_ee_invalid_banner' => ($rawStatus === 'invalid'),
            'expiry_warning_days' => $this->expiryWarningDays,
            'expiry_critical_days' => $this->expiryCriticalDays,
        ];

        $this->runtimeRepository->updateStatus(
            $siteId,
            $addonShortName,
            $rawStatus,
            $now,
            $graceEndsAt,
            $firstRestrictedAt !== null ? (int) $firstRestrictedAt : null
        );

        return $decision;
    }

    public function canMutate(string $addonShortName, int $siteId, int $now): bool
    {
        return (bool) $this->getPolicyDecision($addonShortName, $siteId, $now)['can_mutate'];
    }

    public function isReadOnly(string $addonShortName, int $siteId, int $now): bool
    {
        return (bool) $this->getPolicyDecision($addonShortName, $siteId, $now)['read_only'];
    }
}
