<?php

namespace App\Models\Central;

use App\Services\PosSettings;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
 * One row per tenant — the landlord-editable Branding / Invoice business
 * identity / Theme / Locale / POS configuration shown on the Master Control
 * tenant detail page. Central data, like {@see Tenant} itself: never
 * tenant-scoped.
 *
 * The `invoice` section is the single source of the business identity printed
 * on every invoice: the nine landlord-managed fields plus a repeatable branch
 * list. Everything else on a print (invoice number, dates, party details,
 * lines, tax breakdown, totals, payments) comes from the invoice record itself
 * and is never landlord-configurable.
 */
class TenantSetting extends Model
{
    protected $fillable = [
        'tenant_id',
        'branding_app_name', 'branding_logo_url', 'branding_login_heading', 'branding_login_subtitle', 'branding_panel_color', 'branding_logo_backdrop',
        'invoice_business_name', 'invoice_business_logo', 'invoice_business_address', 'invoice_business_phone',
        'invoice_business_email', 'invoice_business_website', 'invoice_business_registration_no',
        'invoice_tax_registration_no', 'invoice_business_description', 'invoice_branches', 'invoice_vat_enabled',
        'locale_currency', 'locale_currency_symbol', 'locale_locale', 'locale_timezone',
        'theme_primary', 'theme_secondary', 'theme_background', 'theme_foreground', 'theme_radius', 'theme_font_family',
        'theme_preset', 'theme_font_body', 'theme_font_heading', 'theme_scale',
        'pos_walkin_only', 'pos_amend_window_minutes', 'pos_allow_amend',
        'pos_loyalty_enabled', 'pos_vouchers_enabled',
        'sales_order_deadline_warning_days', 'sales_order_auto_invoice', 'sales_order_whatsapp_target',
    ];

    /**
     * Defaults applied when a tenant has no settings row yet, so consumers
     * never have to null-coalesce operational flags into an unsafe state.
     */
    public const POS_DEFAULTS = [
        'walkin_only' => false,
        'amend_window_minutes' => 30,
        'allow_amend' => true,
        'loyalty_enabled' => true,
        'vouchers_enabled' => true,
    ];

    /**
     * As POS_DEFAULTS, for the sales order section. Auto-invoice defaults off:
     * the existing behaviour (take the order, invoice it later by hand) is what
     * a tenant gets until someone deliberately turns it on.
     */
    public const SALES_ORDER_DEFAULTS = [
        'deadline_warning_days' => 3,
        'auto_invoice' => false,
        'whatsapp_target' => null,
    ];

    protected function casts(): array
    {
        return [
            'invoice_branches' => 'array',
            'invoice_vat_enabled' => 'boolean',
            'pos_walkin_only' => 'boolean',
            'pos_amend_window_minutes' => 'integer',
            'pos_allow_amend' => 'boolean',
            'pos_loyalty_enabled' => 'boolean',
            'pos_vouchers_enabled' => 'boolean',
            'sales_order_deadline_warning_days' => 'integer',
            'sales_order_auto_invoice' => 'boolean',
        ];
    }

    public function tenant(): BelongsTo
    {
        return $this->belongsTo(Tenant::class);
    }

    /**
     * Reshape into the nested {branding, invoice, locale, theme, pos} structure
     * the settings form and the invoice print layer expect — so consumers don't
     * need to know this is backed by flat columns.
     *
     * @return array<string, array<string, mixed>>
     */
    public function toNestedArray(): array
    {
        return [
            'branding' => [
                'app_name' => $this->branding_app_name,
                'logo_url' => $this->branding_logo_url,
                'login_heading' => $this->branding_login_heading,
                'login_subtitle' => $this->branding_login_subtitle,
                'panel_color' => $this->branding_panel_color,
                'logo_backdrop' => $this->branding_logo_backdrop,
            ],
            'invoice' => [
                'business_name' => $this->invoice_business_name,
                'business_logo' => $this->invoice_business_logo,
                'business_address' => $this->invoice_business_address,
                'business_phone' => $this->invoice_business_phone,
                'business_email' => $this->invoice_business_email,
                'business_website' => $this->invoice_business_website,
                'business_registration_no' => $this->invoice_business_registration_no,
                'tax_registration_no' => $this->invoice_tax_registration_no,
                'business_description' => $this->invoice_business_description,
                'branches' => $this->branchList(),
                'vat_enabled' => (bool) $this->invoice_vat_enabled,
            ],
            'locale' => [
                'currency' => $this->locale_currency,
                'currency_symbol' => $this->locale_currency_symbol,
                'locale' => $this->locale_locale,
                'timezone' => $this->locale_timezone,
            ],
            'theme' => [
                'preset' => $this->theme_preset,
                'font_body' => $this->theme_font_body,
                'font_heading' => $this->theme_font_heading,
                'radius' => $this->theme_radius,
                'scale' => $this->theme_scale,
            ],
            'pos' => [
                'walkin_only' => (bool) $this->pos_walkin_only,
                'amend_window_minutes' => (int) ($this->pos_amend_window_minutes ?? self::POS_DEFAULTS['amend_window_minutes']),
                'allow_amend' => (bool) $this->pos_allow_amend,
                'loyalty_enabled' => (bool) ($this->pos_loyalty_enabled ?? self::POS_DEFAULTS['loyalty_enabled']),
                'vouchers_enabled' => (bool) ($this->pos_vouchers_enabled ?? self::POS_DEFAULTS['vouchers_enabled']),
            ],
            'sales_order' => $this->salesOrderSection(),
        ];
    }

    /**
     * Flatten the nested {branding, invoice, locale, theme, pos} payload back
     * into this model's column names, for mass assignment.
     *
     * @param  array<string, array<string, mixed>>  $nested
     * @return array<string, mixed>
     */
    public static function flatten(array $nested): array
    {
        $flat = [];
        foreach (['branding', 'invoice', 'locale', 'theme', 'pos', 'sales_order'] as $section) {
            foreach ($nested[$section] ?? [] as $key => $value) {
                $flat["{$section}_{$key}"] = $value;
            }
        }

        if (array_key_exists('invoice_branches', $flat)) {
            $flat['invoice_branches'] = self::normalizeBranches($flat['invoice_branches']);
        }

        return $flat;
    }

    /**
     * The branch Repeater hands back rows keyed by generated UUIDs, and a
     * landlord who clicks "add" then changes their mind leaves an all-blank
     * row behind. Re-index to a list and drop rows with nothing in them, so
     * the print layer can trust every entry it iterates.
     *
     * @return list<array{label: string|null, address: string|null, phone: string|null}>
     */
    public static function normalizeBranches(mixed $branches): array
    {
        if (! is_array($branches)) {
            return [];
        }

        $rows = [];

        foreach ($branches as $branch) {
            if (! is_array($branch)) {
                continue;
            }

            $row = [
                'label' => self::trimmedOrNull($branch['label'] ?? null),
                'address' => self::trimmedOrNull($branch['address'] ?? null),
                'phone' => self::trimmedOrNull($branch['phone'] ?? null),
            ];

            if ($row['label'] === null && $row['address'] === null && $row['phone'] === null) {
                continue;
            }

            $rows[] = $row;
        }

        return $rows;
    }

    private static function trimmedOrNull(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $value = trim($value);

        return $value === '' ? null : $value;
    }

    /**
     * @return list<array{label: string|null, address: string|null, phone: string|null}>
     */
    public function branchList(): array
    {
        return self::normalizeBranches($this->invoice_branches);
    }

    /**
     * The POS section with defaults applied — safe to call on a tenant that has
     * no settings row yet. Used by {@see PosSettings}.
     *
     * @return array{walkin_only: bool, amend_window_minutes: int, allow_amend: bool, loyalty_enabled: bool, vouchers_enabled: bool}
     */
    public function posSection(): array
    {
        return [
            'walkin_only' => (bool) $this->pos_walkin_only,
            'amend_window_minutes' => (int) ($this->pos_amend_window_minutes ?? self::POS_DEFAULTS['amend_window_minutes']),
            'allow_amend' => (bool) $this->pos_allow_amend,
            'loyalty_enabled' => (bool) ($this->pos_loyalty_enabled ?? self::POS_DEFAULTS['loyalty_enabled']),
            'vouchers_enabled' => (bool) ($this->pos_vouchers_enabled ?? self::POS_DEFAULTS['vouchers_enabled']),
        ];
    }

    /**
     * The sales order section with defaults applied — safe to call on a tenant
     * that has no settings row yet. Used by {@see \App\Services\AppSettings}.
     *
     * @return array{deadline_warning_days: int, auto_invoice: bool, whatsapp_target: string|null}
     */
    public function salesOrderSection(): array
    {
        $target = trim((string) ($this->sales_order_whatsapp_target ?? ''));

        return [
            'deadline_warning_days' => (int) ($this->sales_order_deadline_warning_days
                ?? self::SALES_ORDER_DEFAULTS['deadline_warning_days']),
            'auto_invoice' => (bool) ($this->sales_order_auto_invoice
                ?? self::SALES_ORDER_DEFAULTS['auto_invoice']),
            'whatsapp_target' => $target === '' ? null : $target,
        ];
    }
}
