<?php

namespace App\Services;

use App\Events\TicketEscalated;
use App\Jobs\SendEmailJob;
use App\Models\Admin;
use App\Models\EscalationRule;
use App\Models\Setting;
use App\Models\Ticket;
use App\Models\TicketEscalationRule;
use App\Models\TicketReply;
use Illuminate\Support\Facades\Log;

class TicketEscalationService
{
    /**
     * Check instant (non-time-based) escalation rules for a ticket.
     * Called by event listener when ticket is created or client replies.
     */
    public function checkInstantRules(Ticket $ticket): array
    {
        if (! $this->isEnabled()) {
            return [];
        }

        $ticket->loadMissing(['status', 'department']);

        if ($ticket->status?->is_closed) {
            return [];
        }

        $rules = EscalationRule::active()
            ->ordered()
            ->whereNull('condition_time_elapsed')
            ->matchingConditions(
                $ticket->department_id,
                $ticket->status_id,
                $ticket->priority
            )
            ->get();

        return $this->processRules($ticket, $rules);
    }

    /**
     * Process all open tickets for time-based escalation rules.
     * Called by the scheduled command.
     */
    public function processAllOpenTickets(): int
    {
        if (! $this->isEnabled()) {
            return 0;
        }

        $timeBasedRules = EscalationRule::active()
            ->whereNotNull('condition_time_elapsed')
            ->count();

        if ($timeBasedRules === 0) {
            return 0;
        }

        $totalEscalated = 0;

        Ticket::open()
            ->with(['status', 'department'])
            ->chunk(100, function ($tickets) use (&$totalEscalated) {
                foreach ($tickets as $ticket) {
                    $timeElapsed = $this->calculateTimeElapsed($ticket);

                    $rules = EscalationRule::active()
                        ->ordered()
                        ->whereNotNull('condition_time_elapsed')
                        ->matchingConditions(
                            $ticket->department_id,
                            $ticket->status_id,
                            $ticket->priority,
                            $timeElapsed
                        )
                        ->get();

                    $applied = $this->processRules($ticket, $rules);
                    $totalEscalated += count($applied);
                }
            });

        return $totalEscalated;
    }

    /**
     * Process a set of rules against a ticket.
     */
    private function processRules(Ticket $ticket, $rules): array
    {
        $applied = [];

        foreach ($rules as $rule) {
            if ($this->hasBeenEscalated($ticket, $rule)) {
                continue;
            }

            $changes = $this->applyRule($ticket, $rule);

            if (! empty($changes)) {
                $this->logEscalation($ticket, $rule, $changes);

                TicketEscalated::dispatch($ticket, $rule, $changes);

                $applied[] = ['rule_id' => $rule->id, 'rule_name' => $rule->name, 'changes' => $changes];

                Log::info('Ticket escalated', [
                    'ticket_id' => $ticket->id,
                    'rule_id' => $rule->id,
                    'rule_name' => $rule->name,
                    'changes' => $changes,
                ]);
            }
        }

        return $applied;
    }

    /**
     * Apply a single escalation rule to a ticket.
     */
    private function applyRule(Ticket $ticket, EscalationRule $rule): array
    {
        $changes = [];

        // Change department
        if ($rule->action_department_id && $rule->action_department_id !== $ticket->department_id) {
            $changes['department_id'] = [
                'from' => $ticket->department_id,
                'to' => $rule->action_department_id,
            ];
            $ticket->department_id = $rule->action_department_id;
        }

        // Change status
        if ($rule->action_status_id && $rule->action_status_id !== $ticket->status_id) {
            $changes['status_id'] = [
                'from' => $ticket->status_id,
                'to' => $rule->action_status_id,
            ];
            $ticket->status_id = $rule->action_status_id;
        }

        // Change priority
        if ($rule->action_priority && $rule->action_priority->value !== $ticket->priority) {
            $changes['priority'] = [
                'from' => $ticket->priority,
                'to' => $rule->action_priority->value,
            ];
            $ticket->priority = $rule->action_priority->value;
        }

        // Flag to admin
        if ($rule->action_flag_to_admin_id) {
            $changes['flagged'] = [
                'admin_id' => $rule->action_flag_to_admin_id,
            ];
            $ticket->is_flagged = true;
            $ticket->flagged_by = $rule->action_flag_to_admin_id;
        }

        // Save ticket changes
        if (! empty($changes)) {
            $ticket->saveQuietly();
        }

        // PRC-794 — assign_to_agent action (tek otorite: TicketAssignmentService)
        if ($rule->action_assign_to_admin_id
            && (int) $ticket->assigned_admin_id !== (int) $rule->action_assign_to_admin_id) {
            $assignee = \App\Models\Admin::find($rule->action_assign_to_admin_id);
            if ($assignee) {
                app(\App\Services\Ticket\TicketAssignmentService::class)
                    ->assign($ticket, $assignee, null, 'escalation');
                $changes['assigned'] = ['admin_id' => $rule->action_assign_to_admin_id];
            }
        }

        // Auto-reply to customer
        if ($rule->auto_reply_message) {
            $this->sendAutoReply($ticket, $rule);
            $changes['auto_reply'] = true;
        }

        // Notify department admins
        if ($rule->notify_department_admins) {
            $this->notifyDepartmentAdmins($ticket, $rule);
            $changes['notified_department_admins'] = true;
        }

        // Notify specific admins
        if (! empty($rule->notify_specific_admins)) {
            $this->notifySpecificAdmins($ticket, $rule);
            $changes['notified_specific_admins'] = $rule->notify_specific_admins;
        }

        return $changes;
    }

    /**
     * Send auto-reply to customer.
     * Creates a system TicketReply and dispatches email.
     */
    private function sendAutoReply(Ticket $ticket, EscalationRule $rule): void
    {
        $reply = TicketReply::create([
            'ticket_id' => $ticket->id,
            'message' => $rule->auto_reply_message,
            'mail' => setting('system_emails_from_email') ?: config('mail.from.address', 'noreply@localhost'),
        ]);

        if ($ticket->client_id) {
            SendEmailJob::dispatch(
                'Support Ticket Reply',
                'ticket',
                $ticket->id,
                [
                    'REPLY_ID' => $reply->id,
                    'REPLY_MESSAGE' => $reply->message,
                    'REPLY_DATE' => $reply->created_at?->format('d.m.Y'),
                    'REPLY_TIME' => $reply->created_at?->format('H:i'),
                    'ADMIN_NAME' => __('admin/escalation-rules.system_auto_reply_sender'),
                    'ADMIN_EMAIL' => '',
                ]
            );
        }
    }

    /**
     * Notify all admins in the ticket's department.
     */
    private function notifyDepartmentAdmins(Ticket $ticket, EscalationRule $rule): void
    {
        $ticket->loadMissing('department.admins');
        $admins = $ticket->department?->admins ?? collect();

        foreach ($admins as $admin) {
            $this->sendAdminEscalationNotification($ticket, $rule, $admin);
        }
    }

    /**
     * Notify specific admins defined in the rule.
     */
    private function notifySpecificAdmins(Ticket $ticket, EscalationRule $rule): void
    {
        $adminIds = $rule->notify_specific_admins ?? [];

        if (empty($adminIds)) {
            return;
        }

        $admins = Admin::whereIn('id', $adminIds)->get();

        foreach ($admins as $admin) {
            $this->sendAdminEscalationNotification($ticket, $rule, $admin);
        }
    }

    /**
     * Send escalation notification email to an admin.
     */
    private function sendAdminEscalationNotification(Ticket $ticket, EscalationRule $rule, Admin $admin): void
    {
        SendEmailJob::dispatch(
            'Escalation Rule Notification',
            'ticket',
            $ticket->id,
            [
                'ESCALATION_RULE' => $rule->name,
                'ADMIN_NAME' => $admin->name,
                'TICKET_PRIORITY' => $ticket->priority,
            ],
            null,
            $admin->email
        );
    }

    /**
     * Check if this rule was already applied to this ticket (prevent duplicates).
     * For time-based rules, checks since the last client reply (allows re-escalation on new cycles).
     * For instant rules, checks if ever applied.
     */
    private function hasBeenEscalated(Ticket $ticket, EscalationRule $rule): bool
    {
        $query = TicketEscalationRule::where('ticket_id', $ticket->id)
            ->where('escalation_rule_id', $rule->id);

        // For time-based rules, allow re-escalation after new client activity
        if ($rule->condition_time_elapsed !== null) {
            $lastClientActivity = $ticket->last_client_reply_at ?? $ticket->created_at;
            $query->where('created_at', '>=', $lastClientActivity);
        }

        return $query->exists();
    }

    /**
     * Log escalation action to ticket_escalation_rules table.
     */
    private function logEscalation(Ticket $ticket, EscalationRule $rule, array $changes): void
    {
        TicketEscalationRule::create([
            'ticket_id' => $ticket->id,
            'escalation_rule_id' => $rule->id,
            'condition' => $rule->condition_summary,
            'action' => $rule->action_summary,
            'changes' => $changes,
            'priority' => $ticket->priority === 'urgent' ? 'high' : ($ticket->priority ?? 'medium'),
        ]);
    }

    /**
     * Calculate minutes elapsed since the ticket needs attention.
     * Uses the most recent activity (client reply, admin reply, or creation).
     */
    private function calculateTimeElapsed(Ticket $ticket): int
    {
        $timestamps = array_filter([
            $ticket->last_client_reply_at,
            $ticket->last_admin_reply_at,
            $ticket->created_at,
        ]);

        if (empty($timestamps)) {
            return 0;
        }

        $lastActivity = collect($timestamps)->max();

        // Carbon 3: $now->diffInMinutes($pastDate) is signed (negative for past).
        // We want minutes elapsed since the last activity → absolute.
        return (int) abs(now()->diffInMinutes($lastActivity));
    }

    /**
     * Check if escalation system is enabled.
     */
    private function isEnabled(): bool
    {
        return (bool) Setting::get('escalation_enabled', true);
    }
}
