<?php

namespace App\Support\Invoice;

use App\Models\Central\Tenant;
use App\Models\Central\TenantSetting;
use App\Services\Storage\TenantStorage;

/**
 * The landlord-managed business identity printed on every invoice — the nine
 * fields the Master Control "Invoice" tab asks for, plus the branch list.
 *
 * This is the ONLY part of an invoice that is configurable. Everything else on
 * a print (numbers, dates, parties, lines, tax breakdown, totals, tenders) is
 * derived from the invoice record and is fixed by what the document has to say
 * to be a valid tax invoice.
 */
final class InvoiceBusiness
{
    /**
     * @param  list<array{label: string|null, address: string|null, phone: string|null}>  $branches
     */
    public function __construct(
        public readonly ?string $name = null,
        public readonly ?string $logo = null,
        public readonly ?string $address = null,
        public readonly ?string $phone = null,
        public readonly ?string $email = null,
        public readonly ?string $website = null,
        public readonly ?string $registrationNo = null,
        public readonly ?string $taxRegistrationNo = null,
        public readonly ?string $description = null,
        public readonly array $branches = [],
        public readonly bool $vatEnabled = false,
    ) {}

    /**
     * Build from the nested settings shape ({@see TenantSetting::toNestedArray()})
     * or from live form state on the Master Control edit page — both use the
     * same keys, which is what keeps the live preview honest.
     *
     * @param  array<string, mixed>  $settings
     */
    public static function fromSettings(array $settings): self
    {
        $invoice = (array) ($settings['invoice'] ?? []);
        $branding = (array) ($settings['branding'] ?? []);

        return new self(
            name: self::clean($invoice['business_name'] ?? null),
            // Falls back to the branding logo so a tenant who has uploaded a
            // logo once never has to upload it again just to get a letterhead.
            logo: self::clean($invoice['business_logo'] ?? null) ?? self::clean($branding['logo_url'] ?? null),
            address: self::clean($invoice['business_address'] ?? null),
            phone: self::clean($invoice['business_phone'] ?? null),
            email: self::clean($invoice['business_email'] ?? null),
            website: self::clean($invoice['business_website'] ?? null),
            registrationNo: self::clean($invoice['business_registration_no'] ?? null),
            taxRegistrationNo: self::clean($invoice['tax_registration_no'] ?? null),
            description: self::clean($invoice['business_description'] ?? null),
            branches: TenantSetting::normalizeBranches($invoice['branches'] ?? []),
            vatEnabled: (bool) ($invoice['vat_enabled'] ?? false),
        );
    }

    /**
     * The persisted settings hold the logo as a storage PATH (rows written
     * before storage became switchable hold a resolved URL, which resolveUrl()
     * passes through untouched), so it is turned into something an <img src>
     * can load here — against whichever source the tenant is on right now.
     */
    public static function forTenant(?Tenant $tenant): self
    {
        $settings = $tenant?->settingsRow?->toNestedArray() ?? [];

        $storage = app(TenantStorage::class);

        foreach ([['invoice', 'business_logo'], ['branding', 'logo_url']] as [$section, $key]) {
            if (isset($settings[$section][$key])) {
                $settings[$section][$key] = $storage->resolveUrl($settings[$section][$key], $tenant);
            }
        }

        return self::fromSettings($settings);
    }

    /**
     * What the letterhead shows when the landlord has filled in nothing yet —
     * the preview must still render a complete document rather than collapsing,
     * otherwise it teaches nothing about the layout.
     */
    public function displayName(?string $fallback = null): string
    {
        return $this->name ?? self::clean($fallback) ?? 'Your business name';
    }

    /** The single letter used when no logo has been uploaded. */
    public function monogram(?string $fallback = null): string
    {
        return mb_strtoupper(mb_substr($this->displayName($fallback), 0, 1));
    }

    /**
     * Registration identifiers, in the order a reader expects them. Kept as
     * discrete label/value pairs so the 80mm layout can stack what the A4
     * layout puts on one line.
     *
     * @return list<array{label: string, value: string}>
     */
    public function registrationLines(): array
    {
        $lines = [];

        if ($this->registrationNo !== null) {
            $lines[] = ['label' => 'Reg. No', 'value' => $this->registrationNo];
        }

        // Not VAT-registered means no trace of VAT anywhere on the print —
        // even if a tax registration number was entered before VAT was
        // switched off, it must not resurface here.
        if ($this->vatEnabled && $this->taxRegistrationNo !== null) {
            $lines[] = ['label' => 'VAT/TIN', 'value' => $this->taxRegistrationNo];
        }

        return $lines;
    }

    /**
     * Phone / email / website as a single ordered list, already stripped of
     * blanks — both layouts iterate this rather than null-checking three
     * fields each.
     *
     * @return list<array{label: string, value: string}>
     */
    public function contactLines(): array
    {
        $lines = [];

        if ($this->phone !== null) {
            $lines[] = ['label' => 'Tel', 'value' => $this->phone];
        }

        if ($this->email !== null) {
            $lines[] = ['label' => 'Email', 'value' => $this->email];
        }

        if ($this->website !== null) {
            $lines[] = ['label' => 'Web', 'value' => $this->website];
        }

        return $lines;
    }

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

        $value = trim($value);

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