<?php

namespace App\Livewire\Pricing;

use App\Domain\Identity\Models\Branch;
use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Enums\AdjustmentType;
use App\Domain\Pricing\Enums\PricingRuleType;
use App\Domain\Pricing\Models\PricingRule;
use App\Domain\Pricing\Services\PricingService;
use App\Domain\Pricing\Support\ConditionSchema;
use App\Domain\Pricing\Support\DiscountRecipe;
use App\Domain\Pricing\Support\Percent;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\TrainingProgram;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

/**
 * Discount builder — recipe gallery, then a sentence you can read out loud.
 *
 * Replaces the five-step column editor. The author never meets rule_type,
 * basis points, morph classes or a conditions JSON blob.
 */
#[Layout('layouts.app')]
#[Title('إنشاء خصم')]
class CreatePricingRuleWizard extends Component
{
    use UsesBranchScope;

    public string $stage = 'recipes';   // recipes | build | done

    public ?string $recipeKey = null;
    public ?int $createdRuleId = null;

    // ─── The sentence ─────────────────────────────────────────────
    public string $nameAr = '';
    public string $ruleType = '';
    public string $adjustmentType = 'percentage_discount';
    public string $amount = '';               // percent, or pounds for fixed types
    public array $conditions = [];
    public array $branchIds = [];
    public ?string $targetType = null;
    public ?int $targetId = null;

    // ─── Behaviour ────────────────────────────────────────────────
    public bool $isStackable = false;         // best-of-one is the normal case
    public bool $isPinned = false;
    public ?string $pinIcon = null;
    public int $displayOrder = 0;
    public bool $isManual = false;
    public bool $requiresApproval = false;
    public ?int $minRoleLevel = null;

    public string $effectiveFrom = '';
    public ?string $effectiveTo = null;
    public bool $isActive = true;
    public ?int $usageLimit = null;
    public ?int $maxDiscountPercent = null;

    // ─── Simulator ────────────────────────────────────────────────
    public ?int $simParticipantId = null;
    public ?int $simProgramId = null;
    public array $simulation = [];
    public array $audience = [];

    public function mount(): void
    {
        $this->authorize('pricing.create');
        $this->effectiveFrom = now()->toDateString();
        // Default to the branch you are standing in — academy-wide should be
        // a deliberate act, not what you get by not thinking about it.
        $this->branchIds = array_values(array_filter([$this->getActiveBranchId()]));
    }

    // ─── Stage 1: recipes ─────────────────────────────────────────

    public function chooseRecipe(string $key): void
    {
        $fill = DiscountRecipe::prefill($key);
        if (! $fill) {
            return;
        }

        $this->recipeKey        = $fill['recipe_key'];
        $this->nameAr           = $fill['name_ar'];
        $this->ruleType         = $fill['rule_type'];
        $this->adjustmentType   = $fill['adjustment_type'];
        $this->amount           = (string) $fill['percent'];
        $this->conditions       = $fill['conditions'];
        $this->isStackable      = $fill['is_stackable'];
        $this->isPinned         = $fill['is_pinned'];
        $this->pinIcon          = $fill['pin_icon'];
        $this->isManual         = $fill['is_manual'];
        $this->requiresApproval = $fill['requires_approval'];

        $this->stage = 'build';
        $this->refreshPreview();
    }

    public function startBlank(): void
    {
        $this->recipeKey = null;
        $this->nameAr = '';
        $this->ruleType = 'age';
        $this->conditions = [];
        $this->amount = '';
        $this->stage = 'build';
    }

    public function backToRecipes(): void
    {
        $this->stage = 'recipes';
        $this->simulation = [];
        $this->audience = [];
    }

    // ─── Live preview ─────────────────────────────────────────────

    public function updated(string $field): void
    {
        if (str_starts_with($field, 'conditions') || in_array($field, ['ruleType', 'amount', 'adjustmentType'], true)) {
            $this->refreshPreview();
        }

        if (in_array($field, ['simParticipantId', 'simProgramId'], true)) {
            $this->refreshPreview();
        }
    }

    public function updatedRuleType(): void
    {
        $this->conditions = [];
    }

    /** The whole rule as one readable Arabic sentence. */
    public function getSentenceProperty(): string
    {
        $value = $this->amount !== '' ? $this->amount : '—';
        $verb = match ($this->adjustmentType) {
            'percentage_increase' => "زِد السعر {$value}%",
            'fixed_discount'      => "امنح خصم {$value} ج.م",
            'fixed_price'         => "اجعل السعر {$value} ج.م",
            default               => "امنح خصم {$value}%",
        };

        $target = $this->targetId ? 'على برنامج محدد' : 'على كل البرامج';
        $who = $this->ruleType ? ConditionSchema::describe($this->ruleType, $this->conditions) : '';
        $where = $this->branchIds
            ? 'في ' . Branch::whereIn('id', $this->branchIds)->pluck('name_ar')->implode('، ')
            : 'في كل الفروع';

        $when = 'من ' . ($this->effectiveFrom ?: '—')
            . ($this->effectiveTo ? " إلى {$this->effectiveTo}" : ' بدون نهاية');

        return trim("{$verb} {$target} {$who} {$where} {$when}");
    }

    /** Run the draft rule against a real participant + program. */
    public function refreshPreview(): void
    {
        $this->simulation = [];
        $this->audience = [];

        if (! $this->ruleType || $this->amount === '') {
            return;
        }

        $draft = $this->draftRule();
        $service = app(PricingService::class);

        try {
            $this->audience = $service->audience($draft, $this->branchIds[0] ?? null);
        } catch (\Throwable $e) {
            $this->audience = [];
        }

        if (! $this->simParticipantId || ! $this->simProgramId) {
            return;
        }

        $participant = Participant::with(['person', 'guardians'])->find($this->simParticipantId);
        $program = TrainingProgram::find($this->simProgramId);

        if (! $participant || ! $program) {
            return;
        }

        try {
            $live = $service->calculate($program, $participant, $this->branchIds[0] ?? null);

            // What the draft rule alone would do to the current price.
            $qualifies = ! empty($draft->conditions)
                ? $service->wouldApply($draft, $participant)
                : true;

            $this->simulation = [
                'participant' => $participant->full_name,
                'program'     => $program->name_ar ?? $program->name,
                'base'        => $live->baseAmount,
                'current'     => $live->finalAmount,
                'applied'     => $live->appliedRules,
                'qualifies'   => $qualifies,
                'draft_name'  => $this->nameAr ?: 'الخصم الجديد',
            ];
        } catch (DomainException $e) {
            $this->simulation = ['error' => $e->getMessage()];
        }
    }

    /** An unsaved PricingRule used only for preview — never persisted. */
    private function draftRule(): PricingRule
    {
        $rule = new PricingRule([
            'name_ar'         => $this->nameAr ?: 'مسودة',
            'rule_type'       => $this->ruleType,
            'adjustment_type' => $this->adjustmentType,
            'conditions'      => ConditionSchema::normalize($this->ruleType, $this->conditions),
            'is_stackable'    => $this->isStackable,
        ]);

        $rule->adjustment_value = $this->storedAmount();

        return $rule;
    }

    private function storedAmount(): int
    {
        return in_array($this->adjustmentType, ['fixed_discount', 'fixed_price'], true)
            ? (int) round((float) $this->amount * 100)
            : Percent::toBasisPoints($this->amount ?: 0);
    }

    // ─── Save ─────────────────────────────────────────────────────

    public function rules(): array
    {
        return [
            'nameAr'             => 'required|string|min:3|max:255',
            'ruleType'           => 'required|in:' . implode(',', array_column(PricingRuleType::cases(), 'value')),
            'adjustmentType'     => 'required|in:' . implode(',', array_column(AdjustmentType::cases(), 'value')),
            'amount'             => 'required|numeric|min:0.01',
            'branchIds'          => 'array',
            'branchIds.*'        => 'integer|exists:branches,id',
            'effectiveFrom'      => 'required|date',
            'effectiveTo'        => 'nullable|date|after:effectiveFrom',
            'usageLimit'         => 'nullable|integer|min:1',
            'maxDiscountPercent' => 'nullable|integer|min:1|max:100',
            'displayOrder'       => 'integer|min:0',
        ] + ConditionSchema::validationRules($this->ruleType ?: 'custom');
    }

    public function messages(): array
    {
        return [
            'nameAr.required'         => __('اسم الخصم مطلوب'),
            'nameAr.min'              => __('اسم الخصم قصير جداً'),
            'ruleType.required'       => __('نوع الشرط مطلوب'),
            'adjustmentType.required' => __('نوع التعديل مطلوب'),
            'amount.required'         => __('قيمة الخصم مطلوبة'),
            'amount.numeric'          => __('قيمة الخصم يجب أن تكون رقماً'),
            'amount.min'              => __('قيمة الخصم يجب أن تكون أكبر من صفر'),
            'effectiveFrom.required'  => __('تاريخ البداية مطلوب'),
            'effectiveTo.after'       => __('تاريخ النهاية يجب أن يكون بعد تاريخ البداية'),
            'conditions.values.required' => __('يجب اختيار قيمة واحدة على الأقل'),
            'conditions.months.required' => __('يجب اختيار شهر واحد على الأقل'),
            'conditions.max.gte'      => __('الحد الأعلى يجب أن يكون أكبر من الحد الأدنى'),
        ];
    }

    public function save(): void
    {
        $this->authorize('pricing.create');
        $this->validate();

        $conditions = ConditionSchema::normalize($this->ruleType, $this->conditions);

        // A rule with no conditions is an academy-wide discount wearing a
        // specific name. Manual rules are the deliberate exception.
        if (! $this->isManual && ! ConditionSchema::isMeaningful($this->ruleType, $conditions)) {
            $this->addError('conditions', __('يجب تحديد شرط واحد على الأقل — وإلا سيُطبق الخصم على كل المشتركين'));

            return;
        }

        try {
            $rule = DB::transaction(function () use ($conditions) {
                $rule = PricingRule::create([
                    'academy_id'           => app('current_academy')->id,
                    'name_ar'              => $this->nameAr,
                    'rule_type'            => $this->ruleType,
                    'adjustment_type'      => $this->adjustmentType,
                    'adjustment_value'     => $this->storedAmount(),
                    'conditions'           => $conditions,
                    'target_type'          => $this->targetType ?: null,
                    'target_id'            => $this->targetId ?: null,
                    // Legacy single-branch column stays null; the pivot owns targeting.
                    'branch_id'            => null,
                    'priority'             => 10,
                    'is_stackable'         => $this->isStackable,
                    'max_discount_percent' => $this->maxDiscountPercent,
                    'effective_from'       => $this->effectiveFrom,
                    'effective_to'         => $this->effectiveTo ?: null,
                    'is_active'            => $this->isActive,
                    'usage_limit'          => $this->usageLimit,
                    'usage_count'          => 0,
                    'created_by'           => auth()->id(),
                    'is_pinned'            => $this->isPinned,
                    'pin_icon'             => $this->pinIcon,
                    'display_order'        => $this->displayOrder,
                    'recipe_key'           => $this->recipeKey,
                    'is_manual'            => $this->isManual,
                    'requires_approval'    => $this->requiresApproval,
                    'min_role_level'       => $this->minRoleLevel,
                ]);

                if ($this->branchIds) {
                    $rule->branches()->sync($this->branchIds);
                }

                return $rule;
            });

            $this->createdRuleId = $rule->id;
            $this->stage = 'done';
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    public function render()
    {
        return view('livewire.pricing.create-pricing-rule-wizard', [
            'recipes'  => DiscountRecipe::all(),
            'branches' => Branch::orderBy('name_ar')->get(['id', 'name_ar']),
            'programs' => TrainingProgram::orderBy('name_ar')->get(['id', 'name_ar']),
            'schemaKind' => $this->ruleType ? ConditionSchema::kind($this->ruleType) : null,
            'schemaUnit' => $this->ruleType ? ConditionSchema::unit($this->ruleType) : null,
            'ruleTypes'  => PricingRuleType::cases(),
        ]);
    }
}
