<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Api\V1\Concerns\RespondsWithApi;
use App\Http\Controllers\Controller;
use App\Services\BookingService;
use App\Services\RefundPolicyService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class BookingController extends Controller
{
    use RespondsWithApi;

    public function index(Request $request)
    {
        return $this->ok(DB::table('bookings')->where('user_id', $request->user()->id)->latest('id')->paginate(20));
    }

    public function store(Request $request, BookingService $bookingService)
    {
        $data = $request->validate([
            'cart_id' => ['required', 'integer'],
            'quote_id' => ['required', 'integer'],
            'notes' => ['nullable', 'string', 'max:500'],
        ]);

        try {
            return $this->ok($bookingService->createFromQuote($data['cart_id'], $data['quote_id'], $request->user()->id, $data['notes'] ?? null), 'Booking created.', [], 201);
        } catch (\Throwable $e) {
            return $this->fail('BOOKING_CREATE_FAILED', $e->getMessage(), [], 422);
        }
    }

    public function show(Request $request, int $booking, BookingService $bookingService)
    {
        $exists = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->exists();

        return $exists ? $this->ok($bookingService->loadBooking($booking)) : $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
    }

    public function cancel(Request $request, int $booking, RefundPolicyService $refundPolicyService)
    {
        $data = $request->validate([
            'reason' => ['nullable', 'string', 'max:255'],
        ]);

        $row = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->first();

        if (! $row) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        if (in_array($row->status, ['completed', 'cancelled'], true)) {
            return $this->fail('BOOKING_STATE_LOCKED', 'Booking cannot be cancelled now.', [], 409);
        }

        $policy = $refundPolicyService->calculate($row, $data['reason'] ?? 'customer_cancelled');

        DB::transaction(function () use ($booking, $request, $data, $policy) {
            DB::table('bookings')->where('id', $booking)->update([
                'status' => 'cancelled',
                'cancellation_reason' => $data['reason'] ?? 'customer_cancelled',
                'cancelled_by_type' => 'customer',
                'cancelled_by_id' => $request->user()->id,
                'cancelled_at' => now(),
                'payment_status' => $policy['refund_amount'] > 0 ? 'partially_refunded' : DB::raw('payment_status'),
                'updated_at' => now(),
            ]);

            DB::table('booking_status_histories')->insert([
                'booking_id' => $booking,
                'from_status' => 'confirmed',
                'to_status' => 'cancelled',
                'actor_type' => 'customer',
                'actor_id' => $request->user()->id,
                'reason' => $data['reason'] ?? 'customer_cancelled',
                'metadata_json' => json_encode($policy),
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            if ($policy['refund_amount'] > 0) {
                DB::table('refunds')->insert([
                    'booking_id' => $booking,
                    'refund_number' => 'RF-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)),
                    'amount' => $policy['refund_amount'],
                    'reason' => $policy['policy'],
                    'status' => 'requested',
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        });

        return $this->ok([
            'booking' => DB::table('bookings')->where('id', $booking)->first(),
            'refund_policy' => $policy,
        ], 'Booking cancelled.');
    }

    public function reschedule(Request $request, int $booking)
    {
        $data = $request->validate([
            'scheduled_start_at' => ['required', 'date'],
            'reason' => ['nullable', 'string', 'max:255'],
        ]);

        $row = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->first();

        if (! $row) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        DB::table('bookings')->where('id', $booking)->update([
            'scheduled_start_at' => $data['scheduled_start_at'],
            'status' => 'rescheduled',
            'updated_at' => now(),
        ]);

        DB::table('booking_status_histories')->insert([
            'booking_id' => $booking,
            'from_status' => $row->status,
            'to_status' => 'rescheduled',
            'actor_type' => 'customer',
            'actor_id' => $request->user()->id,
            'reason' => $data['reason'] ?? 'Customer requested reschedule.',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        return $this->ok(DB::table('bookings')->where('id', $booking)->first(), 'Booking rescheduled.');
    }

    public function timeline(Request $request, int $booking)
    {
        $exists = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->exists();

        if (! $exists) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        return $this->ok(DB::table('booking_status_histories')->where('booking_id', $booking)->orderBy('id')->get());
    }

    public function professional(Request $request, int $booking)
    {
        $exists = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->exists();

        if (! $exists) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        $assignment = DB::table('booking_assignments')
            ->join('professionals', 'professionals.id', '=', 'booking_assignments.professional_id')
            ->leftJoin('users', 'users.id', '=', 'professionals.user_id')
            ->where('booking_assignments.booking_id', $booking)
            ->select('booking_assignments.*', 'professionals.pro_code', 'professionals.rating_avg', 'users.name', 'users.phone')
            ->latest('booking_assignments.id')
            ->first();

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

    public function rating(Request $request, int $booking)
    {
        $data = $request->validate([
            'rating' => ['required', 'integer', 'min:1', 'max:5'],
            'review_text' => ['nullable', 'string', 'max:1000'],
        ]);

        $row = DB::table('bookings')->where('id', $booking)->where('user_id', $request->user()->id)->first();

        if (! $row) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        $assignment = DB::table('booking_assignments')->where('booking_id', $booking)->latest('id')->first();

        $id = DB::table('reviews')->insertGetId([
            'booking_id' => $booking,
            'user_id' => $request->user()->id,
            'professional_id' => $assignment?->professional_id,
            'rating' => $data['rating'],
            'review_text' => $data['review_text'] ?? null,
            'status' => 'visible',
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        return $this->ok(DB::table('reviews')->where('id', $id)->first(), 'Review submitted.', [], 201);
    }
}
