﻿<?php

namespace App\Services;

use App\Models\JobDescription;
use App\Models\JobDescriptionSection;
use App\Models\JobDescriptionValidationCheck;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use RuntimeException;

class JDWizardAIService
{
    // OpenAI Responses API endpoint
    private const RESPONSES_API = 'https://api.openai.com/v1/responses';

    // Prompt IDs
    private const PROMPT_GENERATE_JD          = 'pmpt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    private const PROMPT_REGENERATE_SECTION   = 'pmpt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    private const PROMPT_VALIDATE_JD          = 'pmpt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    private const PROMPT_SUGGEST_COMPETENCIES = 'pmpt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

    private int $timeoutSeconds = 60;

    /**
     * Generate all five core JD sections.
     *
     * Returns an array keyed by section_type:
     *   ['role_summary' => '...', 'key_responsibilities' => '...', ...]
     *
     * @throws RuntimeException
     */
    public function generateJD(JobDescription $jobDescription): array
    {
        $payload  = $this->buildJDPayload($jobDescription);

        Log::info('[JDWizardAIService] generateJD called', ['job_description_id' => $jobDescription->id]);

        $response = $this->callAI(self::PROMPT_GENERATE_JD, $payload);
        $sections = $this->parseJsonResponse($response);

        foreach (JobDescriptionSection::SECTION_TYPES as $type) {
            if (empty($sections[$type])) {
                throw new RuntimeException("AI response missing section: {$type}");
            }
        }

        Log::info('[JDWizardAIService] generateJD completed', ['job_description_id' => $jobDescription->id]);

        return $sections;
    }

    /**
     * Regenerate a single section with an optional user instruction.
     *
     * @throws RuntimeException
     */
    public function regenerateSection(
        JobDescription $jobDescription,
        string $sectionType,
        string $instruction = ''
    ): string {
        $this->assertValidSectionType($sectionType);

        $jobDescription->loadMissing('sections');

        $payload = $this->buildJDPayload($jobDescription);
        $payload['target_section']    = $sectionType;
        $payload['instruction']       = $instruction;
        $payload['existing_sections'] = $jobDescription->sections
            ->where('is_core', true)
            ->mapWithKeys(fn ($s) => [$s->section_type => $s->content])
            ->toArray();

        Log::info('[JDWizardAIService] regenerateSection called', [
            'job_description_id' => $jobDescription->id,
            'section_type'       => $sectionType,
        ]);

        $content = $this->callAI(self::PROMPT_REGENERATE_SECTION, $payload);

        Log::info('[JDWizardAIService] regenerateSection completed', [
            'job_description_id' => $jobDescription->id,
            'section_type'       => $sectionType,
        ]);

        return trim($content);
    }

    /**
     * Run validation and alignment checks against the JD.
     *
     * The AI must return JSON in this exact shape:
     * {
     *   "passed": true|false,         // overall: true only if ALL checks passed
     *   "score": 84,                  // overall score 0-100
     *   "checks": [
     *     {
     *       "check_key":    "inclusive_language",
     *       "section_type": "core_competencies",
     *       "status":       "pass"|"fail"|"warning",
     *       "score":        90,
     *       "passed":       true,
     *       "message":      "Language is inclusive and bias-free."
     *     },
     *     ...
     *   ],
     *   "suggestions": ["...", "..."]
     * }
     *
     * @throws RuntimeException
     */
    public function validateJD(JobDescription $jobDescription): array
    {
        $jobDescription->loadMissing('sections');

        $payload = [
            'job_title'       => $jobDescription->job_title,
            'department'      => $jobDescription->department,
            'level'           => $jobDescription->level,
            'employment_type' => $jobDescription->employment_type,
            'location'        => $jobDescription->location,
            'sections'        => $jobDescription->sections
                ->where('is_core', true)
                ->mapWithKeys(fn ($s) => [$s->section_type => $s->content])
                ->toArray(),
            // Each check must include section_type so the front-end
            // knows which section to navigate to on "review"
            'checks_required' => array_map(
                fn ($checkKey) => [
                    'check_key'    => $checkKey,
                    'section_type' => JobDescriptionSection::sectionTypeForCheck($checkKey),
                ],
                JobDescriptionValidationCheck::CHECK_KEYS
            ),
        ];

        Log::info('[JDWizardAIService] validateJD called', ['job_description_id' => $jobDescription->id]);

        $response = $this->callAI(self::PROMPT_VALIDATE_JD, $payload);
        $result   = $this->parseJsonResponse($response);

        // Validate the shape we depend on
        if (!isset($result['checks']) || !is_array($result['checks'])) {
            throw new RuntimeException('AI validation response missing "checks" array.');
        }

        Log::info('[JDWizardAIService] validateJD completed', [
            'job_description_id' => $jobDescription->id,
            'score'              => $result['score'] ?? null,
            'passed'             => $result['passed'] ?? null,
        ]);

        return $result;
    }

    /**
     * Suggest competencies for a given job title / level.
     * Returns a flat array of competency strings.
     *
     * @throws RuntimeException
     */
    public function suggestCompetencies(JobDescription $jobDescription): array
    {
        $payload = [
            'job_title'       => $jobDescription->job_title,
            'department'      => $jobDescription->department,
            'level'           => $jobDescription->level,
            'employment_type' => $jobDescription->employment_type,
        ];

        Log::info('[JDWizardAIService] suggestCompetencies called', ['job_description_id' => $jobDescription->id]);

        $response     = $this->callAI(self::PROMPT_SUGGEST_COMPETENCIES, $payload);
        $parsed       = $this->parseJsonResponse($response);
        $competencies = $parsed['competencies'] ?? [];

        Log::info('[JDWizardAIService] suggestCompetencies completed', [
            'job_description_id' => $jobDescription->id,
            'count'              => count($competencies),
        ]);

        return $competencies;
    }

    // Private Helpers

    /**
     * Call the OpenAI Responses API using a stored prompt ID.
     * Retries up to 3 times with exponential back-off.
     *
     * @throws RuntimeException
     */
    private function callAI(string $promptId, array $payload): string
    {
        $maxRetries    = 3;
        $lastException = null;

        for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
            try {
                $response = Http::withToken(config('services.openai.key'))
                    ->timeout($this->timeoutSeconds)
                    ->post(self::RESPONSES_API, [
                        'prompt' => ['id' => $promptId],
                        'input'  => json_encode($payload),
                    ]);

                if ($response->successful()) {
                    $content = collect($response->json('output'))
                        ->flatMap(fn ($item) => $item['content'] ?? [])
                        ->firstWhere('type', 'output_text')['text'] ?? null;

                    if (empty($content)) {
                        throw new RuntimeException('AI returned an empty output_text block.');
                    }

                    return $content;
                }

                Log::warning("[JDWizardAIService] HTTP error on attempt {$attempt}", [
                    'prompt_id' => $promptId,
                    'status'    => $response->status(),
                    'body'      => $response->body(),
                ]);

                throw new RuntimeException(
                    "AI provider returned HTTP {$response->status()}: {$response->body()}"
                );

            } catch (RuntimeException $e) {
                $lastException = $e;

                if ($attempt < $maxRetries) {
                    $sleepSeconds = $attempt * 2;
                    Log::info("[JDWizardAIService] Retrying in {$sleepSeconds}s (attempt {$attempt}/{$maxRetries})");
                    sleep($sleepSeconds);
                }
            }
        }

        Log::error('[JDWizardAIService] All retry attempts exhausted', [
            'prompt_id' => $promptId,
            'error'     => $lastException?->getMessage(),
        ]);

        throw new RuntimeException(
            "AI service failed after {$maxRetries} attempts: " . $lastException?->getMessage()
        );
    }

    /**
     * Parse a JSON string from the AI, stripping any accidental markdown fences.
     *
     * @throws RuntimeException
     */
    private function parseJsonResponse(string $raw): array
    {
        $clean   = preg_replace('/^```(?:json)?\s*/i', '', trim($raw));
        $clean   = preg_replace('/\s*```$/', '', $clean);
        $decoded = json_decode(trim($clean), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            Log::error('[JDWizardAIService] JSON parse failure', [
                'raw'   => $raw,
                'error' => json_last_error_msg(),
            ]);
            throw new RuntimeException('Failed to parse AI response as JSON: ' . json_last_error_msg());
        }

        return $decoded;
    }

    /**
     * Common job payload for generate / regenerate calls.
     */
    private function buildJDPayload(JobDescription $jobDescription): array
    {
        return [
            'job_title'       => $jobDescription->job_title,
            'department'      => $jobDescription->department,
            'level'           => $jobDescription->level,
            'location'        => $jobDescription->location,
            'employment_type' => $jobDescription->employment_type,
        ];
    }

    /**
     * Guard against unsupported section types.
     *
     * @throws RuntimeException
     */
    private function assertValidSectionType(string $type): void
    {
        if (!in_array($type, JobDescriptionSection::SECTION_TYPES, true)) {
            throw new RuntimeException(
                "Invalid section type: {$type}. Must be one of: " .
                implode(', ', JobDescriptionSection::SECTION_TYPES)
            );
        }
    