<?php

namespace App\Subscription\Controller;

use App\Auth\Entity\User;
use App\Auth\Repository\UserActiveContextRepository;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\SubscriptionPeriod;
use App\Subscription\Entity\SubscriptionPlan;
use App\Subscription\Repository\SubscriptionPlanRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Service\SubscriptionService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'Subscription')]
class SubscriptionController extends BaseController
{
    public function __construct(
        private readonly SubscriptionService           $subscriptionService,
        private readonly SubscriptionPlanRepository    $planRepo,
        private readonly SubscriptionPeriodRepository  $periodRepo,
        private readonly ClinicSubscriptionRepository  $subscriptionRepo,
        private readonly DoctorRepository              $doctorRepo,
        private readonly ClinicRepository              $clinicRepo,
        private readonly UserActiveContextRepository   $contextRepo,
        private readonly EntityManagerInterface        $em,
    ) {}

    // ── Public ──────────────────────────────────────────────────────────────

    #[Route('/api/v1/subscription/plans', methods: ['GET'])]
    public function plans(): JsonResponse
    {
        $plans = $this->planRepo->findAllActive();

        return $this->success(array_map(
            fn(SubscriptionPlan $p) => $p->toArray(withPeriods: true),
            $plans
        ));
    }

    // ── Authenticated ────────────────────────────────────────────────────────

    #[Route('/api/v1/subscription/my', methods: ['GET'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function my(#[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $subscription  = $this->subscriptionService->getActiveSubscription($entityType, $entityId);
        $usedTrial     = $this->subscriptionService->hasUsedTrial($entityType, $entityId);
        $effectivePlan = $this->subscriptionService->getEffectivePlan($entityType, $entityId);

        return $this->success([
            'subscription'   => $subscription?->toArray(),
            'used_trial'     => $usedTrial,
            'effective_plan' => $effectivePlan?->toArray(),
        ]);
    }

    #[Route('/api/v1/subscription/trial', methods: ['POST'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function trial(#[CurrentUser] User $user): JsonResponse
    {
        [$entityType, $entityId] = $this->resolveEntity($user);
        if ($entityId === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        try {
            $subscription = $this->subscriptionService->activateTrial($entityType, $entityId);
        } catch (AppException $e) {
            return $this->error($e->getErrorCode(), $e->getMessage(), $e->getHttpStatus());
        }

        return $this->success($subscription->toArray(), 201);
    }

    // ── Admin ────────────────────────────────────────────────────────────────

    #[Route('/api/v1/admin/subscription/plans', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminPlans(): JsonResponse
    {
        $plans = $this->planRepo->findAllForAdmin();

        return $this->paginated(
            array_map(fn(SubscriptionPlan $p) => $p->toArray(withPeriods: true), $plans),
            count($plans),
            1,
            100
        );
    }

    #[Route('/api/v1/admin/subscription/plan', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminCreatePlan(Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true) ?? [];

        $name = trim($data['name'] ?? '');
        if ($name === '' || !isset($data['level'])) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'name و level الزامی هستند', 422);
        }

        if ($this->planRepo->findByName($name) !== null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'پلنی با این نام از قبل وجود دارد', 422);
        }

        $plan = new SubscriptionPlan(
            $name,
            (int) $data['level'],
            (int) ($data['max_secretaries'] ?? 1),
            $data['features'] ?? []
        );

        $this->planRepo->save($plan);

        return $this->success($plan->toArray(), 201);
    }

    #[Route('/api/v1/admin/subscription/plan/{uuid}', methods: ['PATCH'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminUpdatePlan(string $uuid, Request $request): JsonResponse
    {
        $plan = $this->planRepo->findByUuid($uuid);
        if ($plan === null) {
            return $this->error(ErrorCodes::ERR_SUBSCRIPTION_NOT_FOUND, ErrorCodes::message(ErrorCodes::ERR_SUBSCRIPTION_NOT_FOUND), 404);
        }

        $data = json_decode($request->getContent(), true) ?? [];

        if (isset($data['name'])) {
            $existing = $this->planRepo->findByName(trim($data['name']));
            if ($existing !== null && $existing->getUuid() !== $plan->getUuid()) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'پلنی با این نام از قبل وجود دارد', 422);
            }
            $plan->setName($data['name']);
        }
        if (isset($data['level']))           { $plan->setLevel((int) $data['level']); }
        if (isset($data['max_secretaries'])) { $plan->setMaxSecretaries((int) $data['max_secretaries']); }
        if (isset($data['features']))        { $plan->setFeatures($data['features']); }
        if (isset($data['active']))          { $plan->setActive((bool) $data['active']); }

        $this->planRepo->save($plan);

        return $this->success($plan->toArray());
    }

    #[Route('/api/v1/admin/subscription/period', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminCreatePeriod(Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true) ?? [];

        $planUuid = $data['plan_uuid'] ?? '';
        $plan     = $this->planRepo->findByUuid($planUuid);
        if ($plan === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پنل یافت نشد', 404);
        }

        if (empty($data['label']) || !isset($data['duration_months'])) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'label و duration_months الزامی هستند', 422);
        }

        $period = new SubscriptionPeriod(
            $plan,
            $data['label'],
            (int) $data['duration_months'],
            (int) ($data['price_rials'] ?? 0),
            (bool) ($data['is_trial'] ?? false)
        );
        $period->setSortOrder((int) ($data['sort_order'] ?? 0));

        $this->periodRepo->save($period);

        return $this->success($period->toArray(), 201);
    }

    #[Route('/api/v1/admin/subscription/period/{uuid}', methods: ['PATCH'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminUpdatePeriod(string $uuid, Request $request): JsonResponse
    {
        $period = $this->periodRepo->findByUuid($uuid);
        if ($period === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'دوره یافت نشد', 404);
        }

        $data = json_decode($request->getContent(), true) ?? [];

        if (isset($data['label']))           { $period->setLabel($data['label']); }
        if (isset($data['duration_months'])) { $period->setDurationMonths((int) $data['duration_months']); }
        if (isset($data['price_rials']))     { $period->setPriceRials((int) $data['price_rials']); }
        if (isset($data['active']))          { $period->setActive((bool) $data['active']); }
        if (isset($data['sort_order']))      { $period->setSortOrder((int) $data['sort_order']); }

        $this->periodRepo->save($period);

        return $this->success($period->toArray());
    }

    #[Route('/api/v1/admin/subscription/period/{uuid}', methods: ['DELETE'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminDeletePeriod(string $uuid): JsonResponse
    {
        $period = $this->periodRepo->findByUuid($uuid);
        if ($period === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'دوره یافت نشد', 404);
        }

        $period->setActive(false);
        $this->periodRepo->save($period);

        return $this->success(['message' => 'دوره غیرفعال شد']);
    }

    #[Route('/api/v1/admin/subscription/report', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function adminReport(Request $request): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(100, max(10, (int) $request->query->get('limit', 20)));

        $total = (int) $this->em->createQuery('SELECT COUNT(s.id) FROM App\Subscription\Entity\ClinicSubscription s')
            ->getSingleScalarResult();

        $subscriptions = $this->em->createQuery('
            SELECT s.uuid, s.entityType, s.entityId, s.isTrial, s.startsAt, s.expiresAt, s.createdAt,
                   p.name AS plan_name, p.level AS plan_level
            FROM App\Subscription\Entity\ClinicSubscription s
            JOIN s.plan p
            ORDER BY s.id DESC
        ')
            ->setFirstResult(($page - 1) * $limit)
            ->setMaxResults($limit)
            ->getArrayResult();

        return $this->paginated($subscriptions, $total, $page, $limit);
    }

    // ── Helpers ─────────────────────────────────────────────────────────────

    private function resolveEntity(User $user): array
    {
        if ($user->hasRole('ROLE_DOCTOR')) {
            $doctor = $this->doctorRepo->findByUser($user);
            return $doctor !== null ? ['doctor', $doctor->getId()] : ['doctor', null];
        }

        if ($user->hasRole('ROLE_CLINIC')) {
            $clinic = $this->clinicRepo->findByUser($user);
            return $clinic !== null ? ['clinic', $clinic->getId()] : ['clinic', null];
        }

        if ($user->hasRole('ROLE_SECRETARY')) {
            $dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
            if ($dbUuid !== null) {
                $clinic = $this->clinicRepo->findByUuid($dbUuid);
                if ($clinic !== null) {
                    return ['clinic', $clinic->getId()];
                }
                $doctor = $this->doctorRepo->findByUuid($dbUuid);
                if ($doctor !== null) {
                    return ['doctor', $doctor->getId()];
                }
            }
        }

        return ['unknown', null];
    }
}
