<?php

namespace App\Http\Controllers;

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

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

    private function autoDetectPriority(string $title, string $description, string $category): string
    {
        if (class_exists(\App\Support\TicketHelper::class)) {
            return \App\Support\TicketHelper::autoDetectPriority($title, $description, $category);
        }

        $combined = strtolower($title . ' ' . $description);
        $urgentKeywords = ['crash', 'down', 'critical', 'emergency', 'urgent', 'immediately', 'asap', 'cannot work', 'system failure'];
        $highKeywords = ['broken', 'error', 'bug', 'issue', 'problem', 'not working', 'failed', 'high priority'];
        $mediumKeywords = ['help', 'question', 'how to', 'support', 'assistance', 'medium'];

        foreach ($urgentKeywords as $keyword) {
            if (str_contains($combined, $keyword)) {
                return 'Urgent';
            }
        }

        foreach ($highKeywords as $keyword) {
            if (str_contains($combined, $keyword)) {
                return 'High';
            }
        }

        foreach ($mediumKeywords as $keyword) {
            if (str_contains($combined, $keyword)) {
                return 'Medium';
            }
        }

        return 'Low';
    }

    private function calculateSlaDates(?string $priority): array
    {
        if (class_exists(\App\Support\TicketHelper::class)) {
            return \App\Support\TicketHelper::calculateSlaDates($priority);
        }

        $now = now();
        $slaConfig = [
            'Urgent' => ['first_response_minutes' => 15, 'resolution_hours' => 4],
            'High' => ['first_response_minutes' => 60, 'resolution_hours' => 24],
            'Medium' => ['first_response_minutes' => 240, 'resolution_hours' => 48],
            'Low' => ['first_response_minutes' => 1440, 'resolution_hours' => 72],
        ];
        $config = $slaConfig[$priority] ?? $slaConfig['Low'];

        return [
            'first_response_sla' => $now->copy()->addMinutes($config['first_response_minutes']),
            'resolution_sla' => $now->copy()->addHours($config['resolution_hours']),
        ];
    }

    private function agentsForCategory(?string $category)
    {
        if (class_exists(\App\Support\TicketHelper::class)) {
            return \App\Support\TicketHelper::agentsForCategory($category);
        }

        if (!$category) {
            return collect();
        }

        return User::query()
            ->where('role', 'agent')
            ->where('category', $category)
            ->orderBy('name')
            ->get();
    }

    private function findAvailableAgent(?string $category): ?User
    {
        if (class_exists(\App\Support\TicketHelper::class)) {
            return \App\Support\TicketHelper::findAvailableAgent($category);
        }

        $scopedQuery = User::query()->where('role', 'agent');

        if ($category) {
            $agent = (clone $scopedQuery)->where('category', $category)->orderBy('name')->first();
            if ($agent) {
                return $agent;
            }
        }

        return $scopedQuery->orderBy('name')->first();
    }

    private function generateTicketId(): string
    {
        if (class_exists(\App\Support\TicketHelper::class)) {
            return \App\Support\TicketHelper::generateTicketId();
        }

        $prefix = 'TKT';
        $timestamp = time();
        $random = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 6);

        return "{$prefix}-{$timestamp}-{$random}";
    }

    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 transitionBlocked(Ticket $ticket, string $nextStatus): bool
    {
        if (class_exists(\App\Support\TicketStatus::class)) {
            return \App\Support\TicketStatus::transitionBlocked($ticket, $nextStatus);
        }

        return strtolower((string) $ticket->status) === 'closed' && strtolower($nextStatus) !== 'closed';
    }

    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 canAdminAccessTicket(Ticket $ticket, ?User $user): bool
    {
        if (class_exists(OrganizationScope::class)) {
            return OrganizationScope::canAccessTicket($ticket, $user);
        }

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

        if ($this->isSuperAdmin($user)) {
            return true;
        }

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

        return $organization !== null && $ticket->department === $organization;
    }

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

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

        return $relations;
    }

    private function categoryValidationRules(bool $required = true): array
    {
        $rules = [$required ? 'required' : 'nullable', 'string'];

        if (Schema::hasTable('support_categories')) {
            SupportCategory::ensureDefaults();
            $rules[] = Rule::exists('support_categories', 'name');
        } else {
            $rules[] = Rule::in(collect(SupportCategory::defaultCategories())->pluck('name')->all());
        }

        return $rules;
    }

    public function index(Request $request)
    {
        $perPage = $request->query('per_page', 50);
        $page = $request->query('page', 1);
        $perPage = min($perPage, 100); // Max 100 per page

        $query = Ticket::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'),
                'attachments',
                'agents',
            ])
            ->orderByDesc('updated_at');

        // Regular users only see their own tickets
        $user = $request->user();
        if ($user && $this->userRole($user) === 'user') {
            $query->where('requester_id', $user->id);
        } elseif ($user && $this->userRole($user) === 'admin') {
            $this->applyAdminTicketScope($query, $user);
        }

        $tickets = $query->paginate($perPage, ['*'], 'page', $page);
        
        return response()->json($tickets);
    }

    public function show(Request $request, string $id)
    {
        $ticket = Ticket::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'),
                'attachments',
                'agents',
            ])
            ->find($id);

        if (!$ticket) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        // Regular users can only view their own tickets
        $user = $request->user();
        if ($user && $this->userRole($user) === 'user' && $ticket->requester_id != $user->id) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        if ($user && $this->userRole($user) === 'admin' && !$this->canAdminAccessTicket($ticket, $user)) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        // Filter out internal comments for regular users
        if ($user && $this->userRole($user) === 'user') {
            $ticket->setRelation(
                'comments',
                $ticket->comments->filter(fn ($c) => !$c->is_internal)->values()
            );
        }

        return response()->json($ticket);
    }

    public function store(Request $request)
    {
        try {
            $validated = $request->validate([
                'title' => ['required', 'string', 'min:3'],
                'description' => ['required', 'string', 'min:5'],
                'category' => $this->categoryValidationRules(),
                'priority' => ['nullable', 'string', 'in:Low,Medium,High,Urgent'],
                'requesterId' => ['nullable'],
                'organization' => ['nullable', 'string'],
                'department' => ['nullable', 'string'],
                'requesterName' => ['nullable', 'string'],
                'attachmentIds' => ['nullable', 'array'],
            ]);

            $priority = $validated['priority'] ?? $this->autoDetectPriority(
                $validated['title'],
                $validated['description'],
                $validated['category']
            );

            $sla = $this->calculateSlaDates($priority);
            $matchingAgents = $this->agentsForCategory($validated['category']);
            $availableAgent = $matchingAgents->first() ?: $this->findAvailableAgent($validated['category']);
            $requesterId = $request->user()?->id ?? ($validated['requesterId'] ?? null);

            $organization = $validated['organization'] ?? $validated['department'] ?? null;
            if ($this->userRole($request->user()) === 'user') {
                $organization = $request->user()?->department ?? $organization;
            }

            // Auto-set organization from the user's account if not provided
            $department = $organization ?? $request->user()?->department ?? null;

            $ticket = Ticket::create([
                'id' => $this->generateTicketId(),
                'title' => $validated['title'],
                'description' => $validated['description'],
                'category' => $validated['category'],
                'priority' => $priority,
                'status' => 'open',
                'requester' => $validated['requesterName'] ?? $request->user()?->name ?? 'Anonymous',
                'requester_id' => $requesterId,
                'requester_name' => $validated['requesterName'] ?? $request->user()?->name ?? null,
                'department' => $department,
                'assigned_to_id' => $availableAgent?->id,
                'sla_due' => $sla['resolution_sla'],
                'first_response_sla' => $sla['first_response_sla'],
                'resolution_sla' => $sla['resolution_sla'],
                'first_response_met' => false,
            ]);

            if (!empty($validated['attachmentIds'])) {
                Attachment::whereIn('id', $validated['attachmentIds'])
                    ->whereNull('ticket_id')
                    ->update(['ticket_id' => $ticket->id]);
            }

            if ($matchingAgents->isNotEmpty()) {
                $ticket->agents()->syncWithoutDetaching($matchingAgents->pluck('id')->all());
            } elseif ($availableAgent) {
                $ticket->agents()->syncWithoutDetaching([$availableAgent->id]);
            }

            return response()->json($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'),
                'attachments',
                'agents',
            ]), 201);
        } catch (\Exception $e) {
            Log::error('ticket store failed', [
                'user_id' => $request->user()?->id,
                'payload' => $request->except(['attachmentIds']),
                'error' => $e->getMessage(),
            ]);

            return response()->json([
                'message' => 'Failed to create ticket',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    public function update(Request $request, string $id)
    {
        $ticket = Ticket::find($id);
        if (!$ticket) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        $user = $request->user();
        if ($user && $this->userRole($user) === 'admin' && !$this->canAdminAccessTicket($ticket, $user)) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        $validated = $request->validate([
            'title' => ['sometimes', 'string', 'min:3'],
            'description' => ['sometimes', 'string', 'min:5'],
            'category' => ['sometimes', ...$this->categoryValidationRules(false)],
            'priority' => ['sometimes', 'string', 'in:Low,Medium,High,Urgent'],
            'status' => ['sometimes', 'string', Rule::in($this->ticketStatuses())],
            'requester' => ['sometimes', 'string', 'min:2'],
            'organization' => ['nullable', 'string'],
            'department' => ['nullable', 'string'],
            'assigneeId' => ['nullable', 'string', Rule::exists('users', 'id')],
        ]);

        if (array_key_exists('assigneeId', $validated)) {
            $validated['assigned_to_id'] = $validated['assigneeId'];
            unset($validated['assigneeId']);
        }

        if (array_key_exists('organization', $validated)) {
            $validated['department'] = $validated['organization'];
            unset($validated['organization']);
        }

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

        $ticket->update($validated);

        return Ticket::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'),
                'attachments',
            ])
            ->find($id);
    }

    public function destroy(Request $request, string $id)
    {
        $ticket = Ticket::find($id);
        if (!$ticket) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        $user = $request->user();
        if ($user && $this->userRole($user) === 'admin' && !$this->canAdminAccessTicket($ticket, $user)) {
            return response()->json(['message' => 'Ticket not found'], 404);
        }

        $ticket->delete();
        return response()->noContent();
    }
}
