<?php

namespace Modules\Bms\Http\Controllers\Api;

use Illuminate\Http\Request;
use Modules\Bms\Entities\BmsApiToken;
use Modules\Bms\Entities\BmsClientOtp;
use Modules\Bms\Services\Contracts\ContactServiceContract;
use Modules\Bms\Services\Contracts\NotificationServiceContract;

/**
 * Client verification — OTP issued only at the commitment moment (visit
 * booking / portal access), never to enter the funnel. A verified phone
 * yields a client bearer token.
 */
class ClientAuthController extends ApiController
{
    /** Send a one-time code by phone (WhatsApp/SMS) or email. */
    public function otpRequest(Request $request, NotificationServiceContract $notify)
    {
        $data = $request->validate([
            'channel' => 'nullable|in:phone,email',
            'phone'   => 'required_without:email|nullable|string|max:30',
            'email'   => 'required_without:phone|nullable|email|max:191',
        ]);
        $businessId = $this->publicBusinessId($request);
        $channel = ! empty($data['email']) && ($data['channel'] ?? 'email') === 'email' ? 'email' : 'phone';

        $code = (string) random_int(100000, 999999);
        BmsClientOtp::create([
            'business_id' => $businessId,
            'phone'       => $data['phone'] ?? null,
            'email'       => $data['email'] ?? null,
            'channel'     => $channel,
            'code'        => $code,
            'expires_at'  => now()->addMinutes((int) config('bms.otp_ttl_minutes', 10)),
        ]);

        if ($channel === 'email' && ! empty($data['email'])) {
            try {
                \Mail::raw("Your verification code is {$code}", function ($m) use ($data) {
                    $m->to($data['email'])->subject('Your verification code');
                });
            } catch (\Throwable $e) {
                \Log::warning('[BMS] email OTP send failed: ' . $e->getMessage());
            }
        } else {
            $notify->send($businessId, [
                'to'       => $data['phone'],
                'channels' => ['whatsapp', 'sms'],
                'body'     => "Your verification code is {$code}",
            ]);
        }

        $payload = ['sent' => true];
        if (config('app.debug')) {
            $payload['dev_code'] = $code; // dev convenience only
        }

        return $this->ok($payload);
    }

    /** Verify a phone- or email-delivered code and issue a client bearer token. */
    public function otpVerify(Request $request, ContactServiceContract $contacts)
    {
        $data = $request->validate([
            'phone' => 'required_without:email|nullable|string|max:30',
            'email' => 'required_without:phone|nullable|email|max:191',
            'code'  => 'required|string|max:10',
        ]);
        $businessId = $this->publicBusinessId($request);

        $otp = BmsClientOtp::where('business_id', $businessId)
            ->when(! empty($data['email']), fn ($q) => $q->where('email', $data['email']))
            ->when(empty($data['email']), fn ($q) => $q->where('phone', $data['phone']))
            ->whereNull('consumed_at')
            ->latest()->first();

        if (! $otp || ! $otp->isValid()) {
            return $this->fail('Code expired or invalid. Request a new one.', 422);
        }

        $otp->increment('attempts');
        if (! hash_equals($otp->code, $data['code'])) {
            return $this->fail('Incorrect code.', 422);
        }

        $otp->forceFill(['consumed_at' => now()])->save();

        $contact = $contacts->findOrCreateCustomer($businessId, [
            'phone' => $data['phone'] ?? null,
            'email' => $data['email'] ?? null,
            'name'  => $request->input('name'),
        ]);
        return $this->issueFor($businessId, $contact);
    }

    /** Email + password sign-in (portal password must have been set first). */
    public function passwordLogin(Request $request)
    {
        $data = $request->validate([
            'email'    => 'required|email|max:191',
            'password' => 'required|string|max:191',
        ]);
        $businessId = $this->publicBusinessId($request);

        $contact = \App\Contact::where('business_id', $businessId)
            ->where('email', $data['email'])
            ->whereNotNull('bms_portal_password')
            ->first();

        if (! $contact || ! \Hash::check($data['password'], $contact->bms_portal_password)) {
            return $this->fail('Invalid email or password.', 422);
        }

        return $this->issueFor($businessId, $contact);
    }

    /** Set/change the portal password for the signed-in client. */
    public function passwordSet(Request $request)
    {
        $data = $request->validate(['password' => 'required|string|min:6|max:191']);
        $contact = $request->attributes->get('bms_owner');
        if (! $contact) {
            return $this->fail('Unauthenticated.', 401);
        }
        $contact->forceFill(['bms_portal_password' => \Hash::make($data['password'])])->save();

        return $this->ok(['set' => true]);
    }

    /** Whether the one-tap test sign-in is available (testing only). */
    protected function testLoginEnabled(): bool
    {
        return (bool) (config('bms.test_login') || config('app.debug'));
    }

    /** The seeded demo client used for test sign-in (richest mock data first). */
    protected function testContact(int $businessId)
    {
        $cid = \DB::table('bms_bookings')->where('business_id', $businessId)->value('contact_id');
        $contact = $cid ? \App\Contact::find($cid) : null;
        if (! $contact) {
            $contact = \App\Contact::where('business_id', $businessId)->where('type', 'customer')
                ->whereNotNull('mobile')->orderBy('id')->first();
        }
        return $contact;
    }

    /** Expose the seeded test client so the PWA can show a one-tap sign-in. */
    public function devCredentials(Request $request)
    {
        if (! $this->testLoginEnabled()) {
            abort(404);
        }
        $contact = $this->testContact($this->publicBusinessId($request));
        if (! $contact) {
            return $this->fail('No demo client available.', 404);
        }
        return $this->ok([
            'available'   => true,
            'credentials' => ['name' => $contact->name, 'phone' => $contact->mobile, 'email' => $contact->email],
        ]);
    }

    /** One-tap sign-in as the seeded test client (testing only). */
    public function devLogin(Request $request)
    {
        if (! $this->testLoginEnabled()) {
            abort(404);
        }
        $businessId = $this->publicBusinessId($request);
        $contact = $this->testContact($businessId);
        if (! $contact) {
            return $this->fail('No demo client available.', 404);
        }
        return $this->issueFor($businessId, $contact);
    }

    /** Issue a client bearer token + contact summary. */
    protected function issueFor(int $businessId, $contact)
    {
        $token = BmsApiToken::issue($businessId, 'client', $contact->id);

        return $this->ok([
            'token'   => $token->token,
            'contact' => ['id' => $contact->id, 'name' => $contact->name, 'mobile' => $contact->mobile],
        ]);
    }
}
