<?php

namespace App\Tests\Subscription;

use App\Config\Repository\SiteConfigRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\SubscriptionPlan;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\SubscriptionPlanRepository;
use App\Subscription\Service\SubscriptionService;
use PHPUnit\Framework\TestCase;

/**
 * A `basic` plan whose trial period has been deactivated is a normal admin
 * configuration. It used to surface as ERR_NOT_FOUND_001/500; it must now be
 * the same ERR_TRIAL_DISABLED/422 the trial_enabled flag produces.
 */
class ActivateTrialTest extends TestCase
{
    private function service(
        bool  $usedTrial,
        ?string $trialEnabled,
        ?SubscriptionPlan $basicPlan,
        mixed $trialPeriod,
    ): SubscriptionService {
        $subscriptionRepo = $this->createMock(ClinicSubscriptionRepository::class);
        $subscriptionRepo->method('hasUsedTrial')->willReturn($usedTrial);

        $planRepo = $this->createMock(SubscriptionPlanRepository::class);
        $planRepo->method('findByName')->willReturn($basicPlan);

        $periodRepo = $this->createMock(SubscriptionPeriodRepository::class);
        $periodRepo->method('findTrialPeriodForPlan')->willReturn($trialPeriod);

        $configRepo = $this->createMock(SiteConfigRepository::class);
        $configRepo->method('get')->willReturn($trialEnabled);

        return new SubscriptionService($subscriptionRepo, $planRepo, $periodRepo, $configRepo);
    }

    public function testNoActiveTrialPeriodIsReportedAsTrialDisabled(): void
    {
        $service = $this->service(
            usedTrial: false,
            trialEnabled: '1',
            basicPlan: $this->createMock(SubscriptionPlan::class),
            trialPeriod: null,
        );

        try {
            $service->activateTrial('doctor', 1);
            $this->fail('expected AppException');
        } catch (AppException $e) {
            $this->assertSame(ErrorCodes::ERR_TRIAL_DISABLED, $e->getErrorCode());
            $this->assertSame(422, $e->getHttpStatus());
        }
    }

    public function testMissingBasicPlanStaysAServerError(): void
    {
        $service = $this->service(
            usedTrial: false,
            trialEnabled: '1',
            basicPlan: null,
            trialPeriod: null,
        );

        try {
            $service->activateTrial('doctor', 1);
            $this->fail('expected AppException');
        } catch (AppException $e) {
            $this->assertSame(ErrorCodes::ERR_NOT_FOUND_001, $e->getErrorCode());
            $this->assertSame(500, $e->getHttpStatus());
        }
    }

    public function testAlreadyUsedTrialTakesPrecedence(): void
    {
        $service = $this->service(
            usedTrial: true,
            trialEnabled: '1',
            basicPlan: null,
            trialPeriod: null,
        );

        try {
            $service->activateTrial('doctor', 1);
            $this->fail('expected AppException');
        } catch (AppException $e) {
            $this->assertSame(ErrorCodes::ERR_TRIAL_ALREADY_USED, $e->getErrorCode());
            $this->assertSame(422, $e->getHttpStatus());
        }
    }

    public function testDisabledFlagTakesPrecedenceOverMissingPlan(): void
    {
        $service = $this->service(
            usedTrial: false,
            trialEnabled: '0',
            basicPlan: null,
            trialPeriod: null,
        );

        try {
            $service->activateTrial('doctor', 1);
            $this->fail('expected AppException');
        } catch (AppException $e) {
            $this->assertSame(ErrorCodes::ERR_TRIAL_DISABLED, $e->getErrorCode());
            $this->assertSame(422, $e->getHttpStatus());
        }
    }
}
