﻿<?php

namespace App\Http\Controllers;

use App\Models\Department;
use App\Models\JobDescription;
use App\Models\JobDescriptionActivityLog;
use App\Models\JobDescriptionComment;
use App\Models\JobDescriptionSection;
use App\Models\JobDescriptionValidationCheck;
use App\Models\JobDescriptionVersion;
use App\Services\JDWizardAIService;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\PhpWord;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Stub controller for JD Wizard UI preview.
 * TODO: Backend developer to replace with real logic, model bindings, and repository.
 */
class JobDescriptionController extends Controller
{
    public function __construct(private readonly JDWizardAIService $aiService) {}

    public function index(Request $request): View
    {
        $user = Auth::user();
 
        // ── Base query scoped to the authenticated user ───────────────────────
        // Org admins see every JD belonging to their org.
        // Everyone else (including org-less users) sees only their own JDs.
        $query = JobDescription::with('creator')
            ->when(
                $user->organization_id,
                fn ($q) => $q->where('organization_id', $user->organization_id),
                fn ($q) => $q->where('created_by', $user->id)
            );
 
        // ── Search ────────────────────────────────────────────────────────────
        // Reads ?search= from the query string
        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(DB::raw("CONCAT('JD-', LPAD(id, 3, '0'))"), 'like', "%{$search}%");
            });
        }
 
        // ── Department filter ─────────────────────────────────────────────────
        // Reads ?department= from the query string
        if ($request->filled('department') && $request->input('department') !== 'All Departments') {
            $query->where('department', $request->input('department'));
        }
 
        // ── Status filter ─────────────────────────────────────────────────────
        // Reads ?status= from the query string
        if ($request->filled('status') && $request->input('status') !== 'All Status') {
            $statusMap = [
                'Draft'       => JobDescription::STATUS_DRAFT,
                'Approved'    => JobDescription::STATUS_APPROVED,
                'Published'   => JobDescription::STATUS_PUBLISHED,
                'Unpublished' => JobDescription::STATUS_DRAFT, // treated as draft in our model
            ];
            $mapped = $statusMap[$request->input('status')] ?? null;
            if ($mapped) {
                $query->where('status', $mapped);
            }
        }
 
        // ── Stats cards (computed from the scoped base, before filters) ───────
        // Clone the scoped (but un-filtered) query for accurate card counts
        $scopedBase = JobDescription::when(
            $user->organization_id,
            fn ($q) => $q->where('organization_id', $user->organization_id),
            fn ($q) => $q->where('created_by', $user->id)
        );
 
        $stats = [
            'total'     => (clone $scopedBase)->count(),
            'approved'  => (clone $scopedBase)->where('status', JobDescription::STATUS_APPROVED)->count(),
            'published' => (clone $scopedBase)->where('status', JobDescription::STATUS_PUBLISHED)->count(),
            'drafts'    => (clone $scopedBase)->where('status', JobDescription::STATUS_DRAFT)->count(),
        ];
 
        // ── Per-page ──────────────────────────────────────────────────────────
        // Reads ?per_page= from the query string; capped between 5 and 100
        $perPage = $request->input('per_page', 10);
        $perPage = is_numeric($perPage) ? min(max((int) $perPage, 5), 100) : 10;
 
        // ── Paginate and preserve all active query string params ──────────────
        $jobDescriptions = $query->latest()->paginate($perPage)->withQueryString();
 
        // Append the formatted job_id to each item so the blade can render it
        $jobDescriptions->getCollection()->transform(function ($jd) {
            $jd->job_id = 'JD-' . str_pad($jd->id, 3, '0', STR_PAD_LEFT);
            $jd->author = $jd->creator?->name ?? '—';
            return $jd;
        });
 
        // ── Department list for the filter datalist ───────────────────────────
        // Pull distinct departments actually used by JDs visible to this user,
        // merged with the global seeded list so new values still appear.
        $usedDepartments = (clone $scopedBase)
            ->whereNotNull('department')
            ->distinct()
            ->pluck('department');
 
        $seededDepartments = Department::ordered()->pluck('name');
 
        $departments = $usedDepartments->merge($seededDepartments)
            ->unique()
            ->sort()
            ->values();
 
        // Pass total unfiltered count so the blade can show filters even when
        // the current filter returns zero results
        $totalUnfiltered = $stats['total'];
 
        return view('dashboard.job-descriptions.index', compact(
            'stats',
            'jobDescriptions',
            'totalUnfiltered',
            'departments'
        ));
    }

    public function templates(): View
    {
        return view('dashboard.job-descriptions.templates');
    }

    public function create(): View
    {
        // $departments = Department::ordered()->pluck('name');
        $departments = JobDescription::query()
            ->whereNotNull('department')
            ->where('department', '!=', '')
            ->distinct()
            ->orderBy('department')
            ->pluck('department');
        return view('dashboard.job-descriptions.create', compact('departments'));
    }

    public function store(Request $request)
    {
        $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) {
                // 1. Create the draft
                $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(),
                ]);
 
                // 2. Log draft created
                JobDescriptionActivityLog::create([
                    'job_description_id' => $jd->id,
                    'user_id'            => Auth::id(),
                    'event'              => JobDescriptionActivityLog::EVENT_DRAFT_CREATED,
                    'meta'               => null,
                ]);
 
                // 3. Generate AI sections
                $aiSections = $this->aiService->generateJD($jd);
 
                foreach (JobDescriptionSection::SECTION_TYPES as $index => $sectionType) {
                    $raw = $aiSections[$sectionType] ?? '';
 
                    // The AI may return a section as an array of bullet strings
                    // or as a plain string. Normalise to a single string so the
                    // longText column never receives an array.
                    if (is_array($raw)) {
                        $content = implode("\n", array_map('strval', $raw));
                    } else {
                        $content = (string) $raw;
                    }
 
                    JobDescriptionSection::create([
                        'job_description_id' => $jd->id,
                        'section_type'       => $sectionType,
                        'title'              => null,
                        'content'            => $content,
                        'source'             => JobDescriptionSection::SOURCE_AI,
                        'version'            => 1,
                        'is_core'            => true,
                        'sort_order'         => $index,
                    ]);
                }
 
                // 4. Log AI generation complete
                JobDescriptionActivityLog::create([
                    'job_description_id' => $jd->id,
                    'user_id'            => Auth::id(),
                    'event'              => JobDescriptionActivityLog::EVENT_AI_GENERATION_COMPLETE,
                    'meta'               => null,
                ]);
 
                // 5. 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;
            });

            return redirect()->route('dashboard.job-descriptions.show', $result->id);
            return redirect()->route('dashboard.job-descriptions.generating', $result->id);
 
        } catch (\Throwable $e) {
            Log::error('[JobDescriptionController] store failed', [
                'error' => $e->getMessage(),
            ]);
 
            return back()
                ->withInput()
                ->withErrors(['error' => 'Failed to generate job description. Please try again.']);
        }
    }

    public function show($jobDescription): View
    {
        $jobDescription = JobDescription::with([
            'sections' => fn ($q) => $q->orderBy('sort_order'),
            'creator',
        ])->findOrFail($jobDescription);
 
        $this->authorizeAccess($jobDescription);
 
        // Map sections into the shape the blade expects
        $roleSummary    = $jobDescription->getSection(JobDescriptionSection::TYPE_ROLE_SUMMARY)?->content ?? '';
        $responsibilities = $jobDescription->getSection(JobDescriptionSection::TYPE_KEY_RESPONSIBILITIES)?->content ?? '';
        $requiredSkills = $jobDescription->getSection(JobDescriptionSection::TYPE_REQUIRED_SKILLS)?->content ?? '';
        $qualifications = $jobDescription->getSection(JobDescriptionSection::TYPE_QUALIFICATIONS)?->content ?? '';
        $competencies   = $jobDescription->getSection(JobDescriptionSection::TYPE_CORE_COMPETENCIES)?->content ?? '';
 
        // Parse bullet-point lines into arrays for @foreach rendering in blade
        // Strip leading bullet markers (-, •, *, numbered "1." etc.) so the
        // blade's own bullet/number rendering is used consistently.
        $parseLines = fn (string $text): array => array_values(
            array_filter(
                array_map(
                    fn ($l) => preg_replace('/^\s*(?:[-•*]|\d+[.)]?)\s+/', '', trim($l)),
                    preg_split('/\r\n|\r|\n/', $text)
                ),
                fn ($l) => $l !== ''
            )
        );
 
        // Attach the parsed data directly onto the model as dynamic properties
        // so the blade's $jobDescription->responsibilities etc. work
        $jobDescription->role_summary    = $roleSummary;
        $jobDescription->responsibilities = $parseLines($responsibilities);
        $jobDescription->required_skills  = $parseLines($requiredSkills);
        $jobDescription->qualifications   = $parseLines($qualifications);
        $jobDescription->competencies     = $parseLines($competencies);
 
        // job_id formatted as JD-001 etc.
        $jobDescription->job_id = 'JD-' . str_pad($jobDescription->id, 3, '0', STR_PAD_LEFT);
 
        return view('dashboard.job-descriptions.show', compact('jobDescription'));
    }

    /**
     * Detail view for a single job description (non-wizard view).
     */
    public function detail($jobDescription): View
    {
        $jobDescription = JobDescription::with([
            'sections'  => fn ($q) => $q->orderBy('sort_order'),
            'creator',
            'approver',
            'publisher',
        ])->findOrFail($jobDescription);
 
        $this->authorizeAccess($jobDescription);
 
        // Strip leading bullet markers (-, •, *, numbered "1." etc.) so the
        // blade's own bullet/number rendering is used consistently.
        $parseLines = fn (string $text): array => array_values(
            array_filter(
                array_map(
                    fn ($l) => preg_replace('/^\s*(?:[-•*]|\d+[.)]?)\s+/', '', trim($l)),
                    preg_split('/\r\n|\r|\n/', $text)
                ),
                fn ($l) => $l !== ''
            )
        );
 
        $jobDescription->job_id           = 'JD-' . str_pad($jobDescription->id, 3, '0', STR_PAD_LEFT);
        $jobDescription->role_summary     = $jobDescription->getSection(JobDescriptionSection::TYPE_ROLE_SUMMARY)?->content ?? '';
        $jobDescription->responsibilities  = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_KEY_RESPONSIBILITIES)?->content ?? '');
        $jobDescription->required_skills   = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_REQUIRED_SKILLS)?->content ?? '');
        $jobDescription->qualifications    = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_QUALIFICATIONS)?->content ?? '');
        $jobDescription->competencies      = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_CORE_COMPETENCIES)?->content ?? '');
        $jobDescription->created_by        = $jobDescription->creator?->name ?? '—';
        $jobDescription->approved_by       = $jobDescription->approver?->name ?? '—';
 
        return view('dashboard.job-descriptions.detail', compact('jobDescription'));
    }


    /**
     * Public view for sharing job descriptions without authentication.
     */
    public function publicView($jobId, $slug): View
    {
        // job_id format is JD-001 → extract the numeric ID
        $numericId = ltrim(str_replace('JD-', '', strtoupper($jobId)), '0');
 
        $jobDescription = JobDescription::with([
            'sections'     => fn ($q) => $q->orderBy('sort_order'),
            'organization',
        ])
        ->where('id', $numericId)
        ->where('status', JobDescription::STATUS_PUBLISHED)
        ->firstOrFail();
 
        // Strip leading bullet markers (-, •, *, numbered "1." etc.) so the
        // blade's own bullet/number rendering is used consistently.
        $parseLines = fn (string $text): array => array_values(
            array_filter(
                array_map(
                    fn ($l) => preg_replace('/^\s*(?:[-•*]|\d+[.)]?)\s+/', '', trim($l)),
                    preg_split('/\r\n|\r|\n/', $text)
                ),
                fn ($l) => $l !== ''
            )
        );
 
        $jobDescription->job_id          = $jobId;
        $jobDescription->role_summary    = $jobDescription->getSection(JobDescriptionSection::TYPE_ROLE_SUMMARY)?->content ?? '';
        $jobDescription->responsibilities = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_KEY_RESPONSIBILITIES)?->content ?? '');
        $jobDescription->required_skills  = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_REQUIRED_SKILLS)?->content ?? '');
        $jobDescription->qualifications   = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_QUALIFICATIONS)?->content ?? '');
        $jobDescription->competencies     = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_CORE_COMPETENCIES)?->content ?? '');
 
        return view('dashboard.job-descriptions.public', compact('jobDescription'));
    }

    public function edit($jobDescription): View
    {
        $jobDescription = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jobDescription);
 
        $departments = Department::ordered()->pluck('name');
 
        return view('dashboard.job-descriptions.create', compact('jobDescription', 'departments'));
    }

    public function update(Request $request, $jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if ($jd->isPublished()) {
            return back()->withErrors(['error' => 'Published job descriptions cannot be edited.']);
        }
 
        // Section update (triggered by Save Changes)
        if ($request->input('_action') === 'update_section') {
            $validated = $request->validate([
                'section_type' => ['required', Rule::in(JobDescriptionSection::SECTION_TYPES)],
                'content'      => 'required|string|min:1',
            ]);
 
            DB::transaction(function () use ($validated, $jd) {
                $sortOrder = array_search(
                    $validated['section_type'],
                    JobDescriptionSection::SECTION_TYPES
                );
 
                // Use firstOrNew so we can increment version correctly on both
                // insert and update paths — DB::raw('version + 1') only works
                // on UPDATE and throws on INSERT when the column is cast to int.
                $section = JobDescriptionSection::firstOrNew([
                    'job_description_id' => $jd->id,
                    'section_type'       => $validated['section_type'],
                ]);
 
                $section->content    = $validated['content'];
                $section->source     = JobDescriptionSection::SOURCE_USER;
                $section->version    = $section->exists ? ($section->version + 1) : 1;
                $section->is_core    = true;
                $section->sort_order = $sortOrder;
                $section->save();
 
                JobDescriptionActivityLog::create([
                    'job_description_id' => $jd->id,
                    'user_id'            => Auth::id(),
                    'event'              => JobDescriptionActivityLog::EVENT_SECTION_EDITED,
                    'meta'               => ['section_type' => $validated['section_type']],
                ]);
            });
 
            return back()->with('success', 'Section updated.');
        }
 
        // Metadata update
        $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',
        ]);
 
        $jd->update($validated);
 
        return redirect()->route('dashboard.job-descriptions.show', $jd->id);
    }

    public function destroy($jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        $jd->delete();
 
        return redirect()->route('dashboard.job-descriptions.index')
            ->with('success', 'Job description deleted!');
    }

    public function generating($jobDescription): View
    {
        $jobDescription = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jobDescription);
 
        return view('dashboard.job-descriptions.generating', compact('jobDescription'));
    }

    public function validateStep($jobDescription): View
    {
        $jobDescription = JobDescription::with([
            'sections' => fn ($q) => $q->orderBy('sort_order'),
            'validationChecks',
        ])->findOrFail($jobDescription);
 
        $this->authorizeAccess($jobDescription);
 
        // Run the AI validation and persist results
        try {
            $aiResult = $this->aiService->validateJD($jobDescription);
 
            DB::transaction(function () use ($aiResult, $jobDescription) {
                $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,
                    ]);
                }

                JobDescriptionActivityLog::create([
                    'job_description_id' => $jobDescription->id,
                    'user_id'            => Auth::id(),
                    'event'              => JobDescriptionActivityLog::EVENT_VALIDATION_RUN,
                    'meta'               => null,
                ]);
 
                $overallPassed = $aiResult['passed'] ?? false;
                JobDescriptionActivityLog::create([
                    'job_description_id' => $jobDescription->id,
                    'user_id'            => Auth::id(),
                    'event'              => $overallPassed
                        ? JobDescriptionActivityLog::EVENT_VALIDATION_PASSED
                        : JobDescriptionActivityLog::EVENT_VALIDATION_FAILED,
                    'meta'               => ['score' => $aiResult['score'] ?? null],
                ]);
            });
 
        } catch (\Throwable $e) {
            Log::error('[JobDescriptionController] validateStep AI failed', [
                'id'    => $jobDescription->id,
                'error' => $e->getMessage(),
            ]);
        }
 
        // Reload fresh check results from DB
        $jobDescription->load('validationChecks');
        $checks = $jobDescription->validationChecks;
 
        $validationStats = [
            'total'        => $checks->count(),
            'aligned'      => $checks->where('passed', true)->count(),
            'needs_review' => $checks->where('passed', false)->count(),
        ];
 
        return view('dashboard.job-descriptions.validate', compact('jobDescription', 'validationStats'));
    }

    public function review($jobDescription): View
    {
        $jobDescription = JobDescription::with([
            'sections'     => fn ($q) => $q->orderBy('sort_order'),
            'creator',
            'approver',
            'activityLog.user',
            'validationChecks',
        ])->findOrFail($jobDescription);
 
        $this->authorizeAccess($jobDescription);
 
        // Strip leading bullet markers (-, •, *, numbered "1." etc.) so the
        // blade's own bullet/number rendering is used consistently.
        $parseLines = fn (string $text): array => array_values(
            array_filter(
                array_map(
                    fn ($l) => preg_replace('/^\s*(?:[-•*]|\d+[.)]?)\s+/', '', trim($l)),
                    preg_split('/\r\n|\r|\n/', $text)
                ),
                fn ($l) => $l !== ''
            )
        );
 
        $jobDescription->job_id          = 'JD-' . str_pad($jobDescription->id, 3, '0', STR_PAD_LEFT);
        $jobDescription->role_summary    = $jobDescription->getSection(JobDescriptionSection::TYPE_ROLE_SUMMARY)?->content ?? '';
        $jobDescription->responsibilities = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_KEY_RESPONSIBILITIES)?->content ?? '');
        $jobDescription->required_skills  = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_REQUIRED_SKILLS)?->content ?? '');
        $jobDescription->qualifications   = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_QUALIFICATIONS)?->content ?? '');
        $jobDescription->competencies     = $parseLines($jobDescription->getSection(JobDescriptionSection::TYPE_CORE_COMPETENCIES)?->content ?? '');
 
        return view('dashboard.job-descriptions.review', compact('jobDescription'));
    }

    public function publish($id): View
    {
        $jobDescription = JobDescription::with(['creator', 'approver'])->findOrFail($id);
        $this->authorizeAccess($jobDescription);

        DB::transaction(function () use ($jobDescription) {

            $jobId = 'JD-' . str_pad($jobDescription->id, 3, '0', STR_PAD_LEFT);

            $jobDescription->update([
                'status' => JobDescription::STATUS_PUBLISHED,
                'published_by' => Auth::id(),
                'published_at' => now(),
                'published_link_token' => \Illuminate\Support\Str::uuid()->toString(),
            ]);

            JobDescriptionActivityLog::create([
                'job_description_id' => $jobDescription->id,
                'user_id' => Auth::id(),
                'event' => JobDescriptionActivityLog::EVENT_PUBLISHED,
                'meta' => null,
            ]);
        });

        return view('dashboard.job-descriptions.publish', compact('jobDescription'));
    }
    public function submitApproval(Request $request, $jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        $validated = $request->validate([
            'comment' => 'nullable|string|max:1000',
        ]);
 
        DB::transaction(function () use ($validated, $jd) {
            $jd->update([
                'status'      => JobDescription::STATUS_APPROVED,
                'approved_by' => Auth::id(),
                'approved_at' => now(),
            ]);
 
            if (!empty($validated['comment'])) {
                JobDescriptionComment::create([
                    'job_description_id' => $jd->id,
                    'user_id'            => Auth::id(),
                    'comment'            => $validated['comment'],
                ]);
            }
 
            JobDescriptionActivityLog::create([
                'job_description_id' => $jd->id,
                'user_id'            => Auth::id(),
                'event'              => JobDescriptionActivityLog::EVENT_APPROVED,
                'meta'               => null,
            ]);
        });
 
        $referer = request()->headers->get('referer', '');
        if (str_contains($referer, '/detail')) {
            return redirect()->route('dashboard.job-descriptions.detail', $jd->id)
                ->with('success', 'Job description approved successfully.');
        }
 
        return redirect()->route('dashboard.job-descriptions.review', $jd->id)
            ->with('success', 'Job description approved successfully.');
    }

    public function saveDraft(Request $request, $jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if ($jd->isPublished()) {
            return back()->withErrors(['error' => 'Published job descriptions cannot be reverted to draft.']);
        }
 
        DB::transaction(function () use ($jd) {
            $jd->update([
                'status'      => JobDescription::STATUS_DRAFT,
                'approved_by' => null,
                'approved_at' => null,
            ]);
 
            JobDescriptionActivityLog::create([
                'job_description_id' => $jd->id,
                'user_id'            => Auth::id(),
                'event'              => JobDescriptionActivityLog::EVENT_SAVED_AS_DRAFT,
                'meta'               => null,
            ]);
        });
 
        return redirect()->route('dashboard.job-descriptions.index')
            ->with('success', 'Draft saved!');
    }

    // Publish an approved job description
    public function doPublish($jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if (!$jd->isApproved()) {
            return back()->withErrors(['error' => 'Only approved job descriptions can be published.']);
        }
 
        DB::transaction(function () use ($jd) {
            $jd->update([
                'status'               => JobDescription::STATUS_PUBLISHED,
                'published_by'         => Auth::id(),
                'published_at'         => now(),
                'published_link_token' => \Illuminate\Support\Str::uuid()->toString(),
            ]);
 
            JobDescriptionActivityLog::create([
                'job_description_id' => $jd->id,
                'user_id'            => Auth::id(),
                'event'              => JobDescriptionActivityLog::EVENT_PUBLISHED,
                'meta'               => null,
            ]);
        });
 
        // Redirect back to whichever page triggered the publish action.
        // Uses the HTTP referer so it works from both detail and publish pages.
        $referer = request()->headers->get('referer', '');
        if (str_contains($referer, '/publish')) {
            return redirect()->route('dashboard.job-descriptions.publish', $jd->id)
                ->with('success', 'Job description published successfully.');
        }
 
        return redirect()->route('dashboard.job-descriptions.detail', $jd->id)
            ->with('success', 'Job description published successfully.');
    }

    // Unpublish a published job description (revert back to approved status)
    public function unpublish($jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if (!$jd->isPublished()) {
            return back()->withErrors(['error' => 'This job description is not published.']);
        }
 
        DB::transaction(function () use ($jd) {
            $jd->update([
                'status'               => JobDescription::STATUS_APPROVED,
                'published_by'         => null,
                'published_at'         => null,
                'published_link_token' => null,
            ]);
 
            JobDescriptionActivityLog::create([
                'job_description_id' => $jd->id,
                'user_id'            => Auth::id(),
                'event'              => JobDescriptionActivityLog::EVENT_SAVED_AS_DRAFT,
                'meta'               => ['action' => 'unpublished'],
            ]);
        });
 
        $referer = request()->headers->get('referer', '');
        if (str_contains($referer, '/publish')) {
            return redirect()->route('dashboard.job-descriptions.publish', $jd->id)
                ->with('success', 'Job description unpublished.');
        }
 
        return redirect()->route('dashboard.job-descriptions.detail', $jd->id)
            ->with('success', 'Job description unpublished.');
    }
 
    public function regenerateSection(Request $request, $jobDescription)
    {
        $jd = JobDescription::with('sections')->findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if ($jd->isPublished()) {
            return back()->withErrors(['error' => 'Published job descriptions cannot be edited.']);
        }
 
        $validated = $request->validate([
            'section_type' => ['required', Rule::in(JobDescriptionSection::SECTION_TYPES)],
            'instruction'  => 'nullable|string|max:500',
        ]);
 
        try {
            $newContent = $this->aiService->regenerateSection(
                $jd,
                $validated['section_type'],
                $validated['instruction'] ?? ''
            );
 
            DB::transaction(function () use ($newContent, $validated, $jd) {
                $section = JobDescriptionSection::firstOrNew([
                    'job_description_id' => $jd->id,
                    'section_type'       => $validated['section_type'],
                ]);
 
                $section->content    = $newContent;
                $section->source     = JobDescriptionSection::SOURCE_AI;
                $section->version    = $section->exists ? ($section->version + 1) : 1;
                $section->is_core    = true;
                $section->sort_order = array_search(
                    $validated['section_type'],
                    JobDescriptionSection::SECTION_TYPES
                );
                $section->save();
 
                JobDescriptionActivityLog::create([
                    'job_description_id' => $jd->id,
                    'user_id'            => Auth::id(),
                    'event'              => JobDescriptionActivityLog::EVENT_SECTION_REGENERATED,
                    'meta'               => ['section_type' => $validated['section_type']],
                ]);
            });
 
        } catch (\Throwable $e) {
            return back()->withErrors(['error' => 'Regeneration failed. Please try again.']);
        }
 
        return back()->with('success', 'Section regenerated!');
    }

    public function export($jobDescription, $format)
    {
        $jd = JobDescription::with([
            'sections'     => fn ($q) => $q->orderBy('sort_order'),
            'creator',
            'approver',
            'organization',
        ])->findOrFail($jobDescription);
 
        $this->authorizeAccess($jd);
 
        if (!$jd->isPublished()) {
            return back()->withErrors(['error' => 'A job description must be published before it can be exported.']);
        }
 
        $allowed = ['pdf', 'docx'];
        if (!in_array($format, $allowed)) {
            return back()->withErrors(['error' => 'Invalid export format.']);
        }
 
        $slug     = Str::slug($jd->job_title);
        $filename = "{$slug}-{$jd->id}.{$format}";
 
        try {
            if ($format === 'pdf') {
                return $this->streamPdf($jd, $filename);
            }
 
            return $this->streamDocx($jd, $filename);
 
        } catch (\Throwable $e) {
            Log::error('[JobDescriptionController] export failed', [
                'id'     => $jd->id,
                'format' => $format,
                'error'  => $e->getMessage(),
            ]);
 
            return back()->withErrors(['error' => 'Export failed. Please try again.']);
        }
    }

    // Add a custom section
    public function addCustomSection(Request $request, $jobDescription)
    {
        $jd = JobDescription::findOrFail($jobDescription);
        $this->authorizeAccess($jd);
 
        if ($jd->isPublished()) {
            return back()->withErrors(['error' => 'Published job descriptions cannot be edited.']);
        }
 
        $validated = $request->validate([
            'title'   => 'required|string|max:255',
            'content' => 'required|string|min:10',
        ]);
        $section = null;
        DB::transaction(function () use ($validated, $jd, &$section) {
            $maxOrder = $jd->sections()->max('sort_order') ?? 0;

            $section = JobDescriptionSection::create([
                'job_description_id' => $jd->id,
                'section_type'       => null,
                'title'              => $validated['title'],
                'content'            => $validated['content'],
                'source'             => JobDescriptionSection::SOURCE_USER,
                'version'            => 1,
                'is_core'            => false,
                'sort_order'         => $maxOrder + 1,
            ]);
 
            JobDescriptionActivityLog::create([
                'job_description_id' => $jd->id,
                'user_id'            => Auth::id(),
                'event'              => JobDescriptionActivityLog::EVENT_CUSTOM_SECTION_ADDED,
                'meta'               => ['title' => $validated['title']],
            ]);
        });

        // return back()->with('success', 'Custom section added.');
        return response()->json([
            'success' => true,
            'section' => $section,
            'message' => 'Custom section added successfully.',
        ]);
    }
 
    // Delete a custom section
    // public function deleteCustomSection($jobDescription, $section)
    // {
    //     $jd  = JobDescription::findOrFail($jobDescription);
    //     $sec = JobDescriptionSection::findOrFail($section);
    //     $this->authorizeAccess($jd);

    //     if ($jd->isPublished()) {
    //         return back()->withErrors(['error' => 'Published job descriptions cannot be edited.']);
    //     }

    //     if ($sec->isCore()) {
    //         return back()->withErrors(['error' => 'Core sections cannot be deleted.']);
    //     }

    //     $title = $sec->title;
    //     $sec->delete();

    //     JobDescriptionActivityLog::create([
    //         'job_description_id' => $jd->id,
    //         'user_id'            => Auth::id(),
    //         'event'              => JobDescriptionActivityLog::EVENT_CUSTOM_SECTION_DELETED,
    //         'meta'               => ['title' => $title],
    //     ]);

    //     return back()->with('success', 'Custom section deleted.');
    // }

    public function deleteCustomSection($jobDescription, $section)
    {
        // Load job description
        $jd = JobDescription::findOrFail($jobDescription);

        // Authorization FIRST
        $this->authorizeAccess($jd);

        // Block edits if published
        if ($jd->isPublished()) {
            return response()->json([
                'success' => false,
                'message' => 'Published job descriptions cannot be edited.'
            ], 403);
        }

        // IMPORTANT: scope section to job description (FIX)
        $sec = JobDescriptionSection::where('job_description_id', $jd->id)
            ->where('id', $section)
            ->first();

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

        // Prevent deleting core sections
        if ($sec->isCore()) {
            return response()->json([
                'success' => false,
                'message' => 'Core sections cannot be deleted.'
            ], 403);
        }

        // Store title for logging before delete
        $title = $sec->title;

        // Delete section
        $sec->delete();

        // Log activity
        JobDescriptionActivityLog::create([
            'job_description_id' => $jd->id,
            'user_id' => Auth::id(),
            'event' => JobDescriptionActivityLog::EVENT_CUSTOM_SECTION_DELETED,
            'meta' => [
                'title' => $title
            ],
        ]);
        return response()->json([
            'success' => true,
            'message' => 'Custom section deleted successfully.',
            'data' => [
                'section_id' => $section
            ]
        ]);
    }

    /**
     * Ensure the authenticated user is allowed to access this JD.
     * Org users can access any JD in their org.
     */
    private function authorizeAccess(JobDescription $jobDescription): void
    {
        $user = Auth::user();
 
        if (
            $user->organization_id
            && (int) $jobDescription->organization_id === (int) $user->organization_id
        ) {
            return;
        }
 
        if ((int) $jobDescription->created_by !== (int) $user->id) {
            abort(403, 'You do not have permission to access this job description.');
        }
    }

    /**
     * Render the JD as PDF via DomPDF and stream it as a download.
     */
    private function streamPdf(JobDescription $jd, string $filename): Response
    {
        $pdf = Pdf::loadView(
            'jd-wizard.export-pdf',
            ['jobDescription' => $jd]
        );
 
        $pdf->setPaper('A4', 'portrait');
 
        return $pdf->download($filename);
    }

    public function viewPdf($jd)
    {
        $jobDescription = JobDescription::with(['customSections'])->where('id', $jd)->first();
        return view(
            'jd-wizard.export-pdf',
            compact('jobDescription')
        );

    }
 
    /**
     * Build the JD as a DOCX via PhpWord and stream it as a download.
     */
    private function streamDocx(JobDescription $jd, string $filename): StreamedResponse
    {
        $phpWord = new PhpWord();
 
        // Document defaults
        $phpWord->setDefaultFontName('Arial');
        $phpWord->setDefaultFontSize(11);
 
        $phpWord->getDocInfo()
            ->setTitle($jd->job_title)
            ->setCreator($jd->creator?->name ?? 'System')
            ->setCompany($jd->organization?->name ?? '');
 
        // Style definitions
        $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,
        ]);
 
        // Title
        $section->addTitle($jd->job_title, 1);
 
        // Meta block
        foreach (array_filter([
            'Department'      => $jd->department,
            'Level'           => $jd->level,
            'Location'        => $jd->location,
            'Employment Type' => $jd->employment_type,
        ]) as $label => $value) {
            $para = $section->addTextRun(['spaceAfter' => 40]);
            $para->addText("{$label}: ", 'metaLabel');
            $para->addText($value, 'metaValue');
        }
 
        $section->addTextBreak(1);
 
        // Sections
        foreach ($jd->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;
                }
 
                // Strip leading bullet markers before writing
                $clean = preg_replace('/^[-•*]\s+/', '', $line);
 
                if (preg_match('/^[-•*]\s+/', $line)) {
                    $section->addListItem(
                        $clean, 0, 'bodyText',
                        ['listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_BULLET_FILLED]
                    );
                } else {
                    $section->addText($clean, 'bodyText', 'body');
                }
            }
 
            $section->addTextBreak(1);
        }
 
        // Footer
        $footer = $section->addFooter();
        $footer->addPreserveText(
            ($jd->organization?->name ?? '') . '  |  Page {PAGE} of {NUMPAGES}',
            ['size' => 9, 'color' => '999999'],
            ['alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER]
        );
 
        // Stream to browser
        $writer = IOFactory::createWriter($phpWord, 'Word2007');
 
        return response()->stream(function () use ($writer) {
            $writer->save('php://output');
        }, 200, [
            'Content-Type'        => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
            'Cache-Control'       => 'no-cache, no-store, must-revalidate',
            'Pragma'              => 'no-cache',
            'Expires'             => '0',
        ]);
    }