<?php

namespace App\Http\Controllers\Customer;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\Ticket;
use App\Models\TicketMessage;
use App\Services\TicketNotificationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class TicketController extends Controller
{
    public function __construct(private readonly TicketNotificationService $ticketNotifications)
    {
        $this->middleware('auth');
    }

    public function index(Request $request): View
    {
        $userId = Auth::id();
        $baseQuery = Ticket::where('customer_id', $userId);

        $counts = [
            'total' => (clone $baseQuery)->count(),
            'open' => (clone $baseQuery)->whereIn('status', [
                Ticket::STATUS_OPEN_UNASSIGNED,
                Ticket::STATUS_ASSIGNED,
                Ticket::STATUS_IN_PROGRESS,
                Ticket::STATUS_REOPENED,
            ])->count(),
            'answered' => (clone $baseQuery)->where('status', Ticket::STATUS_ANSWERED)->count(),
            'resolved' => (clone $baseQuery)->where('status', Ticket::STATUS_CLOSED_BY_CUSTOMER)->count(),
        ];

        $query = Ticket::with(['order', 'lastReplyBy'])
            ->where('customer_id', $userId);

        if ($request->filled('search')) {
            $query->where('subject', 'like', '%' . $request->string('search')->toString() . '%');
        }

        if ($request->filled('status')) {
            $query->where('status', $request->string('status')->toString());
        }

        $tickets = $query
            ->orderByDesc('last_reply_at')
            ->orderByDesc('id')
            ->paginate(10)
            ->withQueryString();

        return view('customer.tickets.index', compact('tickets', 'counts'));
    }

    public function create(Order $order): View|RedirectResponse
    {
        if ((int) $order->user_id !== (int) Auth::id()) {
            abort(403);
        }

        $existingActiveTicket = Ticket::where('order_id', $order->id)
            ->where('customer_id', Auth::id())
            ->whereIn('status', [
                Ticket::STATUS_OPEN_UNASSIGNED,
                Ticket::STATUS_ASSIGNED,
                Ticket::STATUS_IN_PROGRESS,
                Ticket::STATUS_ANSWERED,
                Ticket::STATUS_REOPENED,
            ])
            ->first();

        if ($existingActiveTicket) {
            return redirect()
                ->route('customer.tickets.show', $existingActiveTicket)
                ->with('error', 'An active ticket already exists for this order.');
        }

        return view('customer.tickets.create', compact('order'));
    }

    public function store(Request $request, Order $order): RedirectResponse
    {
        if ((int) $order->user_id !== (int) Auth::id()) {
            abort(403);
        }

        $validated = $request->validate([
            'subject' => 'required|string|max:191',
            'category' => 'required|string|in:order_issue,payment,delivery,return_refund,other',
            'priority' => 'required|string|in:low,medium,high,urgent',
            'message' => 'required|string|min:5|max:5000',
        ]);

        $activeKey = $this->activeTicketKey($order->id, (int) Auth::id());

        try {
            $ticket = DB::transaction(function () use ($order, $validated, $activeKey) {
                $existing = Ticket::where('active_ticket_key', $activeKey)->first();
                if ($existing) {
                    return $existing;
                }

                $ticket = Ticket::create([
                    'order_id' => $order->id,
                    'customer_id' => Auth::id(),
                    'subject' => $validated['subject'],
                    'category' => $validated['category'],
                    'priority' => $validated['priority'],
                    'status' => Ticket::STATUS_OPEN_UNASSIGNED,
                    'active_ticket_key' => $activeKey,
                    'last_reply_at' => now(),
                    'last_replied_by' => Auth::id(),
                ]);

                TicketMessage::create([
                    'ticket_id' => $ticket->id,
                    'sender_id' => Auth::id(),
                    'sender_type' => TicketMessage::SENDER_CUSTOMER,
                    'message' => $validated['message'],
                    'is_internal' => false,
                ]);

                return $ticket;
            });
        } catch (\Throwable $e) {
            return back()->withInput()->with('error', 'Unable to create ticket. Please try again.');
        }

        $this->ticketNotifications->notifyAdminsForNewTicket($ticket);

        return redirect()
            ->route('customer.tickets.show', $ticket)
            ->with('success', 'Ticket created successfully.');
    }

    public function show(Ticket $ticket): View
    {
        $this->ensureOwnTicket($ticket);
        $this->authorize('view', $ticket);

        $ticket->load([
            'order',
            'messages' => fn ($query) => $query
                ->where('is_internal', false)
                ->with('sender')
                ->orderBy('id'),
        ]);

        return view('customer.tickets.show', compact('ticket'));
    }

    public function reply(Request $request, Ticket $ticket): RedirectResponse
    {
        $this->ensureOwnTicket($ticket);
        $this->authorize('reply', $ticket);

        if ($ticket->isClosed()) {
            return back()->with('error', 'This ticket is closed. Please contact support to reopen.');
        }

        $validated = $request->validate([
            'message' => 'required|string|min:2|max:5000',
        ]);

        DB::transaction(function () use ($ticket, $validated) {
            TicketMessage::create([
                'ticket_id' => $ticket->id,
                'sender_id' => Auth::id(),
                'sender_type' => TicketMessage::SENDER_CUSTOMER,
                'message' => $validated['message'],
                'is_internal' => false,
            ]);

            $ticket->update([
                'status' => Ticket::STATUS_REOPENED,
                'last_reply_at' => now(),
                'customer_reminder_sent_at' => null,
                'last_replied_by' => Auth::id(),
                'closed_at' => null,
                'auto_closed_at' => null,
                'auto_close_reason' => null,
                'closed_by' => null,
                'active_ticket_key' => $this->activeTicketKey($ticket->order_id, $ticket->customer_id),
            ]);
        });

        $this->ticketNotifications->notifyAdminsForNewTicket($ticket->fresh());

        return back()->with('success', 'Reply sent successfully.');
    }

    private function activeTicketKey(int $orderId, int $customerId): string
    {
        return $orderId . ':' . $customerId;
    }

    public function close(Ticket $ticket): RedirectResponse
    {
        $this->ensureOwnTicket($ticket);
        $this->authorize('close', $ticket);

        $ticket->update([
            'status' => Ticket::STATUS_CLOSED_BY_CUSTOMER,
            'closed_at' => now(),
            'closed_by' => Auth::id(),
            'auto_closed_at' => null,
            'auto_close_reason' => null,
            'active_ticket_key' => null,
            'last_replied_by' => Auth::id(),
        ]);

        return back()->with('success', 'Ticket closed successfully.');
    }

    private function ensureOwnTicket(Ticket $ticket): void
    {
        if ((int) $ticket->customer_id !== (int) Auth::id()) {
            abort(403);
        }
    }
}
