<?php

namespace App\Domain\Financial\Enums;

/**
 * Every kind of money the ERP can take in, as a routing key.
 *
 * This is the catalogue the revenue routing screen lists. A source is only
 * here if some code path can actually produce it — a row that can never fire
 * is a rule an accountant will configure and then wonder why it never moved
 * anything.
 *
 * The key is stored in `revenue_mappings.source_key` and is permanent: renaming
 * a case orphans every rule a client has written against it.
 */
enum RevenueSource: string
{
    /** An invoice line with no itemable — the programme subscription convention. */
    case Subscription = 'subscription';

    /** A line pointed at a programme explicitly (the reporting path uses this). */
    case TrainingProgram = 'training_program';

    case Product = 'product';
    case Kit = 'kit';
    case Event = 'event';
    case SpaceReservation = 'space_reservation';

    /** The platform fee on an invoice — not a line, a column on the invoice. */
    case ServiceFee = 'service_fee';

    /** Sponsorships and club money, recorded outside the invoice flow. */
    case External = 'external';

    case Other = 'other';

    public function label(): string
    {
        return match ($this) {
            self::Subscription => 'اشتراك تدريبي',
            self::TrainingProgram => 'اشتراك برنامج مرتبط',
            self::Product => 'بيع منتج',
            self::Kit => 'بيع طقم',
            self::Event => 'رسوم إيفنت أو بطولة',
            self::SpaceReservation => 'حجز ملعب أو مساحة',
            self::ServiceFee => 'رسوم خدمة المنصة',
            self::External => 'إيراد خارجي',
            self::Other => 'إيرادات أخرى',
        };
    }

    /** Where in the system this money is taken, in the operator's own words. */
    public function description(): string
    {
        return match ($this) {
            self::Subscription => 'كل بند فاتورة غير مرتبط بصنف — اشتراكات البرامج التي يكتبها الاستقبال ووحدة التحصيل',
            self::TrainingProgram => 'بنود الفواتير المرتبطة ببرنامج تدريبي بعينه',
            self::Product => 'مبيعات نقطة البيع وبنود المنتجات في فواتير التسجيل',
            self::Kit => 'الأطقم المباعة ضمن التسجيل أو من نقطة البيع',
            self::Event => 'رسوم الاشتراك في الإيفنتات والبطولات',
            self::SpaceReservation => 'حجوزات الملاعب والمساحات',
            self::ServiceFee => 'رسوم المنصة المضافة على الفاتورة حين يتحملها العميل',
            self::External => 'الإيرادات الخارجية المسجَّلة يدوياً: رعاة، أندية، دفعات من خارج النظام',
            self::Other => 'أي تحصيل لا ينطبق عليه ما سبق — شبكة الأمان حتى لا يسقط مبلغ من الدفاتر',
        };
    }

    /** The account code the hardcoded `match` used before routing existed. */
    public function legacyAccountCode(): string
    {
        return match ($this) {
            self::Subscription, self::TrainingProgram => '4000',
            self::Product, self::Kit => '4020',
            self::Event => '4050',
            self::SpaceReservation => '4030',
            self::ServiceFee, self::External, self::Other => '4060',
        };
    }

    /**
     * The model an invoice line of this source points at, when it points at
     * anything. This is what makes a per-item override possible: "this product
     * specifically goes somewhere else".
     *
     * @return class-string|null
     */
    public function scopeModel(): ?string
    {
        return match ($this) {
            self::Product => \App\Domain\Inventory\Models\Product::class,
            self::Kit => \App\Domain\Inventory\Models\Kit::class,
            self::Event => \App\Domain\Event\Models\Event::class,
            self::SpaceReservation => \App\Domain\Facility\Models\SpaceReservation::class,
            self::TrainingProgram => \App\Domain\Training\Models\TrainingProgram::class,
            self::Subscription, self::ServiceFee, self::External, self::Other => null,
        };
    }

    /** Can an accountant narrow this source down to one specific item? */
    public function supportsOverrides(): bool
    {
        return $this->scopeModel() !== null;
    }

    /**
     * Which source an invoice line belongs to.
     *
     * A null itemable means a subscription: that is the convention
     * ParticipantBillingService, the settlement worklist and every revenue
     * widget already read, and this must not disagree with them.
     */
    public static function forItemable(?string $itemableType): self
    {
        return match ($itemableType) {
            null, '' => self::Subscription,
            \App\Domain\Inventory\Models\Product::class => self::Product,
            \App\Domain\Inventory\Models\Kit::class => self::Kit,
            \App\Domain\Event\Models\Event::class => self::Event,
            \App\Domain\Facility\Models\SpaceReservation::class => self::SpaceReservation,
            \App\Domain\Training\Models\TrainingProgram::class => self::TrainingProgram,
            default => self::Other,
        };
    }

    /** The catalogue in the order the screen shows it. */
    public static function catalogue(): array
    {
        return [
            self::Subscription,
            self::Product,
            self::Kit,
            self::Event,
            self::SpaceReservation,
            self::TrainingProgram,
            self::ServiceFee,
            self::External,
            self::Other,
        ];
    }
}
