<?php

namespace App\Livewire\Pricing;

use App\Domain\Pricing\Enums\AdjustmentType;
use App\Domain\Pricing\Enums\PromotionType;
use App\Domain\Pricing\Models\Promotion;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('عرض / كوبون')]
class PromotionForm extends Component
{
    use UsesBranchScope;

    /**
     * #[Locked] because a public model property is re-resolved from the id in
     * the wire payload on every round-trip, and $editing is what decides
     * between update and create.
     */
    #[Locked]
    public ?Promotion $promotion = null;

    #[Locked]
    public bool $editing = false;

    public string $name_ar = '';
    public string $name = '';
    public string $code = '';
    public string $type = '';
    public string $adjustment_type = '';
    public string $adjustment_value = '';

    /**
     * No control in promotion-form.blade.php binds either of these — they are
     * read out of the record in mount() and written back in save(). Left
     * unlocked they were still settable from the browser, which is how a
     * promotion could be aimed at another branch's programme without the form
     * ever showing a target field.
     */
    #[Locked]
    public string $target_type = '';

    #[Locked]
    public string $target_id = '';

    public string $branch_id = '';
    public string $min_purchase_amount = '';
    public string $max_discount_amount = '';
    public string $start_date = '';
    public string $end_date = '';
    public bool $is_active = true;
    public string $usage_limit = '';
    public string $per_participant_limit = '';
    public int $priority = 0;

    public function mount(?Promotion $promotion = null): void
    {
        $this->authorize('pricing.create');

        if ($promotion && $promotion->exists) {
            $this->authorize('pricing.update');
            $this->assertBranchVisible($promotion->branch_id);
            $this->promotion = $promotion;
            $this->editing = true;
            $this->name_ar = $promotion->name_ar;
            $this->name = $promotion->name ?? '';
            $this->code = $promotion->code ?? '';
            $this->type = $promotion->type?->value ?? '';
            $this->adjustment_type = $promotion->adjustment_type?->value ?? '';
            $this->adjustment_value = $this->formatAdjustmentForDisplay($promotion);
            $this->target_type = $promotion->target_type ?? '';
            $this->target_id = (string) ($promotion->target_id ?? '');
            $this->branch_id = (string) ($promotion->branch_id ?? '');
            $this->min_purchase_amount = $promotion->min_purchase_amount ? (string) ($promotion->min_purchase_amount / 100) : '';
            $this->max_discount_amount = $promotion->max_discount_amount ? (string) ($promotion->max_discount_amount / 100) : '';
            $this->start_date = $promotion->start_date?->format('Y-m-d') ?? '';
            $this->end_date = $promotion->end_date?->format('Y-m-d') ?? '';
            $this->is_active = $promotion->is_active;
            $this->usage_limit = (string) ($promotion->usage_limit ?? '');
            $this->per_participant_limit = (string) ($promotion->per_participant_limit ?? '');
            $this->priority = $promotion->priority ?? 0;
        } else {
            $this->start_date = now()->format('Y-m-d');
        }
    }

    /**
     * Route model binding is substituted before ResolveBranchContext runs, so
     * BranchScope is not yet enforcing when the router resolves {promotion}.
     * The record — including its coupon code and usage counters — has to be
     * checked here by hand.
     *
     * An offer belongs to the branch that runs it, so only that branch may
     * open it.
     */
    private function assertBranchVisible(?int $branchId): void
    {
        $active = $this->getActiveBranchId();

        // A null active branch is a user genuinely in all-branches mode, who
        // may reach anything. A null on the record itself is a row the backfill
        // missed: strictly scoped, it belongs to no branch and is editable from
        // none, so refuse rather than let one branch quietly adopt it.
        abort_if($active !== null && $branchId !== $active, 404);
    }

    private function formatAdjustmentForDisplay(Promotion $promotion): string
    {
        if (in_array($promotion->adjustment_type, [AdjustmentType::FixedDiscount, AdjustmentType::FixedPrice])) {
            return (string) ($promotion->adjustment_value / 100);
        }

        return (string) \App\Domain\Pricing\Support\Percent::fromBasisPoints((int) $promotion->adjustment_value);
    }

    public function rules(): array
    {
        return [
            'name_ar' => 'required|string|max:255',
            'name' => 'nullable|string|max:255',
            'code' => $this->type === 'coupon' ? 'required|string|max:50' : 'nullable|string|max:50',
            'type' => 'required|in:' . implode(',', array_column(PromotionType::cases(), 'value')),
            'adjustment_type' => 'required|in:' . implode(',', array_column(AdjustmentType::cases(), 'value')),
            'adjustment_value' => 'required|numeric|min:0.01',
            'target_type' => 'nullable|string|max:255',
            'target_id' => 'nullable|integer',
            // Not exists:branches,id — a raw table query accepts every branch
            // in the academy. Only what the picker may legitimately offer, plus
            // the empty "all branches" value.
            // Required, not nullable: the row is strictly branch-scoped, so an
            // empty picker would file it against no branch and hide it from
            // every one of them.
            'branch_id' => ['required', Rule::in($this->selectableBranches()->pluck('id')->all())],
            'min_purchase_amount' => 'nullable|numeric|min:0',
            'max_discount_amount' => 'nullable|numeric|min:0',
            'start_date' => 'required|date',
            'end_date' => 'nullable|date|after:start_date',
            'is_active' => 'boolean',
            'usage_limit' => 'nullable|integer|min:1',
            'per_participant_limit' => 'nullable|integer|min:1',
            'priority' => 'integer|min:0',
        ];
    }

    public function messages(): array
    {
        return [
            'name_ar.required' => __('الاسم بالعربية مطلوب'),
            'code.required' => __('كود الكوبون مطلوب'),
            'type.required' => __('نوع العرض مطلوب'),
            'type.in' => __('نوع العرض غير صالح'),
            'adjustment_type.required' => __('نوع التعديل مطلوب'),
            'adjustment_type.in' => __('نوع التعديل غير صالح'),
            'adjustment_value.required' => __('قيمة التعديل مطلوبة'),
            'adjustment_value.min' => __('قيمة التعديل يجب أن تكون أكبر من صفر'),
            'start_date.required' => __('تاريخ البداية مطلوب'),
            'end_date.after' => __('تاريخ النهاية يجب أن يكون بعد تاريخ البداية'),
            'branch_id.required' => 'يجب اختيار الفرع',
            'branch_id.in' => __('الفرع المحدد غير صالح'),
        ];
    }

    public function save(): void
    {
        $this->validate();

        $adjustmentValue = $this->computeAdjustmentValueForStorage();

        $data = [
            'name_ar' => $this->name_ar,
            'name' => $this->name ?: null,
            'code' => ($this->type === 'coupon' && $this->code) ? strtoupper($this->code) : null,
            'type' => $this->type,
            'adjustment_type' => $this->adjustment_type,
            'adjustment_value' => $adjustmentValue,
            'target_type' => $this->target_type ?: null,
            'target_id' => $this->target_id ? (int) $this->target_id : null,
            'branch_id' => $this->branch_id ? (int) $this->branch_id : null,
            'min_purchase_amount' => $this->min_purchase_amount ? (int) round((float) $this->min_purchase_amount * 100) : null,
            'max_discount_amount' => $this->max_discount_amount ? (int) round((float) $this->max_discount_amount * 100) : null,
            'start_date' => $this->start_date,
            'end_date' => $this->end_date ?: null,
            'is_active' => $this->is_active,
            'usage_limit' => $this->usage_limit ? (int) $this->usage_limit : null,
            'per_participant_limit' => $this->per_participant_limit ? (int) $this->per_participant_limit : null,
            'priority' => $this->priority,
        ];

        if ($this->editing && $this->promotion) {
            $this->promotion->update($data);
            session()->flash('success', __('تم تحديث العرض بنجاح'));
        } else {
            $data['created_by'] = auth()->id();
            $data['usage_count'] = 0;
            $created = Promotion::create($data);


            session()->flash('success', __('تم إنشاء العرض بنجاح'));
        }

        $this->redirect(route('pricing.promotions'), navigate: true);
    }

    private function computeAdjustmentValueForStorage(): int
    {
        $type = AdjustmentType::tryFrom($this->adjustment_type);
        if (in_array($type, [AdjustmentType::FixedDiscount, AdjustmentType::FixedPrice])) {
            return (int) round((float) $this->adjustment_value * 100);
        }

        // Basis points — the engine divides by 10000.
        return \App\Domain\Pricing\Support\Percent::toBasisPoints($this->adjustment_value);
    }

    public function render()
    {
        return view('livewire.pricing.promotion-form', [
            // The topbar switcher is the only branch selector. This one picks
            // which branch the promotion applies to, so it offers the branch
            // you are standing in (or every branch, for an all-branches user)
            // plus the academy-wide empty option the view renders itself.
            'branches' => $this->selectableBranches(),
            'promotionTypes' => collect(PromotionType::cases())->mapWithKeys(fn ($t) => [$t->value => $t->label()])->toArray(),
            'adjustmentTypes' => collect(AdjustmentType::cases())->mapWithKeys(fn ($t) => [$t->value => $t->label()])->toArray(),
        ]);
    }
}
