<?php

namespace App\Domain\Project\Jobs;

use App\Domain\Experiment\Models\PlaybookStep;
use App\Domain\Outbound\Actions\SendOutboundAction;
use App\Domain\Outbound\Enums\OutboundProposalStatus;
use App\Domain\Outbound\Models\OutboundProposal;
use App\Domain\Project\Models\ProjectRun;
use App\Models\Artifact;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;

class DeliverWorkflowResultsJob implements ShouldQueue
{
    use Dispatchable, Queueable;

    public int $tries = 2;

    public int $timeout = 120;

    public function __construct(
        public readonly string $projectRunId,
    ) {
        $this->onQueue('outbound');
    }

    public function handle(SendOutboundAction $sendAction): void
    {
        $run = ProjectRun::withoutGlobalScopes()
            ->with(['project', 'experiment'])
            ->find($this->projectRunId);

        if (! $run || ! $run->project || ! $run->experiment) {
            Log::warning('DeliverWorkflowResultsJob: Run or related models not found', [
                'run_id' => $this->projectRunId,
            ]);

            return;
        }

        $project = $run->project;
        $experiment = $run->experiment;
        $deliveryConfig = $project->delivery_config;

        $channel = $deliveryConfig['channel'] ?? $deliveryConfig['outbound_channel'] ?? 'none';

        if (empty($deliveryConfig) || $channel === 'none') {
            return;
        }

        // Use pre-collected output summary (set by SyncProjectStatusOnRunComplete),
        // fall back to collecting if not yet populated
        $summary = $run->output_summary ?? $this->collectWorkflowOutput($experiment->id);

        if (! $run->output_summary) {
            $run->update(['output_summary' => $summary]);
        }
        // For signal-triggered email workflows, reply to the original sender
        if ($channel === 'email' && empty($deliveryConfig['target'])) {
            $signalFrom = $experiment->meta['signal_payload']['from'] ?? null;
            if ($signalFrom) {
                $deliveryConfig['target'] = $signalFrom;
            }
        }

        $target = $this->buildTarget($channel, $deliveryConfig);

        // For signal-triggered email replies, use the reply draft as body (not the full output dump)
        $signalPayload = $experiment->meta['signal_payload'] ?? null;
        if ($channel === 'email' && $signalPayload) {
            $content = $this->buildEmailReplyContent($experiment, $signalPayload);
        } else {
            $content = $this->buildContent($project, $run, $summary, $deliveryConfig);
        }

        try {
            $proposal = OutboundProposal::withoutGlobalScopes()->create([
                'team_id' => $project->team_id,
                'experiment_id' => $experiment->id,
                'channel' => $channel,
                'target' => $target,
                'content' => $content,
                'risk_score' => 0,
                'status' => OutboundProposalStatus::Approved,
            ]);

            $sendAction->execute($proposal);

            Log::info('DeliverWorkflowResultsJob: Results delivered', [
                'run_id' => $run->id,
                'channel' => $channel,
                'project_id' => $project->id,
            ]);
        } catch (\Throwable $e) {
            Log::error('DeliverWorkflowResultsJob: Delivery failed', [
                'run_id' => $run->id,
                'channel' => $channel,
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function collectWorkflowOutput(string $experimentId): string
    {
        $steps = PlaybookStep::where('experiment_id', $experimentId)
            ->where('status', 'completed')
            ->orderBy('order')
            ->get();

        if ($steps->isEmpty()) {
            return 'Workflow completed with no output.';
        }

        $parts = [];
        foreach ($steps as $step) {
            $output = $step->output;
            if (! $output) {
                continue;
            }

            $label = $step->label ?? $step->skill_name ?? "Step {$step->order}";

            $text = $output['result']
                ?? $output['text']
                ?? $output['content']
                ?? (string) json_encode($output, JSON_PRETTY_PRINT);

            $parts[] = "## {$label}\n\n{$text}";
        }

        return implode("\n\n---\n\n", $parts) ?: 'Workflow completed with no output.';
    }

    private function buildTarget(string $channel, array $config): array
    {
        return match ($channel) {
            'email' => ['email' => $config['target'] ?? ''],
            'slack' => [
                'webhook_url' => $config['webhook_url'] ?? config('services.slack.webhook_url'),
                'channel' => $config['target'] ?? null,
            ],
            'telegram' => ['chat_id' => $config['target'] ?? ''],
            'webhook' => ['url' => $config['target'] ?? ''],
            default => [],
        };
    }

    private function buildContent(mixed $project, ProjectRun $run, string $summary, array $config): array
    {
        $format = $config['format'] ?? 'summary';
        $title = "{$project->title} — Run #{$run->run_number}";

        $body = match ($format) {
            'full' => $summary,
            'json' => $summary,
            default => $this->truncateSummary($summary, 2000),
        };

        // Append artifact info if any exist
        $artifactSection = $this->buildArtifactSection($run);
        if ($artifactSection) {
            $body .= "\n\n---\n\n".$artifactSection;
        }

        return [
            'subject' => $title,
            'body' => $body,
            'text' => $body,
        ];
    }

    private function buildArtifactSection(ProjectRun $run): ?string
    {
        $artifacts = Artifact::withoutGlobalScopes()
            ->where('experiment_id', $run->experiment_id)
            ->orderBy('created_at')
            ->get();

        if ($artifacts->isEmpty()) {
            return null;
        }

        $lines = ["**Artifacts ({$artifacts->count()}):**"];

        foreach ($artifacts as $artifact) {
            $previewUrl = url("/artifacts/{$artifact->id}/render");
            $lines[] = "- [{$artifact->name}]({$previewUrl}) ({$artifact->type})";
        }

        return implode("\n", $lines);
    }

    private function buildEmailReplyContent($experiment, array $signalPayload): array
    {
        $originalSubject = $signalPayload['subject'] ?? 'Your inquiry';
        $subject = str_starts_with($originalSubject, 'Re:') ? $originalSubject : "Re: {$originalSubject}";

        // Find the last agent step with a text result (the reply draft)
        $steps = PlaybookStep::where('experiment_id', $experiment->id)
            ->where('status', 'completed')
            ->orderByDesc('order')
            ->get();

        $replyBody = null;
        foreach ($steps as $step) {
            $output = $step->output;
            if (is_array($output) && ! empty($output['result']) && is_string($output['result'])) {
                $text = $output['result'];
                // Skip JSON-only outputs (classification results)
                if (! str_starts_with(trim($text), '```json') && ! str_starts_with(trim($text), '{')) {
                    $replyBody = $text;
                    break;
                }
            }
        }

        return [
            'subject' => $subject,
            'body' => $replyBody ?? 'Thank you for your inquiry. We will get back to you shortly.',
            'text' => $replyBody ?? 'Thank you for your inquiry. We will get back to you shortly.',
            'in_reply_to' => $signalPayload['message_id'] ?? null,
        ];
    }

    private function truncateSummary(string $text, int $maxLength): string
    {
        if (mb_strlen($text) <= $maxLength) {
            return $text;
        }

        return mb_substr($text, 0, $maxLength - 3).'...';
    }
}
