<?php

namespace App\Services\Jobs;

use App\Models\ImportedJob;
use App\Models\JobAiAssistance;
use App\Models\User;
use App\Services\JobMatchingService;
use App\Services\Settings\SettingsService;
use OpenAI;
use RuntimeException;

class JobAiAssistantService
{
    public function __construct(
        private readonly SettingsService $settings,
        private readonly JobMatchingService $matchingService,
    ) {}

    /**
     * @return array{content: string, payload: array<string, mixed>}
     */
    public function generate(User $user, ImportedJob $job, string $type): array
    {
        $this->ensureTypeIsEnabled($type);

        $apiKey = (string) $this->settings->get('openai.api_key', config('services.openai.api_key'));

        if (! filled($apiKey)) {
            throw new RuntimeException('OpenAI API key is not configured.');
        }

        $response = OpenAI::client($apiKey)
            ->chat()
            ->create([
                'model' => $this->modelFor($type),
                'response_format' => ['type' => 'json_object'],
                'temperature' => (float) $this->settings->get('openai.temperature', 0.2),
                'max_tokens' => (int) $this->settings->get('openai.maximum_tokens_per_request', 2000),
                'messages' => [
                    [
                        'role' => 'system',
                        'content' => 'You help remote job applicants. Return only valid JSON. Do not promise employment outcomes.',
                    ],
                    [
                        'role' => 'user',
                        'content' => $this->prompt($user, $job, $type),
                    ],
                ],
            ]);

        $content = $response->choices[0]->message->content ?? '{}';
        $payload = json_decode($content, true);

        if (! is_array($payload)) {
            throw new RuntimeException('OpenAI returned invalid JSON.');
        }

        return [
            'content' => $this->contentFromPayload($type, $payload),
            'payload' => $payload,
        ];
    }

    /**
     * @return array<int, string>
     */
    public function localMatchReasons(User $user, ImportedJob $job): array
    {
        return $this->matchingService->explain($user, $job);
    }

    private function ensureTypeIsEnabled(string $type): void
    {
        if (! $this->settings->isEnabled('openai.enabled')) {
            throw new RuntimeException('OpenAI features are disabled.');
        }

        $enabled = match ($type) {
            JobAiAssistance::TYPE_COVER_LETTER => $this->settings->isEnabled('ai_features.enable_cover_letter_generator'),
            JobAiAssistance::TYPE_INTERVIEW_PREP => $this->settings->isEnabled('ai_features.enable_interview_preparation'),
            JobAiAssistance::TYPE_MATCH_EXPLANATION => $this->settings->isEnabled('ai_features.enable_job_match_explanation'),
            default => false,
        };

        if (! $enabled) {
            throw new RuntimeException('This AI feature is disabled in admin settings.');
        }
    }

    private function modelFor(string $type): string
    {
        return match ($type) {
            JobAiAssistance::TYPE_COVER_LETTER => (string) $this->settings->get('openai.cover_letter_model', $this->settings->get('openai.default_model', 'gpt-4.1-mini')),
            JobAiAssistance::TYPE_MATCH_EXPLANATION => (string) $this->settings->get('openai.job_matching_model', $this->settings->get('openai.default_model', 'gpt-4.1-mini')),
            default => (string) $this->settings->get('openai.default_model', 'gpt-4.1-mini'),
        };
    }

    private function prompt(User $user, ImportedJob $job, string $type): string
    {
        $user->loadMissing(['profile', 'questionnaireResponse', 'resumeReport']);
        $job->loadMissing('source');

        $profile = $user->profile;
        $resume = $user->resumeReport;
        $matchScore = $this->matchingService->score($user, $job);
        $jobText = mb_substr($job->displayDescription(), 0, 5000);

        $base = [
            'candidate' => [
                'headline' => $profile?->headline,
                'current_role' => $profile?->current_role,
                'experience_level' => $profile?->experience_level ?? $user->questionnaireResponse?->experience_level,
                'skills' => array_values(array_filter(array_merge($profile?->skills ?? [], $resume?->extracted_skills ?? []))),
                'preferred_roles' => $profile?->preferred_roles ?? [],
                'resume_summary' => $resume?->analysis['summary'] ?? null,
            ],
            'job' => [
                'title' => $job->title,
                'company' => $job->company,
                'category' => $job->category,
                'employment_type' => $job->employment_type,
                'location_type' => $job->location_type,
                'salary' => $job->salaryLabel(),
                'skills' => $job->skills ?? [],
                'tags' => $job->tags ?? [],
                'description' => $jobText,
                'match_score' => $matchScore,
            ],
        ];

        $instructions = match ($type) {
            JobAiAssistance::TYPE_COVER_LETTER => 'Return JSON with keys: content string. Write a concise, editable cover letter under 260 words. Do not invent experience. Do not include external URLs.',
            JobAiAssistance::TYPE_INTERVIEW_PREP => 'Return JSON with keys: questions array of 8 strings, preparation_tips array of 5 strings. Questions should be specific to the role and remote-work context.',
            JobAiAssistance::TYPE_MATCH_EXPLANATION => 'Return JSON with keys: reasons array of 5 strings, gaps array of up to 4 strings, content string. Explain why this job matches the candidate using the supplied signals.',
            default => 'Return JSON with key content string.',
        };

        return $instructions."\n\nContext JSON:\n".json_encode($base, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }

    /**
     * @param  array<string, mixed>  $payload
     */
    private function contentFromPayload(string $type, array $payload): string
    {
        if (is_string($payload['content'] ?? null) && trim($payload['content']) !== '') {
            return trim((string) $payload['content']);
        }

        if ($type === JobAiAssistance::TYPE_INTERVIEW_PREP) {
            $questions = collect($payload['questions'] ?? [])
                ->map(fn (mixed $item): string => trim((string) $item))
                ->filter()
                ->values();
            $tips = collect($payload['preparation_tips'] ?? [])
                ->map(fn (mixed $item): string => trim((string) $item))
                ->filter()
                ->values();

            return $questions
                ->map(fn (string $question, int $index): string => ($index + 1).'. '.$question)
                ->merge($tips->isNotEmpty() ? ['Tips:'] : [])
                ->merge($tips->map(fn (string $tip): string => '- '.$tip))
                ->join("\n");
        }

        if ($type === JobAiAssistance::TYPE_MATCH_EXPLANATION) {
            return collect($payload['reasons'] ?? [])
                ->map(fn (mixed $item): string => '- '.trim((string) $item))
                ->filter(fn (string $item): bool => $item !== '-')
                ->join("\n");
        }

        throw new RuntimeException('OpenAI did not return usable content.');
    }
}
