<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\SettingsRepository;
use InvalidArgumentException;

final class SettingsService
{
    private const DEFAULTS = [
        'business.name' => ['value' => 'Grow Tool Therapies', 'type' => 'string'],
        'business.tagline' => ['value' => 'Occupational Therapy Services', 'type' => 'string'],
        'business.email' => ['value' => '', 'type' => 'string'],
        'business.phone' => ['value' => '', 'type' => 'string'],
        'business.address' => ['value' => '', 'type' => 'string'],
        'business.website' => ['value' => 'www.growtooltherapies.org', 'type' => 'string'],
        'business.logo' => ['value' => '', 'type' => 'string'],
        'business.country' => ['value' => 'Kenya', 'type' => 'string'],
        'regional.currency_code' => ['value' => 'KES', 'type' => 'string'],
        'regional.currency_symbol' => ['value' => 'KSh', 'type' => 'string'],
        'regional.timezone' => ['value' => 'Africa/Nairobi', 'type' => 'string'],
        'regional.language' => ['value' => 'English', 'type' => 'string'],
        'regional.date_format' => ['value' => 'DD/MM/YYYY', 'type' => 'string'],
        'regional.time_format' => ['value' => '12-hour', 'type' => 'string'],
        'regional.decimal_places' => ['value' => '2', 'type' => 'integer'],
        'tax.enabled' => ['value' => '0', 'type' => 'boolean'],
        'tax.name' => ['value' => 'VAT', 'type' => 'string'],
        'tax.rate' => ['value' => '0', 'type' => 'decimal'],
        'numbering.patient.prefix' => ['value' => 'PT-', 'type' => 'string'],
        'numbering.assessment.prefix' => ['value' => 'ASS-', 'type' => 'string'],
        'numbering.report.prefix' => ['value' => 'RPT-', 'type' => 'string'],
        'numbering.invoice.prefix' => ['value' => 'INV-', 'type' => 'string'],
        'numbering.receipt.prefix' => ['value' => 'RCT-', 'type' => 'string'],
        'numbering.quotation.prefix' => ['value' => 'QT-', 'type' => 'string'],
        'mail.smtp.host' => ['value' => '', 'type' => 'string'],
        'mail.smtp.port' => ['value' => '587', 'type' => 'integer'],
        'mail.smtp.username' => ['value' => '', 'type' => 'string'],
        'mail.smtp.password' => ['value' => '', 'type' => 'string', 'sensitive' => true],
        'mail.smtp.encryption' => ['value' => 'tls', 'type' => 'string'],
        'mail.from.address' => ['value' => '', 'type' => 'string'],
        'mail.from.name' => ['value' => 'Grow Tool Therapies', 'type' => 'string'],
    ];

    public function __construct(private ?SettingsRepository $repository = null)
    {
        $this->repository ??= new SettingsRepository();
    }

    public function get(): array
    {
        $this->repository->ensureDefaults(self::DEFAULTS);
        return $this->repository->all();
    }

    public function saveLogo(array $file, int $actorId): void
    {
        if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            throw new InvalidArgumentException('Please choose a PNG or JPG logo to upload.');
        }
        $size = (int) ($file['size'] ?? 0);
        if ($size < 1 || $size > 3 * 1024 * 1024) {
            throw new InvalidArgumentException('Logo must be smaller than 3 MB.');
        }
        $tmp = (string) ($file['tmp_name'] ?? '');
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
        $mime = $finfo->file($tmp);
        $allowed = ['image/png' => 'png', 'image/jpeg' => 'jpg'];
        if (!isset($allowed[$mime])) {
            throw new InvalidArgumentException('Logo must be a valid PNG or JPG image.');
        }
        $root = dirname(__DIR__, 2);
        $dir = $root . '/storage/branding';
        if (!is_dir($dir) && !mkdir($dir, 0750, true) && !is_dir($dir)) {
            throw new InvalidArgumentException('Unable to create the branding storage directory.');
        }
        $filename = 'logo.' . $allowed[$mime];
        $destination = $dir . '/' . $filename;
        if (!move_uploaded_file($tmp, $destination)) {
            throw new InvalidArgumentException('Unable to save the uploaded logo.');
        }
        @chmod($destination, 0640);
        $this->repository->updateMany(['business.logo' => $filename]);
        try { $this->repository->audit($actorId, 'settings.logo.upload', ['filename' => $filename, 'mime' => $mime, 'size' => $size]); } catch (\Throwable) {}
    }

    public function save(array $input, int $actorId): void
    {
        $settings = $this->get();
        $allowed = array_keys(self::DEFAULTS);
        $values = [];
        foreach ($allowed as $key) {
            if (!array_key_exists($key, $input)) continue;
            $value = trim((string) $input[$key]);
            $type = $settings[$key]['type'] ?? self::DEFAULTS[$key]['type'];
            if ($type === 'integer') {
                if (filter_var($value, FILTER_VALIDATE_INT) === false || (int) $value < 0) throw new InvalidArgumentException('Decimal places must be a non-negative whole number.');
                $value = (string) (int) $value;
            }
            if ($type === 'decimal') {
                if (!is_numeric($value) || (float) $value < 0 || (float) $value > 100) throw new InvalidArgumentException('Tax rate must be between 0 and 100.');
                $value = number_format((float) $value, 2, '.', '');
            }
            if ($type === 'boolean') $value = in_array(strtolower($value), ['1', 'true', 'on', 'yes'], true) ? '1' : '0';
            if ($key === 'business.email' && $value !== '' && !filter_var($value, FILTER_VALIDATE_EMAIL)) throw new InvalidArgumentException('Enter a valid business email address.');
            if ($key === 'business.website' && $value !== '') {
                $display = preg_replace('#^https?://#i', '', $value) ?? $value;
                $display = rtrim(trim($display), '/');
                if ($display === '' || !preg_match('/^(?:www\.)?[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::\d+)?(?:/.*)?$/i', $display)) {
                    throw new InvalidArgumentException('Enter a valid business website, for example www.growtooltherapies.org.');
                }
                $value = $display;
            }
            $values[$key] = $value;
        }
        $this->repository->updateMany($values);
        try { $this->repository->audit($actorId, 'settings.update', ['keys' => array_keys($values)]); } catch (\Throwable) {}
    }
}
