﻿<?php

namespace App\Http\Controllers;

use App\Models\JobDescription;
use App\Models\JobDescriptionActivityLog;
use App\Models\JobDescriptionComment;
use App\Models\JobDescriptionExport;
use App\Models\JobDescriptionSection;
use App\Models\JobDescriptionValidationCheck;
use App\Models\JobDescriptionVersion;
use App\Services\JDWizardAIService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class JDWizardController extends Controller
{
    public function __construct(private readonly JDWizardAIService $aiService) {}

    // Fetch all JD in the dashboard with cards
    public function index(Request $request): JsonResponse{
        $user  = Auth::user();
        $query = JobDescription::with(['creator', 'approver', 'publisher', 'organization'])
            ->withCount('sections');

        // Scope: org users see all JDs in their org; regular users see only their own
        if ($user->organization_id) {
            $query->where('organization_id', $user->organization_id);
        } else {
            $query->where('created_by', $user->id);
        }

        // Filters
        if ($request->filled('search')) {
            $search = $request->input('search');
            $query->where(function ($q) use ($search) {
                $q->where('job_title', 'like', "%{$search}%")
                  ->orWhere('department', 'like', "%{$search}%")
                  ->orWhere('level', 'like', "%{$search}%");
            });
        }

        if ($request->filled('department')) {
            $query->where('department', $request->input('department'));
        }

        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

        // Counts for cards
        $baseQuery = clone $query;
        $counts = [
            'total'     => (clone $baseQuery)->count(),
            'approved'  => (clone $baseQuery)->where('status', JobDescription::STATUS_APPROVED)->count(),
            'published' => (clone $baseQuery)->where('status', JobDescription::STATUS_PUBLISHED)->count(),
            'draft'     => (clone $baseQuery)->where('status', JobDescription::STATUS_DRAFT)->count(),
        ];

        // Pagination
        $perPage = $request->input('per_page', 15);
        $perPage = is_numeric($perPage) ? min(max((int) $perPage, 5), 100) : 15;

        $jobDescriptions = $query->latest()->paginate($perPage)->withQueryString();

        // Department list for the filter dropdown
        $departments = JobDescription::where('created_by', $user->id)
            ->when($user->organization_id, fn ($q) => $q->orWhere('organization_id', $user->organization_id))
            ->whereNotNull('department')
            ->distinct()
            ->pluck('department')
            ->sort()
            ->values();

        return response()->json([
            'success'          => true,
            'counts'           => $counts,
            'job_descriptions' => $jobDescriptions,
            'departments'      => $departments,
        ]);
    }

    // Create JD (AI Generattion)
    public function create(Request $request): JsonResponse{
        $validated = $request->validate([
            'job_title'       => 'required|string|max:255',
            'department'      => 'required|string|max:255',
            'level'           => 'required|string|max:255',
            'location'        => 'required|string|max:255',
            'employment_type' => 'required|string|max:100',
        ]);

        try {
            $result = DB::transaction(function () use ($validated) {
                $jd = JobDescription::create([
                    'organization_id' => Auth::user()->organization_id, // nullable
                    'job_title'       => $validated['job_title'],
                    'department'      => $validated['department'],
                    'level'           => $validated['level'],
                    'location'        => $validated['location'],
                    'employment_type' => $validated['employment_type'],
                    'status'          => JobDescription::STATUS_DRAFT,
                    'created_by'      => Auth::id(),
                ]);

                $this->log($jd, JobDescriptionActivityLog::EVENT_DRAFT_CREATED);

                // Generate AI sections
                $aiSections = $this->aiService->generateJD($jd);

                foreach (JobDescriptionSection::SECTION_TYPES as $index => $sectionType) {
                    JobDescriptionSection::create([
                        'job_description_id' => $jd->id,
                        'section_type'       => $sectionType,
                        'title'              => null,
                        'content'            => $aiSections[$sectionType],
                        'source'             => JobDescriptionSection::SOURCE_AI,
                        'version'            => 1,
                        'is_core'            => true,
                        'sort_order'         => $index,
                    ]);
                }

                $this->log($jd, JobDescriptionActivityLog::EVENT_AI_GENERATION_COMPLETE);

                // Save initial version snapshot
                JobDescriptionVersion::create([
                    'job_description_id' => $jd->id,
                    'version_number'     => 1,
                    'snapshot_json'      => $jd->fresh()->toSnapshot(),
                    'created_by'         => Auth::id(),
                ]);

                return $jd->load('sections');
            });

            return response()->json([
                'success'         => true,
                'message'         => 'Job description created and AI draft generated.',
                'job_description' => $result,
                'sections'        => $result->sections->values(),
            ], 201);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] create failed', ['error' => $e->getMessage()]);

            return response()->json([
                'success' => false,
                'message' => 'Failed to create job description. Please try again.',
            ], 500);
        }
    }

    // Manual section edit
    public function updateSection(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);
        $this->assertEditable($jobDescription);

        $validated = $request->validate([
            'section_type' => ['required', Rule::in(JobDescriptionSection::SECTION_TYPES)],
            'content'      => 'required|string|min:10',
        ]);

        try {
            $section = DB::transaction(function () use ($validated, $jobDescription) {
                $section = JobDescriptionSection::updateOrCreate(
                    [
                        'job_description_id' => $jobDescription->id,
                        'section_type'       => $validated['section_type'],
                    ],
                    [
                        'content'    => $validated['content'],
                        'source'     => JobDescriptionSection::SOURCE_USER,
                        'version'    => DB::raw('version + 1'),
                        'is_core'    => true,
                        'sort_order' => array_search(
                            $validated['section_type'],
                            JobDescriptionSection::SECTION_TYPES
                        ),
                    ]
                );

                $this->saveVersion($jobDescription);
                $this->log($jobDescription, JobDescriptionActivityLog::EVENT_SECTION_EDITED, [
                    'section_type' => $validated['section_type'],
                ]);

                return $section->fresh();
            });

            return response()->json([
                'success' => true,
                'message' => 'Section updated.',
                'section' => $section,
            ]);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] updateSection failed', [
                'id' => $jobDescription->id, 'error' => $e->getMessage(),
            ]);

            return response()->json(['success' => false, 'message' => 'Failed to update section.'], 500);
        }
    }

    // AI regeneration of a single section
    public function regenerateSection(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);
        $this->assertEditable($jobDescription);

        $validated = $request->validate([
            'section_type' => ['required', Rule::in(JobDescriptionSection::SECTION_TYPES)],
            'instruction'  => 'nullable|string',
        ]);

        try {
            $newContent = $this->aiService->regenerateSection(
                $jobDescription,
                $validated['section_type'],
                $validated['instruction'] ?? ''
            );

            $section = DB::transaction(function () use ($newContent, $validated, $jobDescription) {
                $section = JobDescriptionSection::updateOrCreate(
                    [
                        'job_description_id' => $jobDescription->id,
                        'section_type'       => $validated['section_type'],
                    ],
                    [
                        'content'    => $newContent,
                        'source'     => JobDescriptionSection::SOURCE_AI,
                        'version'    => DB::raw('version + 1'),
                        'is_core'    => true,
                        'sort_order' => array_search(
                            $validated['section_type'],
                            JobDescriptionSection::SECTION_TYPES
                        ),
                    ]
                );

                $this->saveVersion($jobDescription);
                $this->log($jobDescription, JobDescriptionActivityLog::EVENT_SECTION_REGENERATED, [
                    'section_type' => $validated['section_type'],
                ]);

                return $section->fresh();
            });

            return response()->json([
                'success' => true,
                'message' => 'Section regenerated.',
                'section' => $section,
            ]);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] regenerateSection failed', [
                'id' => $jobDescription->id, 'error' => $e->getMessage(),
            ]);

            return response()->json(['success' => false, 'message' => 'Regeneration failed. Please try again.'], 500);
        }
    }

    // Add a custom section
    public function addCustomSection(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);
        $this->assertEditable($jobDescription);

        $validated = $request->validate([
            'title'   => 'required|string|max:255',
            'content' => 'required|string|min:5',
        ]);

        try {
            $section = DB::transaction(function () use ($validated, $jobDescription) {
                // Custom sections are appended after all core sections
                $maxOrder = $jobDescription->sections()->max('sort_order') ?? 4;

                $section = JobDescriptionSection::create([
                    'job_description_id' => $jobDescription->id,
                    'section_type'       => null,
                    'title'              => $validated['title'],
                    'content'            => $validated['content'],
                    'source'             => JobDescriptionSection::SOURCE_USER,
                    'version'            => 1,
                    'is_core'            => false,
                    'sort_order'         => $maxOrder + 1,
                ]);

                $this->saveVersion($jobDescription);
                $this->log($jobDescription, JobDescriptionActivityLog::EVENT_CUSTOM_SECTION_ADDED, [
                    'title' => $validated['title'],
                ]);

                return $section;
            });

            return response()->json([
                'success' => true,
                'message' => 'Custom section added.',
                'section' => $section,
            ], 201);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] addCustomSection failed', [
                'id' => $jobDescription->id, 'error' => $e->getMessage(),
            ]);

            return response()->json(['success' => false, 'message' => 'Failed to add custom section.'], 500);
        }
    }

    // Only custom sections can be deleted
    public function deleteCustomSection(
        JobDescription $jobDescription,
        JobDescriptionSection $section
    ): JsonResponse {
        $this->authorizeJD($jobDescription);
        $this->assertEditable($jobDescription);

        if ($section->job_description_id !== $jobDescription->id) {
            return response()->json(['success' => false, 'message' => 'Section not found.'], 404);
        }

        if ($section->isCore()) {
            return response()->json([
                'success' => false,
                'message' => 'Core sections cannot be deleted.',
            ], 422);
        }

        DB::transaction(function () use ($jobDescription, $section) {
            $title = $section->title;
            $section->delete();

            $this->log($jobDescription, JobDescriptionActivityLog::EVENT_CUSTOM_SECTION_DELETED, [
                'title' => $title,
            ]);
        });

        return response()->json(['success' => true, 'message' => 'Custom section deleted.']);
    }

    // Runs all 14 checks via AI, persists each result
    public function validation(JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);

        try {
            $aiResult = $this->aiService->validateJD($jobDescription);

            DB::transaction(function () use ($aiResult, $jobDescription) {
                // Replace all previous check rows for this JD
                $jobDescription->validationChecks()->delete();

                foreach ($aiResult['checks'] as $check) {
                    JobDescriptionValidationCheck::create([
                        'job_description_id' => $jobDescription->id,
                        'section_type'       => $check['section_type']
                            ?? JobDescriptionSection::sectionTypeForCheck($check['check_key']),
                        'check_key'          => $check['check_key'],
                        'status'             => $check['status'],
                        'score'              => $check['score'] ?? 0,
                        'passed'             => $check['passed'],
                        'message'            => $check['message'] ?? null,
                    ]);
                }

                // Log the run
                $this->log($jobDescription, JobDescriptionActivityLog::EVENT_VALIDATION_RUN);

                $overallPassed = $aiResult['passed'] ?? false;
                $this->log(
                    $jobDescription,
                    $overallPassed
                        ? JobDescriptionActivityLog::EVENT_VALIDATION_PASSED
                        : JobDescriptionActivityLog::EVENT_VALIDATION_FAILED,
                    ['score' => $aiResult['score'] ?? null]
                );
            });

            // Build summary cards
            $checks        = $jobDescription->validationChecks()->get();
            $totalChecks   = $checks->count();
            $totalAligned  = $checks->where('passed', true)->count();
            $totalReview   = $checks->where('passed', false)->count();

            // Per-section passed status
            // A section is passed only if ALL its checks passed
            $sectionStatus = [];
            foreach (JobDescriptionSection::SECTION_TYPES as $sectionType) {
                $sectionChecks        = $checks->where('section_type', $sectionType);
                $sectionStatus[$sectionType] = $sectionChecks->isNotEmpty()
                    && $sectionChecks->where('passed', false)->isEmpty();
            }

            return response()->json([
                'success'        => true,
                'overall_passed' => $aiResult['passed'] ?? false,
                'overall_score'  => $aiResult['score'] ?? 0,
                'suggestions'    => $aiResult['suggestions'] ?? [],
                'cards'          => [
                    'total_checks'       => $totalChecks,
                    'total_aligned'      => $totalAligned,
                    'total_needs_review' => $totalReview,
                ],
                'section_status' => $sectionStatus,
                'checks'         => $checks->map(fn ($c) => [
                    'id'           => $c->id,
                    'check_key'    => $c->check_key,
                    'label'        => $c->label(),
                    'section_type' => $c->section_type,
                    'status'       => $c->status,
                    'score'        => $c->score,
                    'passed'       => $c->passed,
                    'message'      => $c->message,
                ])->values(),
                // Labels for the front-end to render without hardcoding
                'check_labels'   => JobDescriptionValidationCheck::CHECK_LABELS,
            ]);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] validation failed', [
                'id' => $jobDescription->id, 'error' => $e->getMessage(),
            ]);

            return response()->json(['success' => false, 'message' => 'Validation failed. Please try again.'], 500);
        }
    }

    // Saves/keeps as draft. Also allows reverting an approved JD back to draft.
    public function saveDraft(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);

        if ($jobDescription->isPublished()) {
            return response()->json([
                'success' => false,
                'message' => 'Published job descriptions cannot be reverted to draft.',
            ], 422);
        }

        $jobDescription->update([
            'status'      => JobDescription::STATUS_DRAFT,
            'approved_by' => null,
            'approved_at' => null,
        ]);

        $this->log($jobDescription, JobDescriptionActivityLog::EVENT_SAVED_AS_DRAFT);

        return response()->json(['success' => true, 'message' => 'Job description saved as draft.']);
    }

    // Approves the JD
    public function approve(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);

        if ($jobDescription->isPublished()) {
            return response()->json([
                'success' => false,
                'message' => 'Published job descriptions cannot be re-approved.',
            ], 422);
        }

        if (!$jobDescription->isDraft()) {
            return response()->json([
                'success' => false,
                'message' => 'Only draft job descriptions can be approved.',
            ], 422);
        }

        if (!$jobDescription->isComplete()) {
            return response()->json([
                'success' => false,
                'message' => 'All core sections must be present before approving.',
            ], 422);
        }

        $validated = $request->validate([
            'comment' => 'nullable|string|max:1000',
        ]);

        DB::transaction(function () use ($validated, $jobDescription) {
            $jobDescription->update([
                'status'      => JobDescription::STATUS_APPROVED,
                'approved_by' => Auth::id(),
                'approved_at' => now(),
            ]);

            if (!empty($validated['comment'])) {
                JobDescriptionComment::create([
                    'job_description_id' => $jobDescription->id,
                    'user_id'            => Auth::id(),
                    'comment'            => $validated['comment'],
                ]);
            }

            $this->log($jobDescription, JobDescriptionActivityLog::EVENT_APPROVED);
        });

        return response()->json(['success' => true, 'message' => 'Job description approved.']);
    }

    // Sends a pending JD back to draft with a reason comment
    public function sendBack(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeAdmin();
        $this->authorizeJD($jobDescription);

        if (!$jobDescription->isApproved()) {
            return response()->json([
                'success' => false,
                'message' => 'Only approved job descriptions can be sent back.',
            ], 422);
        }

        $validated = $request->validate([
            'reason' => 'nullable|string|max:1000',
        ]);

        DB::transaction(function () use ($validated, $jobDescription) {
            $jobDescription->update([
                'status'      => JobDescription::STATUS_DRAFT,
                'approved_by' => null,
                'approved_at' => null,
            ]);

            if (!empty($validated['reason'])) {
                JobDescriptionComment::create([
                    'job_description_id' => $jobDescription->id,
                    'user_id'            => Auth::id(),
                    'comment'            => '[Sent back for revision] ' . $validated['reason'],
                ]);
            }

            $this->log($jobDescription, JobDescriptionActivityLog::EVENT_SENT_BACK, [
                'reason' => $validated['reason'] ?? null,
            ]);
        });

        return response()->json(['success' => true, 'message' => 'Job description sent back for revision.']);
    }

    // Publish approved JD
    public function publish(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeAdmin();
        $this->authorizeJD($jobDescription);

        if (!$jobDescription->isApproved()) {
            return response()->json([
                'success' => false,
                'message' => 'A job description must be approved before it can be published.',
            ], 422);
        }

        DB::transaction(function () use ($jobDescription) {
            $jobDescription->update([
                'status'               => JobDescription::STATUS_PUBLISHED,
                'published_by'         => Auth::id(),
                'published_at'         => now(),
                'published_link_token' => Str::uuid()->toString(),
            ]);

            $this->log($jobDescription, JobDescriptionActivityLog::EVENT_PUBLISHED);
        });

        return response()->json([
            'success'    => true,
            'message'    => 'Job description published successfully.',
            'public_url' => route('jd.public', ['token' => $jobDescription->fresh()->published_link_token]),
        ]);
    }

    // Generates a PDF or DOCX file, or a shareable link.
    public function export(Request $request, JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);

        if (!$jobDescription->canBeExported()) {
            return response()->json([
                'success' => false,
                'message' => 'A job description must be published before it can be exported.',
            ], 422);
        }

        $validated = $request->validate([
            'export_type' => ['required', Rule::in([
                JobDescriptionExport::TYPE_PDF,
                JobDescriptionExport::TYPE_DOCX,
                JobDescriptionExport::TYPE_LINK,
            ])],
            'expires_at' => 'nullable|date|after:now',
        ]);

        try {
            $export = DB::transaction(function () use ($validated, $jobDescription) {
                $filePath   = null;
                $shareToken = null;
                $expiresAt  = null;

                if ($validated['export_type'] === JobDescriptionExport::TYPE_LINK) {
                    // Reuse the published token for the shareable link
                    $shareToken = $jobDescription->published_link_token;
                    $expiresAt  = $validated['expires_at'] ?? null; // no expiry by default for public link
                } else {
                    $filePath = $this->generateExportFile($jobDescription, $validated['export_type']);
                }

                return JobDescriptionExport::create([
                    'job_description_id' => $jobDescription->id,
                    'export_type'        => $validated['export_type'],
                    'exported_by'        => Auth::id(),
                    'file_path'          => $filePath,
                    'share_token'        => $shareToken,
                    'expires_at'         => $expiresAt,
                ]);
            });

            $responseData = ['success' => true, 'export_type' => $export->export_type];

            if ($export->export_type === JobDescriptionExport::TYPE_LINK) {
                $responseData['share_url'] = route('jd.public', ['token' => $export->share_token]);
            } else {
                $responseData['file_url'] = $export->file_url;
            }

            return response()->json($responseData);

        } catch (\Throwable $e) {
            Log::error('[JDWizardController] export failed', [
                'id' => $jobDescription->id, 'error' => $e->getMessage(),
            ]);

            return response()->json(['success' => false, 'message' => 'Export failed.'], 500);
        }
    }

    // Returns full JD data plus activity log for the preview screen.
    public function preview(JobDescription $jobDescription): JsonResponse
    {
        $this->authorizeJD($jobDescription);

        $jobDescription->load([
            'sections',
            'creator',
            'approver',
            'publisher',
            'organization',
            'validationChecks',
            'activityLog.user',
            'comments.user',
        ]);

        $checks = $jobDescription->validationChecks;

        return response()->json([
            'success'         => true,
            'job_description' => $jobDescription,
            'can_be_edited'   => $jobDescription->canBeEdited(),
            'can_be_exported' => $jobDescription->canBeExported(),
            'validation_summary' => $checks->isNotEmpty() ? [
                'total_checks'       => $checks->count(),
                'total_aligned'      => $checks->where('passed', true)->count(),
                'total_needs_review' => $checks->where('passed', false)->count(),
            ] : null,
            'activity_log' => $jobDescription->activityLog->map(fn ($entry) => [
                'id'         => $entry->id,
                'event'      => $entry->event,
                'label'      => $entry->label(),
                'meta'       => $entry->meta,
                'user'       => $entry->user?->name ?? 'System',
                'created_at' => $entry->created_at,
            ])->values(),
            'public_url' => $jobDescription->isPublished()
                ? route('jd.public', ['token' => $jobDescription->published_link_token])
                : null,
        ]);
    }

    // Resolves by published_link_token.
    public function publicView(string $token)
    {
        $jobDescription = JobDescription::where('published_link_token', $token)
            ->where('status', JobDescription::STATUS_PUBLISHED)
            ->with(['sections' => fn ($q) => $q->orderBy('sort_order'), 'organization'])
            ->firstOrFail();

        // Return a view for Blade rendering
        return view('jd-wizard.public', compact('jobDescription'));
    }

    // Private Helpers

    /**
     * Ensure the JD belongs to the authenticated user's organization or is owned by them directly (for personal JDs with no org).
     */
    private function authorizeJD(JobDescription $jobDescription): void
    {
        $user = Auth::user();

        // Org-scoped JD
        if ($jobDescription->organization_id && $user->organization_id) {
            if ((int) $jobDescription->organization_id !== (int) $user->organization_id) {
                abort(403, 'This job description does not belong to your organization.');
            }
        }

        // Personal JD or must be creator
        if ((int) $jobDescription->created_by !== (int) $user->id) {
            abort(403, 'You do not have permission to access this job description.');
        }
    }

    /**
     * Reject any mutation on a published JD.
     */
    private function assertEditable(JobDescription $jobDescription): void
    {
        if (!$jobDescription->canBeEdited()) {
            abort(422, 'Published job descriptions cannot be edited.');
        }
    }

    /**
     * Save a version snapshot of the current JD state.
     */
    private function saveVersion(JobDescription $jobDescription): void
    {
        JobDescriptionVersion::create([
            'job_description_id' => $jobDescription->id,
            'version_number'     => $jobDescription->getLatestVersionNumber() + 1,
            'snapshot_json'      => $jobDescription->fresh()->toSnapshot(),
            'created_by'         => Auth::id(),
        ]);
    }

    /**
     * Append an entry to the activity log.
     */
    private function log(JobDescription $jobDescription, string $event, array $meta = []): void
    {
        JobDescriptionActivityLog::create([
            'job_description_id' => $jobDescription->id,
            'user_id'            => Auth::id(),
            'event'              => $event,
            'meta'               => empty($meta) ? null : $meta,
        ]);
    }

    // Export file generation
    private function generateExportFile(JobDescription $jobDescription, string $type): string
    {
        $jobDescription->loadMissing(['sections', 'creator', 'organization']);

        Storage::disk('public')->makeDirectory('jd_exports');

        $slug     = Str::slug($jobDescription->job_title);
        $filename = "jd_exports/{$slug}_{$jobDescription->id}.{$type}";

        match ($type) {
            JobDescriptionExport::TYPE_PDF  => $this->exportAsPdf($jobDescription, $filename),
            JobDescriptionExport::TYPE_DOCX => $this->exportAsDocx($jobDescription, $filename),
        };

        return $filename;
    }

    private function exportAsPdf(JobDescription $jobDescription, string $filename): void
    {
        $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView(
            'jd-wizard.export-pdf',
            ['jobDescription' => $jobDescription]
        );
        $pdf->setPaper('A4', 'portrait');
        Storage::disk('public')->put($filename, $pdf->output());
    }

    private function exportAsDocx(JobDescription $jobDescription, string $filename): void
    {
        $phpWord = new \PhpOffice\PhpWord\PhpWord();
        $phpWord->setDefaultFontName('Arial');
        $phpWord->setDefaultFontSize(11);

        $phpWord->getDocInfo()
            ->setTitle($jobDescription->job_title)
            ->setCreator($jobDescription->creator?->name ?? 'System')
            ->setCompany($jobDescription->organization?->name ?? '');

        $phpWord->addTitleStyle(1, ['bold' => true, 'size' => 20, 'color' => '1F3864'], ['spaceAfter' => 120]);
        $phpWord->addTitleStyle(2, ['bold' => true, 'size' => 13, 'color' => '2E5496'], ['spaceBefore' => 240, 'spaceAfter' => 80]);
        $phpWord->addParagraphStyle('body', ['spaceAfter' => 80, 'lineHeight' => 1.3]);
        $phpWord->addFontStyle('metaLabel', ['bold' => true, 'size' => 10, 'color' => '666666']);
        $phpWord->addFontStyle('metaValue', ['size' => 10]);
        $phpWord->addFontStyle('bodyText', ['size' => 11]);

        $section = $phpWord->addSection([
            'marginTop' => 1440, 'marginBottom' => 1440,
            'marginLeft' => 1440, 'marginRight' => 1440,
        ]);

        $section->addTitle($jobDescription->job_title, 1);

        foreach (array_filter([
            'Department'      => $jobDescription->department,
            'Level'           => $jobDescription->level,
            'Location'        => $jobDescription->location,
            'Employment Type' => $jobDescription->employment_type,
        ]) as $label => $value) {
            $para = $section->addTextRun(['spaceAfter' => 40]);
            $para->addText("{$label}: ", 'metaLabel');
            $para->addText($value, 'metaValue');
        }

        $section->addTextBreak(1);

        // Core sections first, then custom sections
        foreach ($jobDescription->sections->sortBy('sort_order') as $jdSection) {
            if (empty(trim($jdSection->content))) continue;

            $section->addTitle($jdSection->label(), 2);

            foreach (preg_split('/\r\n|\r|\n/', trim($jdSection->content)) as $line) {
                $line = trim($line);
                if ($line === '') { $section->addTextBreak(1); continue; }

                if (preg_match('/^[-•*]\s+(.+)/', $line, $matches)) {
                    $section->addListItem(
                        $matches[1], 0, 'bodyText',
                        ['listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_BULLET_FILLED]
                    );
                } else {
                    $section->addText($line, 'bodyText', 'body');
                }
            }
            $section->addTextBreak(1);
        }

        $footer = $section->addFooter();
        $footer->addPreserveText(
            ($jobDescription->organization?->name ?? '') . '  |  Page {PAGE} of {NUMPAGES}',
            ['size' => 9, 'color' => '999999'],
            ['alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER]
        );

        $tmpPath = tempnam(sys_get_temp_dir(), 'jd_docx_') . '.docx';
        $writer  = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
        $writer->save($tmpPath);
        Storage::disk('public')->put($filename, file_get_contents($tmpPath));
        @unlink($tmpPath);
    