<?php

namespace App\Insurance\Service;

use App\Insurance\Entity\InsuranceCoverageDefault;
use App\Insurance\Enum\ServiceCategory;
use App\Insurance\Repository\InsuranceCoverageDefaultRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;

/**
 * Admin-managed coverage percentages. Every read returns a complete category set
 * (missing rows read as 0) so callers never have to know which categories exist.
 */
class InsuranceCoverageDefaultService
{
    public function __construct(
        private readonly InsuranceCoverageDefaultRepository $repo,
    ) {}

    /** @return array<string, float> service_category => percent, all categories present */
    public function percentMap(int $insuranceId): array
    {
        return $this->fill($this->repo->percentMapFor($insuranceId));
    }

    /**
     * @param list<int> $insuranceIds
     * @return array<int, array<string, float>>
     */
    public function percentMapForMany(array $insuranceIds): array
    {
        $stored = $this->repo->percentMapForMany($insuranceIds);

        $map = [];
        foreach ($insuranceIds as $id) {
            $map[$id] = $this->fill($stored[$id] ?? []);
        }

        return $map;
    }

    public function percentFor(int $insuranceId, ServiceCategory $category): ?float
    {
        return $this->repo->findOneFor($insuranceId, $category)?->getCoveragePercent();
    }

    /**
     * Category rows shaped for the admin settings UI.
     *
     * @return list<array{key: string, label: string, coverage_percent: float}>
     */
    public function settingsRows(int $insuranceId): array
    {
        $map = $this->percentMap($insuranceId);

        return array_map(static fn(ServiceCategory $c) => [
            'key'              => $c->value,
            'label'            => $c->label(),
            'coverage_percent' => $map[$c->value],
        ], ServiceCategory::cases());
    }

    /**
     * @param list<array{key?: string, coverage_percent?: mixed}> $rows
     * @throws AppException on an unknown category or an out-of-range percentage
     */
    public function save(int $insuranceId, array $rows): void
    {
        foreach ($rows as $row) {
            $category = ServiceCategory::tryFromValue(isset($row['key']) ? (string) $row['key'] : null);
            if ($category === null) {
                throw new AppException(
                    ErrorCodes::ERR_VALIDATION_001,
                    'نوع خدمت نامعتبر است: ' . implode('، ', ServiceCategory::values()),
                    422,
                );
            }

            $percent = (float) ($row['coverage_percent'] ?? 0);
            if ($percent < 0 || $percent > 100) {
                throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'درصد پوشش باید بین ۰ تا ۱۰۰ باشد', 422);
            }

            $entity = $this->repo->findOneFor($insuranceId, $category)
                ?? new InsuranceCoverageDefault($insuranceId, $category);

            $this->repo->save($entity->setCoveragePercent($percent), false);
        }

        $this->repo->flush();
    }

    /** @param array<string, float> $stored */
    private function fill(array $stored): array
    {
        $map = [];
        foreach (ServiceCategory::cases() as $category) {
            $map[$category->value] = $stored[$category->value] ?? 0.0;
        }

        return $map;
    }
}
