<?php

declare(strict_types=1);

namespace ApiModel\Service;

use ApiModel\Domain\Repository\TaxRateRepositoryInterface;
use ApiModel\Infrastructure\Persistence\Database;
use ApiModel\Shared\Exceptions\BadRequestException;
use ApiModel\Shared\Exceptions\ConflictException;
use ApiModel\Shared\Exceptions\NotFoundException;

final class TaxRateService
{
    private const TAX_TYPES = ['vat', 'withholding', 'exempt', 'other'];

    public function __construct(private readonly TaxRateRepositoryInterface $taxRates) {}

    /** @return list<array<string, mixed>> */
    public function listTaxRates(string $companyId): array
    {
        return array_map($this->mapTaxRate(...), $this->taxRates->listByCompany($companyId));
    }

    /** @return array<string, mixed> */
    public function getTaxRate(string $companyId, string $id): array
    {
        $row = $this->taxRates->findById($id);
        if ($row === null || (string) $row['company_id'] !== $companyId) {
            throw new NotFoundException('Tasa de impuesto no encontrada');
        }

        return $this->mapTaxRate($row);
    }

    /** @param array<string, mixed> $data @return array<string, mixed> */
    public function createTaxRate(string $companyId, array $data): array
    {
        $name = trim((string) ($data['name'] ?? ''));
        $rate = (float) ($data['rate'] ?? 0.0);
        $type = strtolower(trim((string) ($data['type'] ?? 'vat')));

        $this->validateTaxFields($name, $rate, $type);

        if ($this->taxRates->findByName($companyId, $name) !== null) {
            throw new ConflictException('Ya existe una tasa de impuesto con ese nombre');
        }

        $id = Database::uuid();
        $this->taxRates->insert($id, $companyId, $name, $rate, $type);

        $row = $this->taxRates->findById($id);

        return $this->mapTaxRate($row ?? []);
    }

    /** @param array<string, mixed> $data @return array<string, mixed> */
    public function updateTaxRate(string $companyId, string $id, array $data): array
    {
        $row = $this->taxRates->findById($id);
        if ($row === null || (string) $row['company_id'] !== $companyId) {
            throw new NotFoundException('Tasa de impuesto no encontrada');
        }

        $name = trim((string) ($data['name'] ?? $row['name']));
        $rate = isset($data['rate']) ? (float) $data['rate'] : (float) $row['rate'];
        $type = strtolower(trim((string) ($data['type'] ?? $row['type'])));
        $isActive = (bool) ($data['is_active'] ?? $row['is_active']);

        $this->validateTaxFields($name, $rate, $type);

        $existing = $this->taxRates->findByName($companyId, $name);
        if ($existing !== null && (string) $existing['id'] !== $id) {
            throw new ConflictException('Ya existe otra tasa de impuesto con ese nombre');
        }

        $this->taxRates->update($id, $name, $rate, $type, $isActive);

        return $this->mapTaxRate($this->taxRates->findById($id) ?? []);
    }

    public function deleteTaxRate(string $companyId, string $id): void
    {
        $row = $this->taxRates->findById($id);
        if ($row === null || (string) $row['company_id'] !== $companyId) {
            throw new NotFoundException('Tasa de impuesto no encontrada');
        }

        $this->taxRates->delete($id);
    }

    /** @param array<string, mixed> $row @return array<string, mixed> */
    private function mapTaxRate(array $row): array
    {
        return [
            'id' => $row['id'] ?? null,
            'company_id' => $row['company_id'] ?? null,
            'name' => $row['name'] ?? '',
            'rate' => (float) ($row['rate'] ?? 0.0),
            'type' => $row['type'] ?? '',
            'is_active' => (bool) ($row['is_active'] ?? true),
        ];
    }

    private function validateTaxFields(string $name, float $rate, string $type): void
    {
        if ($name === '') {
            throw new BadRequestException('El nombre es obligatorio');
        }
        if ($rate < 0) {
            throw new BadRequestException('La tasa de impuesto no puede ser negativa');
        }
        if (! in_array($type, self::TAX_TYPES, true)) {
            throw new BadRequestException('Tipo de impuesto inválido');
        }
    }
}
