<?php

namespace App\Services;

use App\Models\Ticket;
use App\Models\TicketDepartment;
use App\Models\TicketReply;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
 * Sends ticket-related notifications to a per-department Discord channel webhook.
 *
 * Two modes:
 *  - Flat mode (default): all ticket events land as separate messages in one channel.
 *  - Forum thread mode: requires the webhook to point to a Forum Channel. The first
 *    message for each ticket creates a new forum thread (thread_name parameter), and
 *    subsequent replies are posted into that thread via ?thread_id=...
 *
 * Discord webhooks cannot DM users and cannot create threads in non-forum channels.
 * For full per-ticket isolation in a regular text channel you'd need a bot token.
 */
class TicketDiscordService
{
    public function isEnabled(TicketDepartment $department): bool
    {
        return $department->discord_enabled
            && !empty($department->discord_webhook_url);
    }

    /**
     * Post a new-ticket notification. If forum threads are enabled, creates a new
     * thread and persists discord_thread_id on the ticket for subsequent replies.
     */
    public function sendTicketOpened(Ticket $ticket): void
    {
        $department = $ticket->department;
        if (!$this->isEnabled($department)) {
            return;
        }

        $client = $ticket->client;
        $clientName = $client ? trim($client->first_name . ' ' . $client->last_name) : 'Guest';
        $firstMessage = $ticket->replies()->oldest()->first()?->message ?? '';

        $embed = [
            'title' => "🎫 New Ticket #{$ticket->id}",
            'description' => Str::limit($firstMessage, 1500),
            'color' => $this->priorityColor($ticket->priority),
            'fields' => [
                ['name' => 'Subject', 'value' => Str::limit($ticket->title ?? '-', 200), 'inline' => false],
                ['name' => 'Priority', 'value' => ucfirst($ticket->priority ?? 'normal'), 'inline' => true],
                ['name' => 'Client', 'value' => "{$clientName} ({$ticket->mail})", 'inline' => true],
                ['name' => 'Department', 'value' => $department->name ?? '-', 'inline' => true],
            ],
            'timestamp' => now()->toIso8601String(),
        ];

        $payload = ['embeds' => [$embed]];

        // Forum channels: create a new thread for this ticket on the first message.
        if ($department->discord_use_forum_threads) {
            $payload['thread_name'] = Str::limit("#{$ticket->id} - " . ($ticket->title ?? 'Ticket'), 100, '');
        }

        $response = $this->executeWebhook($department->discord_webhook_url, $payload, waitForResponse: true);

        if ($response === null) {
            return;
        }

        // Persist IDs for threaded replies later.
        // In forum mode, `channel_id` on the response is the new thread ID.
        // In flat mode, `id` is the first message ID (we can still thread via that if desired).
        $update = [];
        if (!empty($response['id'])) {
            $update['discord_message_id'] = (string) $response['id'];
        }
        if ($department->discord_use_forum_threads && !empty($response['channel_id'])) {
            $update['discord_thread_id'] = (string) $response['channel_id'];
        }

        if (!empty($update)) {
            $ticket->update($update);
        }
    }

    /**
     * Post a reply into the ticket's Discord thread (forum mode) or the channel (flat mode).
     */
    public function sendReply(Ticket $ticket, TicketReply $reply): void
    {
        $department = $ticket->department;
        if (!$this->isEnabled($department) || $reply->is_internal) {
            return;
        }

        $authorName = $reply->isFromAdmin()
            ? ($reply->admin?->name ?? 'Admin')
            : trim(($reply->client?->first_name ?? '') . ' ' . ($reply->client?->last_name ?? ''));

        $label = $reply->isFromAdmin() ? '👤 Staff Reply' : '💬 Client Reply';

        $embed = [
            'title' => "{$label} — Ticket #{$ticket->id}",
            'description' => Str::limit($reply->message ?? '', 1500),
            'color' => $reply->isFromAdmin() ? 0x5865F2 : 0x57F287,
            'fields' => [
                ['name' => 'By', 'value' => $authorName ?: '-', 'inline' => true],
            ],
            'timestamp' => now()->toIso8601String(),
        ];

        $this->executeWebhook(
            $department->discord_webhook_url,
            ['embeds' => [$embed]],
            threadId: $ticket->discord_thread_id,
        );
    }

    public function sendTicketClosed(Ticket $ticket): void
    {
        $department = $ticket->department;
        if (!$this->isEnabled($department)) {
            return;
        }

        $embed = [
            'title' => "✅ Ticket #{$ticket->id} Closed",
            'description' => Str::limit($ticket->title ?? '-', 200),
            'color' => 0x57F287,
            'timestamp' => now()->toIso8601String(),
        ];

        $this->executeWebhook(
            $department->discord_webhook_url,
            ['embeds' => [$embed]],
            threadId: $ticket->discord_thread_id,
        );
    }

    public function sendEscalationNotice(Ticket $ticket, string $ruleName, array $changes): void
    {
        $department = $ticket->department;
        if (!$this->isEnabled($department)) {
            return;
        }

        $changeLines = [];
        if (isset($changes['department_id'])) $changeLines[] = '• Department changed';
        if (isset($changes['status_id'])) $changeLines[] = '• Status changed';
        if (isset($changes['priority'])) $changeLines[] = "• Priority: {$changes['priority']['from']} → {$changes['priority']['to']}";
        if (isset($changes['flagged'])) $changeLines[] = '• Flagged to admin';
        if (isset($changes['auto_reply'])) $changeLines[] = '• Auto-reply sent';

        $embed = [
            'title' => "🚨 Ticket #{$ticket->id} Escalated",
            'description' => "**Rule:** {$ruleName}\n" . implode("\n", $changeLines),
            'color' => 0xED4245,
            'timestamp' => now()->toIso8601String(),
        ];

        $this->executeWebhook(
            $department->discord_webhook_url,
            ['embeds' => [$embed]],
            threadId: $ticket->discord_thread_id,
        );
    }

    /**
     * Execute a Discord webhook. Optionally waits for the message object response
     * (needed when we want to persist the returned id/channel_id for threading).
     *
     * @return array|null Decoded response when waitForResponse=true, otherwise null.
     */
    private function executeWebhook(
        string $webhookUrl,
        array $payload,
        bool $waitForResponse = false,
        ?string $threadId = null,
    ): ?array {
        $url = $webhookUrl;
        $query = [];
        if ($waitForResponse) {
            $query['wait'] = 'true';
        }
        if ($threadId) {
            $query['thread_id'] = $threadId;
        }
        if (!empty($query)) {
            $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($query);
        }

        try {
            $response = Http::timeout(10)->post($url, $payload);

            if ($response->failed()) {
                Log::warning('Discord webhook failed', [
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);
                return null;
            }

            return $waitForResponse ? $response->json() : null;
        } catch (\Exception $e) {
            Log::error('Discord webhook exception', ['error' => $e->getMessage()]);
            return null;
        }
    }

    private function priorityColor(?string $priority): int
    {
        return match (strtolower($priority ?? 'normal')) {
            'high', 'urgent', 'critical' => 0xED4245, // red
            'medium', 'normal' => 0xFEE75C,           // yellow
            'low' => 0x57F287,                        // green
            default => 0x5865F2,                      // blurple
        };
    }
}
