<?php

namespace App\Http\Requests\Admin;

use App\Models\Terminal;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

/**
 * Validates POST /admin/terminals (create) and PATCH /admin/terminals/{id}.
 * Terminal `code` is unique within its store (two stores can both have a
 * "POS-1"); the AJAX status toggle reuses this request, so `code`/`name`
 * stay `sometimes`-friendly via persistedAttributes().
 */
class TerminalRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    /** @return array<string, mixed> */
    public function rules(): array
    {
        $terminal = $this->route('terminal');
        $id       = $terminal instanceof Terminal ? $terminal->id : null;

        // The store a unique-code check scopes to: the submitted store on
        // create, or the terminal's existing store on edit/toggle (the
        // toggle PATCH doesn't resend store_id).
        $storeId = (int) ($this->input('store_id')
            ?: ($terminal instanceof Terminal ? $terminal->store_id : 0));

        return [
            'store_id' => [
                $id ? 'sometimes' : 'required',
                'integer',
                Rule::exists('stores', 'id')->whereNull('deleted_at'),
            ],
            'name' => ['required', 'string', 'max:100'],
            'code' => [
                'nullable',
                'string',
                'max:32',
                'regex:/^[A-Za-z0-9._-]+$/',
                Rule::unique('terminals', 'code')
                    ->where(fn ($q) => $q->where('store_id', $storeId))
                    ->ignore($id),
            ],
            'is_active' => ['sometimes', 'boolean'],

            // Hardware config (only sent by the full editor, never by the
            // status-toggle PATCH — see persistedAttributes()).
            'printer_mode'        => ['sometimes', 'in:browser_print,webusb,network,none'],
            'paper_width'         => ['sometimes', 'in:58mm,80mm,a4'],
            'cut_paper'           => ['sometimes', 'boolean'],
            'open_drawer_on_cash' => ['sometimes', 'boolean'],
            'drawer_pin'          => ['sometimes', 'in:2,5'],
            'webusb_vendor_id'    => ['nullable', 'string', 'regex:/^0x[0-9a-fA-F]{1,4}$/'],
            'webusb_product_id'   => ['nullable', 'string', 'regex:/^0x[0-9a-fA-F]{1,4}$/'],
        ];
    }

    /** @return array<string, string> */
    public function messages(): array
    {
        return [
            'code.regex'  => __('terminals.errors.code_format'),
            'code.unique' => __('terminals.errors.code_taken'),
        ];
    }

    /**
     * Map the validated payload to persisted columns. A blank code is
     * auto-derived from the name (uppercased, hyphenated) so the operator
     * never has to invent one.
     *
     * @return array<string, mixed>
     */
    public function persistedAttributes(): array
    {
        $data = $this->validated();

        if (empty($data['code'])) {
            $base = Str::upper(Str::slug($data['name'] ?? 'pos')) ?: 'POS';
            $data['code'] = Str::limit($base, 26, '').'-'.Str::upper(Str::random(4));
        } else {
            $data['code'] = trim($data['code']);
        }

        $data['is_active'] = $this->boolean('is_active', true);

        // Strip the flat hardware fields back out of the column payload and
        // fold them into the `default_printer_config` JSON — but ONLY when
        // the editor actually sent them. The status-toggle PATCH omits
        // `printer_mode`, so a toggle never clobbers a saved hardware config.
        foreach (['printer_mode', 'paper_width', 'cut_paper', 'open_drawer_on_cash', 'drawer_pin', 'webusb_vendor_id', 'webusb_product_id'] as $k) {
            unset($data[$k]);
        }

        if ($this->has('printer_mode')) {
            $data['default_printer_config'] = $this->buildPrinterConfig();
        }

        return $data;
    }

    /** @return array<string, mixed> */
    private function buildPrinterConfig(): array
    {
        $mode  = $this->input('printer_mode', 'browser_print');
        $paper = $this->input('paper_width', '80mm');
        $open  = $this->boolean('open_drawer_on_cash');

        $receipt = [
            'mode'                => $mode,
            'paper_width'         => $paper,
            'cut_paper'           => $this->boolean('cut_paper'),
            'open_drawer_on_cash' => $open,
            'drawer_pin'          => ((int) $this->input('drawer_pin')) === 5 ? 5 : 2,
        ];

        // Stash the paired device descriptor so the cashier's print bridge
        // can reconnect to the same printer without re-prompting.
        $vid = $this->input('webusb_vendor_id');
        $pid = $this->input('webusb_product_id');
        if ($mode === 'webusb' && $vid && $pid) {
            $receipt['webusb_device_descriptor'] = [
                'vendor_id'  => $vid,
                'product_id' => $pid,
            ];
        }

        return [
            'receipt_printer' => $receipt,
            'cash_drawer'     => ['mode' => $open ? 'via_receipt_printer' : 'none'],
        ];
    }
}
