<?php

namespace App\Http\Requests\Admin\Manage;

use App\Enums\CuttingStatus;
use App\Enums\Permission;
use App\Enums\PriceType;
use App\Enums\Role;
use App\Models\Cutting;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;

class CuttingStatusTransitionRequest extends FormRequest
{
    public function authorize(): bool
    {
        $status = CuttingStatus::tryFrom((string) $this->input('status'));

        if ($status === null) {
            return false;
        }

        if ($status === CuttingStatus::IN_PROGRESS) {
            return $this->user()?->can(Permission::CUTTINGS_REJECT->value)
                || $this->user()?->can(Permission::CUTTINGS_UPDATE->value)
                || false;
        }

        try {
            $permission = $status->transitionPermission();
        } catch (\InvalidArgumentException) {
            return false;
        }

        return $this->user()?->can($permission->value) ?? false;
    }

    /**
     * @return array<string, mixed>
     */
    public function rules(): array
    {
        return [
            'status' => ['required', Rule::enum(CuttingStatus::class)],
            'reason' => ['nullable', 'string', 'max:500'],
            'verification_note' => ['nullable', 'string', 'max:500'],
            'results' => ['nullable', 'array'],
            'results.*.product_variant_id' => ['required_with:results', 'integer', 'exists:product_variants,id'],
            'results.*.sample' => ['required_with:results', 'integer', 'min:0'],
            'results.*.original_outside_sample' => ['required_with:results', 'integer', 'min:0'],
            'result_prices' => ['nullable', 'array'],
            'result_prices.*.product_variant_id' => ['required_with:result_prices', 'integer', 'exists:product_variants,id'],
            'result_prices.*.prices' => ['required_with:result_prices', 'array', 'min:1'],
            'result_prices.*.prices.*.type' => ['required', Rule::enum(PriceType::class)],
            'result_prices.*.prices.*.price' => ['required', 'integer', 'gt:0'],
        ];
    }

    /**
     * @return array<string, string>
     */
    public function attributes(): array
    {
        return [
            'status' => 'status',
            'reason' => 'alasan penolakan',
            'verification_note' => 'catatan verifikasi',
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $validator): void {
            /** @var Cutting $cutting */
            $cutting = $this->route('cutting');
            $status = CuttingStatus::tryFrom((string) $this->input('status'));

            if ($status === null) {
                return;
            }

            if (! $cutting->status->canTransitionTo($status)) {
                $validator->errors()->add('status', 'Status cutting tidak dapat diubah.');
            }

            // Only owner/developer can verify directly from COMPLETED status
            if ($cutting->status === CuttingStatus::COMPLETED && $status === CuttingStatus::VERIFIED) {
                $user = $this->user();
                if (! $user || ! $user->hasRole([Role::OWNER->value, Role::DEVELOPER->value])) {
                    $validator->errors()->add('status', 'Hanya owner yang dapat langsung memverifikasi cutting.');
                }
            }

            if ($status === CuttingStatus::REJECTED && trim((string) $this->input('reason')) === '') {
                $validator->errors()->add('reason', 'Alasan penolakan wajib diisi.');
            }

            if ($status === CuttingStatus::VERIFIED) {
                if ($this->has('results')) {
                    $cuttingResults = $cutting->results->keyBy('product_variant_id');
                    foreach ($this->input('results', []) as $index => $item) {
                        $variantId = $item['product_variant_id'] ?? 0;
                        $sample = (int) ($item['sample'] ?? 0);
                        $originalOutsideSample = (int) ($item['original_outside_sample'] ?? 0);

                        $originalResult = $cuttingResults->get($variantId);
                        if ($originalResult === null) {
                            $validator->errors()->add("results.{$index}.product_variant_id", 'Varian produk tidak ditemukan pada cutting ini.');

                            continue;
                        }

                        if (($sample + $originalOutsideSample) !== (int) $originalResult->cutting_result) {
                            $validator->errors()->add("results.{$index}.sample", "Total jumlah (sample + hasil cutting diluar sample) harus sama dengan hasil cutting asli ({$originalResult->cutting_result} pcs).");
                        }
                    }
                }

                if (! $this->has('result_prices') || $this->input('result_prices') === []) {
                    $validator->errors()->add('result_prices', 'Harga jual wajib diisi saat verifikasi.');
                } else {
                    $variantIds = $cutting->results->pluck('product_variant_id')->all();
                    $submittedVariantIds = collect($this->input('result_prices', []))
                        ->pluck('product_variant_id')
                        ->map(fn ($id) => (int) $id)
                        ->all();

                    foreach ($variantIds as $variantId) {
                        if (! in_array($variantId, $submittedVariantIds, true)) {
                            $validator->errors()->add('result_prices', 'Harga jual wajib diisi untuk semua varian hasil cutting.');
                            break;
                        }
                    }
                }
            }
        });
    }
}
