<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Ticket;
use App\Models\TicketMessage;
use App\Models\User;
use App\Services\TicketNotificationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;

class TicketController extends Controller
{
    public function __construct(private readonly TicketNotificationService $ticketNotifications)
    {
        $this->middleware('auth');
    }

    public function index(Request $request): View
    {
        // Used for counts across all statuses while respecting non-status filters.
        $countsBaseQuery = Ticket::query();
        $query = Ticket::with(['customer', 'order', 'lastReplyBy', 'assignee']);

        if ($request->filled('status')) {
            $status = $request->string('status')->toString();
            $query->where('status', $status);
        }
        if ($request->filled('priority')) {
            $priority = $request->string('priority')->toString();
            $countsBaseQuery->where('priority', $priority);
            $query->where('priority', $priority);
        }
        if ($request->filled('order_id')) {
            $orderId = (int) $request->input('order_id');
            $countsBaseQuery->where('order_id', $orderId);
            $query->where('order_id', $orderId);
        }
        if ($request->filled('customer')) {
            $customer = trim((string) $request->input('customer'));
            $customerScope = function ($q) use ($customer) {
                $q->where('name', 'like', "%{$customer}%")
                    ->orWhere('mobile', 'like', "%{$customer}%");
            };
            $countsBaseQuery->whereHas('customer', $customerScope);
            $query->whereHas('customer', function ($q) use ($customer) {
                $q->where('name', 'like', "%{$customer}%")
                    ->orWhere('mobile', 'like', "%{$customer}%");
            });
        }

        $statusTabs = [
            Ticket::STATUS_OPEN_UNASSIGNED => 'Open (Unassigned)',
            Ticket::STATUS_ASSIGNED => 'Assigned',
            Ticket::STATUS_IN_PROGRESS => 'In Progress',
            Ticket::STATUS_ANSWERED => 'Answered',
            Ticket::STATUS_REOPENED => 'Reopened',
            Ticket::STATUS_CLOSED_BY_CUSTOMER => 'Closed',
        ];

        $statusCounts = [
            'total' => (clone $countsBaseQuery)->count(),
        ];
        foreach (array_keys($statusTabs) as $tabStatus) {
            $statusCounts[$tabStatus] = (clone $countsBaseQuery)->where('status', $tabStatus)->count();
        }

        $autoCloseStats = [
            'today' => 0,
            'last_7_days' => 0,
        ];

        if (Schema::hasColumn('tickets', 'auto_closed_at') && Schema::hasColumn('tickets', 'auto_close_reason')) {
            $autoCloseQuery = Ticket::query()
                ->whereNotNull('auto_closed_at')
                ->where('auto_close_reason', 'no_customer_reply_timeout');

            $autoCloseStats['today'] = (clone $autoCloseQuery)
                ->where('auto_closed_at', '>=', now()->startOfDay())
                ->count();

            $autoCloseStats['last_7_days'] = (clone $autoCloseQuery)
                ->where('auto_closed_at', '>=', now()->subDays(7))
                ->count();
        }

        $tickets = $query
            ->orderByRaw("CASE status WHEN 'open_unassigned' THEN 1 WHEN 'assigned' THEN 2 WHEN 'in_progress' THEN 3 WHEN 'reopened' THEN 4 WHEN 'answered' THEN 5 ELSE 6 END")
            ->orderByDesc('last_reply_at')
            ->orderByDesc('id')
            ->paginate(15)
            ->withQueryString();

        return view('admin.tickets.index', compact('tickets', 'statusTabs', 'statusCounts', 'autoCloseStats'));
    }

    public function show(Ticket $ticket): View
    {
        $this->authorize('view', $ticket);

        $ticket->load([
            'order',
            'customer',
            'assignee',
            'assignedBy',
            'messages' => fn ($query) => $query->with('sender')->orderBy('id'),
        ]);
        $staffUsers = User::whereIn('user_type', ['admin', 'staff'])->orderBy('name')->get(['id', 'name', 'user_type']);

        return view('admin.tickets.show', compact('ticket', 'staffUsers'));
    }

    public function reply(Request $request, Ticket $ticket): RedirectResponse
    {
        $this->authorize('reply', $ticket);

        if ($ticket->isClosed()) {
            return back()->with('error', 'Closed tickets cannot be replied to. Ask customer to raise a new ticket.');
        }

        $validated = $request->validate([
            'message' => 'required|string|min:2|max:5000',
            'is_internal' => 'nullable|boolean',
        ]);

        $isInternal = (bool) ($validated['is_internal'] ?? false);
        if (!$isInternal && is_null($ticket->assigned_to)) {
            return back()->with('error', 'Pick or assign this ticket before sending a reply.');
        }

        $nextStatus = $ticket->status;
        if (!$isInternal) {
            $nextStatus = Ticket::STATUS_ANSWERED;
        }

        DB::transaction(function () use ($ticket, $validated, $isInternal, $nextStatus) {
            TicketMessage::create([
                'ticket_id' => $ticket->id,
                'sender_id' => Auth::id(),
                'sender_type' => TicketMessage::SENDER_ADMIN,
                'message' => $validated['message'],
                'is_internal' => $isInternal,
            ]);

            $ticket->update([
                'status' => $nextStatus,
                '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),
            ]);
        });

        if (!$isInternal) {
            $freshTicket = $ticket->fresh();
            $this->ticketNotifications->notifyCustomerForReply($freshTicket, [
                'ticket_id' => $freshTicket->id,
                'last_reply_at' => optional($freshTicket->last_reply_at)->timestamp,
                'status' => $freshTicket->status,
            ]);
        }

        return back()->with('success', $isInternal ? 'Internal note added.' : 'Reply sent to customer.');
    }

    public function updateStatus(Request $request, Ticket $ticket): RedirectResponse
    {
        $this->authorize('updateStatus', $ticket);

        if ($ticket->isClosed()) {
            return back()->with('error', 'Closed tickets cannot be updated.');
        }

        $validated = $request->validate([
            'status' => 'required|in:assigned,in_progress,answered,reopened',
        ]);

        $status = $validated['status'];
        if (is_null($ticket->assigned_to) && in_array($status, [Ticket::STATUS_IN_PROGRESS, Ticket::STATUS_ANSWERED], true)) {
            return back()->with('error', 'Assign this ticket first.');
        }

        $oldStatus = $ticket->status;
        $oldAssignee = $ticket->assigned_to;
        $ticket->update([
            'status' => $status,
            'customer_reminder_sent_at' => null,
            '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),
            'last_replied_by' => Auth::id(),
        ]);

        if ($status !== $oldStatus) {
            $freshTicket = $ticket->fresh();
            $this->ticketNotifications->notifyCustomerForStatus($freshTicket, [
                'ticket_id' => $freshTicket->id,
                'old_status' => $oldStatus,
                'new_status' => $status,
                'updated_by' => Auth::id(),
            ]);
        }

        return back()->with('success', 'Ticket status updated.');
    }

    public function pick(Ticket $ticket): RedirectResponse
    {
        $this->authorize('assign', $ticket);

        $updatedRows = Ticket::query()
            ->whereKey($ticket->id)
            ->whereNull('assigned_to')
            ->where('status', Ticket::STATUS_OPEN_UNASSIGNED)
            ->update([
                'assigned_to' => Auth::id(),
                'assigned_by' => Auth::id(),
                'assigned_at' => now(),
                'status' => Ticket::STATUS_ASSIGNED,
                'customer_reminder_sent_at' => null,
                'auto_closed_at' => null,
                'auto_close_reason' => null,
                'active_ticket_key' => $this->activeTicketKey($ticket->order_id, $ticket->customer_id),
                'updated_at' => now(),
            ]);

        if ($updatedRows === 0) {
            return back()->with('error', 'Ticket is already picked or no longer open-unassigned.');
        }

        $freshTicket = $ticket->fresh();
        $this->ticketNotifications->notifyCustomerForStatus($freshTicket, [
            'ticket_id' => $freshTicket->id,
            'old_status' => Ticket::STATUS_OPEN_UNASSIGNED,
            'new_status' => Ticket::STATUS_ASSIGNED,
            'updated_by' => Auth::id(),
            'event' => 'picked',
        ]);

        return back()->with('success', 'Ticket picked successfully.');
    }

    public function assign(Request $request, Ticket $ticket): RedirectResponse
    {
        $this->authorize('assign', $ticket);

        $validated = $request->validate([
            'assigned_to' => 'required|exists:users,id',
        ]);

        $assignee = User::findOrFail((int) $validated['assigned_to']);
        if (!in_array($assignee->user_type, ['admin', 'staff'], true)) {
            return back()->with('error', 'Ticket can be assigned only to admin/staff users.');
        }

        $oldStatus = $ticket->status;
        $oldAssignee = $ticket->assigned_to;
        $ticket->update([
            'assigned_to' => $assignee->id,
            'assigned_by' => Auth::id(),
            'assigned_at' => now(),
            'status' => in_array($ticket->status, [Ticket::STATUS_OPEN_UNASSIGNED, Ticket::STATUS_REOPENED], true)
                ? Ticket::STATUS_ASSIGNED
                : $ticket->status,
            'customer_reminder_sent_at' => null,
            'auto_closed_at' => null,
            'auto_close_reason' => null,
            'active_ticket_key' => $this->activeTicketKey($ticket->order_id, $ticket->customer_id),
        ]);

        $freshTicket = $ticket->fresh();
        if ($freshTicket->status !== $oldStatus || (int) $freshTicket->assigned_to !== (int) $oldAssignee) {
            $this->ticketNotifications->notifyCustomerForStatus($freshTicket, [
                'ticket_id' => $freshTicket->id,
                'old_status' => $oldStatus,
                'new_status' => $freshTicket->status,
                'updated_by' => Auth::id(),
                'event' => 'assigned',
            ]);
        }

        return back()->with('success', 'Ticket assigned successfully.');
    }

    private function activeTicketKey(int $orderId, int $customerId): string
    {
        return $orderId . ':' . $customerId;
    }
}
