<?php

namespace App\Services;

use App\Models\Central\Tenant;
use App\Models\Central\TenantSetting;
use Illuminate\Support\Carbon;

/**
 * Typed read access to the landlord-controlled POS configuration, safe to call
 * for tenants that have no settings row yet (defaults from
 * {@see TenantSetting::POS_DEFAULTS} are applied). This is the one place
 * controllers read `walkin_only` / `amend_window_minutes` / `allow_amend` /
 * `loyalty_enabled` / `vouchers_enabled` from, so the master switches have a
 * single, consistent implementation.
 */
class PosSettings
{
    /**
     * @return array{walkin_only: bool, amend_window_minutes: int, allow_amend: bool, loyalty_enabled: bool, vouchers_enabled: bool}
     */
    public function forCurrentTenant(): array
    {
        $tenant = app(CurrentContext::class)->tenant();
        if (! $tenant) {
            return TenantSetting::POS_DEFAULTS;
        }

        return $this->forTenant($tenant);
    }

    /**
     * @return array{walkin_only: bool, amend_window_minutes: int, allow_amend: bool, loyalty_enabled: bool, vouchers_enabled: bool}
     */
    public function forTenant(Tenant $tenant): array
    {
        $settings = $tenant->settingsRow;

        if (! $settings) {
            return TenantSetting::POS_DEFAULTS;
        }

        return $settings->posSection();
    }

    /**
     * The walk-in-only master switch. When on, the tenant never registers
     * customers: no customer selection anywhere, and credit notes are disabled
     * system-wide (a credit note is meaningless without a customer to hold it).
     */
    public function walkinOnly(): bool
    {
        return (bool) ($this->forCurrentTenant()['walkin_only'] ?? false);
    }

    /**
     * Whether the tenant runs any form of registered-customer flow at all —
     * the inverse of {@see walkinOnly()}.
     */
    public function hasCustomers(): bool
    {
        return ! $this->walkinOnly();
    }

    /**
     * How many minutes after a sale completes a cashier may recall and amend
     * it from POS without a manager.
     */
    public function amendWindowMinutes(): int
    {
        return (int) ($this->forCurrentTenant()['amend_window_minutes'] ?? TenantSetting::POS_DEFAULTS['amend_window_minutes']);
    }

    /**
     * Whether post-print sale amendment is enabled at all.
     */
    public function allowAmend(): bool
    {
        return (bool) ($this->forCurrentTenant()['allow_amend'] ?? TenantSetting::POS_DEFAULTS['allow_amend']);
    }

    /**
     * Whether the loyalty module is switched on system-wide (Master Control →
     * POS). When off, no loyalty UI renders anywhere and the service layer
     * refuses earn/redeem (see LoyaltyService).
     */
    public function loyaltyEnabled(): bool
    {
        return (bool) ($this->forCurrentTenant()['loyalty_enabled'] ?? TenantSetting::POS_DEFAULTS['loyalty_enabled']);
    }

    /**
     * Whether vouchers/promotions are switched on system-wide (Master Control
     * → POS). When off, the voucher code UI never renders and
     * VoucherService::validate() refuses every code.
     */
    public function vouchersEnabled(): bool
    {
        return (bool) ($this->forCurrentTenant()['vouchers_enabled'] ?? TenantSetting::POS_DEFAULTS['vouchers_enabled']);
    }

    /**
     * The tenant's display name for branded surfaces (POS terminal, panel
     * chrome): the invoice "Business name" set on Master Control, falling back
     * to the branding "App name", then the platform default. The business name
     * is the tenant-facing identity, so it overrides the system default
     * whenever the landlord has filled it in.
     */
    public function businessName(): string
    {
        $tenant = app(CurrentContext::class)->tenant();

        $businessName = trim((string) ($tenant?->setting('invoice.business_name') ?? ''));
        if ($businessName !== '') {
            return $businessName;
        }

        $appName = trim((string) ($tenant?->setting('branding.app_name') ?? ''));
        if ($appName !== '') {
            return $appName;
        }

        return (string) config('app.name');
    }

    /**
     * Whether the last-completed sale on the given till session can still be
     * amended: the feature is enabled, and the invoice is inside the window.
     */
    public function canAmendInvoice(?Carbon $completedAt): bool
    {
        if (! $this->allowAmend()) {
            return false;
        }

        if ($completedAt === null) {
            return false;
        }

        return $completedAt->gte(now()->subMinutes($this->amendWindowMinutes()));
    }
}
