<?php

namespace App\Domain\Shared\Branding;

/**
 * Everything a surface needs to render a tenant's brand, resolved once.
 *
 * Before this existed, each layout carried its own `@php` block issuing one
 * SettingsService::get() per field — ~16 SELECTs per admin render, repeated on
 * every Livewire round-trip — and each block chose its own fallback, so
 * primary_color had three different defaults across three files.
 *
 * Every value is a finished value: colours are validated hex, images are URLs
 * (never raw storage paths, which is what components/print/sheet.blade.php was
 * emitting into an <img src> and why the logo was broken on every printed
 * sheet), and each brand colour arrives with a shade ramp and a foreground
 * colour that actually contrasts with it.
 */
final readonly class BrandProfile
{
    /**
     * @param  array<string, ColorRamp>  $ramps  keyed by role: primary, secondary, accent…
     */
    public function __construct(
        public int $academyId,
        public int $version,
        public string $academyName,
        public string $appShortName,
        public ?string $address,
        public ?string $phone,
        public ?string $email,

        public ?string $logoUrl,
        public ?string $logoDarkUrl,
        public ?string $faviconUrl,
        public ?string $signatureUrl,
        public ?string $loginBackgroundUrl,
        public ?string $invoiceHeaderUrl,
        public ?string $appIconUrl,
        public ?string $splashImageUrl,

        public string $primary,
        public string $secondary,
        public string $accent,
        public string $sidebarBg,
        public string $sidebarText,
        public string $sidebarActive,
        public string $headerBg,
        public string $success,
        public string $warning,
        public string $danger,
        public string $themeColor,
        public string $themeMode,

        public string $fontAr,
        public string $fontEn,
        public int $fontSizeBase,

        public string $invoiceFooterText,
        public string $receiptFooterText,
        public string $termsAndConditions,

        public bool $showLogoInSidebar,
        public bool $showLogoInInvoice,
        public bool $showSignatureInInvoice,
        public bool $compactSidebar,

        public array $ramps,
    ) {}

    public function ramp(string $role): ColorRamp
    {
        return $this->ramps[$role] ?? $this->ramps['primary'];
    }

    /**
     * The `:root` custom properties every surface binds to. Emitted server-side
     * once per document; Tailwind's `@theme inline` block reads these at
     * runtime rather than baking a copy at build time.
     *
     * @return array<string, string>
     */
    public function cssVariables(): array
    {
        $vars = [
            '--brand-primary' => $this->primary,
            '--brand-secondary' => $this->secondary,
            '--brand-accent' => $this->accent,
            '--brand-sidebar-bg' => $this->sidebarBg,
            '--brand-sidebar-text' => $this->sidebarText,
            '--brand-sidebar-active' => $this->sidebarActive,
            '--brand-header-bg' => $this->headerBg,
            '--brand-success' => $this->success,
            '--brand-warning' => $this->warning,
            '--brand-danger' => $this->danger,

            /*
            Text drawn on top of a semantic colour, black or white, whichever
            actually reads. A filled amber button with white text is about
            2:1 — the amber is the same in every tenant, so this was wrong in
            every tenant, and hardcoding #fff at each call site is how it stayed
            wrong. Same computation as --brand-fg, applied to these three.
            */
            '--brand-success-fg' => $this->readableOn($this->success),
            '--brand-warning-fg' => $this->readableOn($this->warning),
            '--brand-danger-fg' => $this->readableOn($this->danger),

            // …and the other direction: the same colour used *as* text, on a
            // light surface and on a dark one. See ColorRamp::ink().
            '--brand-success-ink' => ColorRamp::ink($this->success),
            '--brand-warning-ink' => ColorRamp::ink($this->warning),
            '--brand-danger-ink' => ColorRamp::ink($this->danger),
            '--brand-success-ink-dark' => ColorRamp::ink($this->success, ColorRamp::DARK_SURFACE),
            '--brand-warning-ink-dark' => ColorRamp::ink($this->warning, ColorRamp::DARK_SURFACE),
            '--brand-danger-ink-dark' => ColorRamp::ink($this->danger, ColorRamp::DARK_SURFACE),
            '--brand-font-size' => $this->fontSizeBase . 'px',
            '--brand-font-ar' => "'{$this->fontAr}'",
            '--brand-font-en' => "'{$this->fontEn}'",
        ];

        foreach ($this->ramps as $role => $ramp) {
            $prefix = $role === 'primary' ? '--brand' : "--brand-{$role}";

            foreach ($ramp->shades as $step => $value) {
                $vars["{$prefix}-{$step}"] = $value;
            }

            $vars["{$prefix}-fg"] = $ramp->foreground;

            // The colour text is set in when it has to read as interactive on
            // a light surface. Not a shade of the ramp — see ColorRamp.
            $vars["{$prefix}-link"] = $ramp->interactive;
            $vars["{$prefix}-link-dark"] = $ramp->interactiveOnDark;
        }

        return $vars;
    }

    private function readableOn(string $hex): string
    {
        [$r, $g, $b] = ColorRamp::hexToRgb($hex);

        return ColorRamp::readableForeground($r, $g, $b);
    }

    public function cssVariableBlock(): string
    {
        $lines = [];

        foreach ($this->cssVariables() as $name => $value) {
            $lines[] = "    {$name}: {$value};";
        }

        return implode("\n", $lines);
    }
}
