<?php

namespace App\Http\Controllers;

use App\Models\Attachment;
use App\Models\Comment;
use App\Models\Ticket;
use App\Models\User;
use App\Support\OrganizationScope;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule;

class AgentController extends Controller
{
    private function userRole(?User $user): string
    {
        return $user?->supportDeskRole() ?? (string) ($user?->role ?? 'user');
    }

    private function ticketStatuses(): array
    {
        return class_exists(\App\Support\TicketStatus::class)
            ? \App\Support\TicketStatus::all()
            : ['open', 'in-progress', 'waiting-customer', 'waiting-internal', 'resolved', 'closed'];
    }

    private function isClosedStatus(string $status): bool
    {
        return strtolower($status) === 'closed';
    }

    private function transitionBlocked(Ticket $ticket, string $nextStatus): bool
    {
        if (class_exists(\App\Support\TicketStatus::class)) {
            return \App\Support\TicketStatus::transitionBlocked($ticket, $nextStatus);
        }

        return $this->isClosedStatus((string) $ticket->status) && !$this->isClosedStatus($nextStatus);
    }

    private function closedTicketMessage(): string
    {
        return class_exists(\App\Support\TicketStatus::class)
            ? \App\Support\TicketStatus::closedTicketMessage()
            : 'Closed tickets cannot be reopened.';
    }

    private function isSuperAdmin(?User $user): bool
    {
        if (class_exists(OrganizationScope::class)) {
            return OrganizationScope::isSuperAdmin($user);
        }

        return $user?->supportDeskIsSuperAdmin() ?? false;
    }

    private function organizationFor(?User $user): ?string
    {
        if (class_exists(OrganizationScope::class)) {
            return OrganizationScope::organizationFor($user);
        }

        $organization = trim((string) ($user?->department ?? ''));

        return $organization !== '' ? $organization : null;
    }

    private function applyAdminTicketScope($query, ?User $user)
    {
        if (class_exists(OrganizationScope::class)) {
            return OrganizationScope::applyTicketScope($query, $user);
        }

        if (!$user || $this->userRole($user) !== 'admin' || $this->isSuperAdmin($user)) {
            return $query;
        }

        $organization = $this->organizationFor($user);

        return $organization ? $query->where('department', $organization) : $query->whereRaw('1 = 0');
    }

    private function commentRelations(): array
    {
        $relations = [
            'author' => fn ($q) => $q->select(User::supportDeskSelectColumns()),
        ];

        if (Attachment::supportsCommentAttachments()) {
            $relations[] = 'attachments';
        }

        return $relations;
    }

    public function stats(Request $request)
    {
        $user = $request->user();
        $agentId = $user ? $user->id : null;
        $agentCategory = $user ? $user->category : null;
        $isAdmin = $user && $this->userRole($user) === 'admin';
        $now = now();
        $today = now()->startOfDay();

        $ticketQuery = Ticket::query();
        if ($isAdmin) {
            $this->applyAdminTicketScope($ticketQuery, $user);
        } else {
            $ticketQuery->where(function ($q) use ($agentId, $agentCategory) {
                $q->where('assigned_to_id', $agentId);
                if ($agentCategory) {
                    $q->orWhere('category', $agentCategory);
                }
            });
        }
        $agentTickets = $ticketQuery->get();

        $myOpenTickets = $agentTickets->whereIn('status', ['open', 'in-progress', 'waiting-customer', 'waiting-internal'])->count();
        $urgentTickets = $agentTickets->filter(fn ($t) => strtolower($t->priority) === 'urgent')->count();
        $overdueTickets = $agentTickets->filter(fn ($t) => $t->sla_due && $t->sla_due->lt($now) && in_array($t->status, ['open', 'in-progress', 'waiting-customer', 'waiting-internal'], true))->count();
        $resolvedToday = $agentTickets->filter(fn ($t) => $t->status === 'resolved' && $t->updated_at->gte($today))->count();

        $responseQuery = Ticket::query();
        if ($isAdmin) {
            $this->applyAdminTicketScope($responseQuery, $user);
        } else {
            $responseQuery->where(function ($q) use ($agentId, $agentCategory) {
                $q->where('assigned_to_id', $agentId);
                if ($agentCategory) {
                    $q->orWhere('category', $agentCategory);
                }
            });
        }
        $ticketsWithResponse = $responseQuery
            ->with(['comments' => fn ($q) => $q->orderBy('created_at')])
            ->get();

        $responseTimes = $ticketsWithResponse
            ->filter(fn ($ticket) => $ticket->comments->isNotEmpty())
            ->map(fn ($ticket) => $ticket->comments->first()->created_at->diffInSeconds($ticket->created_at));

        $averageResponseTime = $responseTimes->count() ? round(($responseTimes->avg() / 3600), 1) : 0;
        $firstResponseEligible = $agentTickets->filter(fn ($t) => $t->first_response_sla);
        $firstResponseMetCount = $firstResponseEligible->filter(fn ($t) => (bool) $t->first_response_met)->count();
        $resolutionEligible = $agentTickets->filter(fn ($t) => $t->resolution_sla);
        $resolutionMetCount = $resolutionEligible
            ->filter(function ($ticket) {
                if (!in_array($ticket->status, ['resolved', 'closed'], true)) {
                    return false;
                }

                return $ticket->updated_at && $ticket->resolution_sla && $ticket->updated_at->lte($ticket->resolution_sla);
            })
            ->count();
        $totalCheckpoints = $firstResponseEligible->count() + $resolutionEligible->count();
        $metCheckpoints = $firstResponseMetCount + $resolutionMetCount;
        $slaCompliance = $totalCheckpoints > 0 ? round(($metCheckpoints / $totalCheckpoints) * 100) : 100;

        return response()->json(compact(
            'myOpenTickets', 'urgentTickets', 'overdueTickets', 'resolvedToday', 'averageResponseTime', 'slaCompliance'
        ));
    }

    public function tickets(Request $request)
    {
        $agentId = $request->user()->id;
        $agentCategory = $request->user()->category;
        $isAdmin = $this->userRole($request->user()) === 'admin';
        $filter = $request->query('filter', 'my-tickets');
        $query = Ticket::query();

        switch ($filter) {
            case 'my-tickets':
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        if ($agentCategory) {
                            $q->orWhere('category', $agentCategory);
                        }
                    });
                }
                break;
            case 'unassigned':
                $query->whereNull('assigned_to_id');
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } elseif ($agentCategory) {
                    $query->where('category', $agentCategory);
                }
                break;
            case 'all-tickets':
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        $q->orWhere(function ($q2) use ($agentCategory) {
                            $q2->whereNull('assigned_to_id');
                            if ($agentCategory) {
                                $q2->where('category', $agentCategory);
                            }
                        });
                    });
                }
                break;
            case 'urgent':
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                    $query->whereRaw('LOWER(priority) = ?', ['urgent']);
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        $q->orWhere(function ($q2) use ($agentCategory) {
                            $q2->whereNull('assigned_to_id');
                            if ($agentCategory) {
                                $q2->where('category', $agentCategory);
                            }
                        });
                    })->whereRaw('LOWER(priority) = ?', ['urgent']);
                }
                break;
            case 'overdue':
                $query->where('sla_due', '<', now())
                      ->whereIn('status', ['open', 'in-progress', 'waiting-customer', 'waiting-internal']);
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        $q->orWhere(function ($q2) use ($agentCategory) {
                            $q2->whereNull('assigned_to_id');
                            if ($agentCategory) {
                                $q2->where('category', $agentCategory);
                            }
                        });
                    });
                }
                break;
            case 'waiting':
                $query->where('status', 'waiting-customer');
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        $q->orWhere(function ($q2) use ($agentCategory) {
                            $q2->whereNull('assigned_to_id');
                            if ($agentCategory) {
                                $q2->where('category', $agentCategory);
                            }
                        });
                    });
                }
                break;
            case 'resolved':
                $query->whereIn('status', ['resolved', 'closed']);
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where('assigned_to_id', $agentId);
                }
                break;
            default:
                if ($isAdmin) {
                    $this->applyAdminTicketScope($query, $request->user());
                } else {
                    $query->where(function ($q) use ($agentId, $agentCategory) {
                        $q->where('assigned_to_id', $agentId);
                        $q->orWhere(function ($q2) use ($agentCategory) {
                            $q2->whereNull('assigned_to_id');
                            if ($agentCategory) {
                                $q2->where('category', $agentCategory);
                            }
                        });
                    });
                }
        }

        return $query
            ->with([
                'assignedToRelation' => fn ($q) => $q->select(User::supportDeskSelectColumns()),
                'requesterUserRelation' => fn ($q) => $q->select(User::supportDeskSelectColumns()),
                'comments' => fn ($q) => $q->with($this->commentRelations())->orderBy('created_at'),
                'agents',
            ])
            ->orderByDesc('updated_at')
            ->orderByDesc('created_at')
            ->orderByRaw("CASE LOWER(priority) WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 WHEN 'medium' THEN 3 WHEN 'low' THEN 4 ELSE 5 END")
            ->get();
    }

    public function updateStatus(Request $request, string $ticketId)
    {
        try {
            Log::debug('updateStatus request', [
                'ticketId' => $ticketId,
                'user' => $request->user(),
                'body' => $request->all(),
            ]);

            $validated = $request->validate([
                'status' => ['required', Rule::in($this->ticketStatuses())],
            ]);

            $ticket = Ticket::findOrFail($ticketId);
            $user = $request->user();
            $userId = (string) $user->id;
            $assignedToId = (string) $ticket->assigned_to_id;
            // Check if agent has access: assigned directly, in agents pivot, or category match
            if ($this->userRole($user) !== 'admin') {
                $hasAccess = $assignedToId === $userId
                    || $ticket->agents()->where('users.id', $user->id)->exists()
                    || ($user->category && $ticket->category === $user->category);

                if (!$hasAccess) {
                    Log::debug('updateStatus: agent has no access', ['userId' => $userId, 'assignedToId' => $assignedToId, 'ticketCategory' => $ticket->category, 'userCategory' => $user->category]);
                    return response()->json(['message' => 'Agent does not have access to this ticket'], 403);
                }
            }

            if ($this->transitionBlocked($ticket, $validated['status'])) {
                return response()->json(['message' => $this->closedTicketMessage()], 422);
            }

            $oldStatus = $ticket->status;
            $ticket->update(['status' => $validated['status']]);

            if ($oldStatus !== $validated['status']) {
                try {
                    Comment::create([
                        'ticket_id' => $ticketId,
                        'author_id' => $request->user()->id,
                        'content' => "Status changed from {$oldStatus} to {$validated['status']}",
                        'is_internal' => false,
                    ]);
                } catch (\Throwable $commentError) {
                    Log::warning('updateStatus comment log failed', [
                        'ticketId' => $ticketId,
                        'error' => $commentError->getMessage(),
                    ]);
                }
            }

            $ticket->load([
                'assignedToRelation' => fn ($q) => $q->select(User::supportDeskSelectColumns()),
                'requesterUserRelation' => fn ($q) => $q->select(User::supportDeskSelectColumns()),
                'comments' => fn ($q) => $q->with($this->commentRelations())->orderBy('created_at'),
                'agents',
                'attachments',
            ]);

            Log::debug('updateStatus: success', ['ticketId' => $ticketId, 'status' => $validated['status']]);
            return response()->json($ticket);
        } catch (\Throwable $e) {
            Log::error('updateStatus error', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
            return response()->json(['message' => 'Failed to update ticket status', 'error' => $e->getMessage()], 500);
        }
    }

    public function assign(Request $request, string $ticketId)
    {
        $validated = $request->validate([
            'assignToMe' => ['required', 'boolean'],
        ]);

        $ticket = Ticket::findOrFail($ticketId);
        if ($this->isClosedStatus((string) $ticket->status)) {
            return response()->json(['message' => $this->closedTicketMessage()], 422);
        }

        if ($ticket->assigned_to_id) {
            return response()->json(['message' => 'Ticket is already assigned'], 409);
        }

        if ($validated['assignToMe']) {
            $ticket->update([
                'assigned_to_id' => $request->user()->id,
                'status' => 'in-progress',
            ]);

            Comment::create([
                'ticket_id' => $ticketId,
                'author_id' => $request->user()->id,
                'content' => 'Ticket assigned to ' . ($request->user()->department ?: 'Support') . ' Organization',
                'is_internal' => false,
            ]);
        }

        return response()->json(['message' => 'Ticket assigned successfully']);
    }

    public function escalate(Request $request, string $ticketId)
    {
        $ticket = Ticket::findOrFail($ticketId);
        if ($this->isClosedStatus((string) $ticket->status)) {
            return response()->json(['message' => $this->closedTicketMessage()], 422);
        }

        if ((string) $ticket->assigned_to_id !== (string) $request->user()->id) {
            return response()->json(['message' => 'Agent does not have access to this ticket'], 403);
        }

        Comment::create([
            'ticket_id' => $ticketId,
            'author_id' => $request->user()->id,
            'content' => '🚨 Ticket escalated to admin for further assistance',
            'is_internal' => true,
        ]);

        $ticket->update(['status' => 'waiting-internal']);

        return response()->json(['message' => 'Ticket escalated successfully']);
    }
}
