<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Concours;
use App\Models\FieldDefinition;
use App\Models\FieldValue;
use App\Models\FieldFile;
use App\Models\Candidate;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;    
use Illuminate\Database\QueryException; 
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Illuminate\Http\Response;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Str;
use Spatie\Browsershot\Browsershot;
use Illuminate\Support\Facades\Schema;


class ConcoursExtController extends Controller
{
	
	
public function index(Request $request)
{
    $q = trim($request->input('q', ''));

    $hasTitre    = Schema::hasColumn('concours', 'titre');
    $hasTitreAr  = Schema::hasColumn('concours', 'titre_ar');
    $hasTitreFr  = Schema::hasColumn('concours', 'titre_fr');
    $hasTitreEn  = Schema::hasColumn('concours', 'titre_en');

    if ($hasTitre) {
        $selectRaw = 'concours.id, concours.titre, concours.etat, concours.date_debut, concours.date_fin, concours.id_user';
    } elseif ($hasTitreAr || $hasTitreFr || $hasTitreEn) {
        $parts = [];
        if ($hasTitreAr) $parts[] = 'concours.titre_ar';
        if ($hasTitreFr) $parts[] = 'concours.titre_fr';
        if ($hasTitreEn) $parts[] = 'concours.titre_en';
        $selectRaw = 'concours.id, COALESCE(' . implode(', ', $parts) . ", '') as titre, concours.etat, concours.date_debut, concours.date_fin, concours.id_user";
    } else {
        $selectRaw = 'concours.*';
    }

    $query = Concours::active()
        ->selectRaw($selectRaw)
        ->withCount(['fields', 'candidates'])
        ->orderBy('date_debut', 'desc');

    $query->with('user:id,nom_prenom');

    if ($q !== '') {
        if ($hasTitre) {
            $query->where('titre', 'like', "%{$q}%");
        } else {
            $query->where(function ($qb) use ($q, $hasTitreAr, $hasTitreFr, $hasTitreEn) {
                $added = false;
                if ($hasTitreAr) { $qb->orWhere('titre_ar', 'like', "%{$q}%"); $added = true; }
                if ($hasTitreFr) { $qb->orWhere('titre_fr', 'like', "%{$q}%"); $added = true; }
                if ($hasTitreEn) { $qb->orWhere('titre_en', 'like', "%{$q}%"); $added = true; }
                if (! $added) {
                    $qb->where('etat', 'like', "%{$q}%");
                }
            });
        }
    }

    $concours = $query->paginate(12)->withQueryString();

    return view('concours_ext.index', compact('concours', 'q'));
}

public function downloadPdf(Request $request, Concours $concours)
{
    $cin = trim($request->query('cin', ''));

    if ($cin === '') {
        return redirect()->back()->withErrors(['cin' => 'CIN manquant.']);
    }

    $candidate = Candidate::with(['fieldValues', 'files'])
        ->where('cin', $cin)
        ->first();

    if (! $candidate) {
        return redirect()->back()->withErrors(['cin' => 'المترشح غير موجود أو رقم البطاقة غير صحيح.']);
    }

    $attached = DB::table('concours_candidate')
        ->where('concours_id', $concours->id)
        ->where('candidate_id', $candidate->id)
        ->exists();

    if (! $attached) {
        return redirect()->back()->withErrors(['cin' => 'هذا المترشح لا ينتمي إلى هذه المناظرة.']);
    }

    $defs = $concours->fields()->orderBy('sort_order')->get();
    $fvMap = $candidate->fieldValues->keyBy('field_definition_id');
    $allFields = [];
    $allFields[] = [
        'label' => __('site.table_name'),
        'value' => trim("{$candidate->nom} {$candidate->prenom}"),
    ];

    $allFields[] = [
        'label' => __('site.table_cin'),
        'value' => $candidate->cin ?? '—',
    ];

    $allFields[] = [
        'label' => __('site.table_email'),
        'value' => $candidate->email ?? '—',
    ];

    $allFields[] = [
        'label' => __('site.table_status'),
        'value' => $candidate->etat_label ?? $candidate->etat,
    ];

    $allFields[] = [
        'label' => __('site.table_created_at'),
        'value' => optional($candidate->created_at)->format('Y-m-d') ?? '—',
    ];
    $permanentKeys = [
        'nom','prenom','cin','email','date_naissance','tel','adresse','gouvernorat','code_postale','etat','created_at'
    ];

    foreach ($defs as $def) {
        $label = $def->label ?: $def->key ?: 'field_'.$def->id;
        $key = $def->key ?? null;

        if ($key && in_array($key, $permanentKeys)) {
            $val = $candidate->{$key} ?? null;
            if ($key === 'date_naissance' && $val) $val = optional($val)->format('Y-m-d');
            if ($val === null || $val === '') $val = '—';

            $existsLabel = collect($allFields)->first(function ($f) use ($label) {
                return isset($f['label']) && $f['label'] === $label;
            });
            if (! $existsLabel) {
                $allFields[] = ['label' => $label, 'value' => $val];
            }
            continue;
        }

        $fv = $fvMap->get($def->id);
        $value = '—';
        if ($fv) {
            $raw = $fv->value;
            if (is_string($raw) && (str_starts_with($raw, '[') || str_starts_with($raw, '{'))) {
                $try = json_decode($raw, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    if (is_array($try)) {
                        $value = implode(', ', array_map(function ($v) { return (string) $v; }, $try));
                    } else {
                        $value = (string) $try;
                    }
                } else {
                    $value = (string) $raw;
                }
            } else {
                $value = (string) $raw;
            }

            if ($value === '') $value = '—';
        } else {
            $value = '—';
        }

        $allFields[] = [
            'label' => $label,
            'value' => $value,
        ];
    }

    $extras = [
        'date_naissance' => optional($candidate->date_naissance)->format('Y-m-d') ?? null,
        'tel' => $candidate->tel ?? null,
        'adresse' => $candidate->adresse ?? null,
        'gouvernorat' => $candidate->gouvernorat ?? null,
        'code_postale' => $candidate->code_postale ?? null,
    ];
    foreach ($extras as $k => $v) {
        if (empty($v)) continue;
        $label = __('site.'.$k) ?? ucfirst(str_replace('_', ' ', $k));
        $existsLabel = collect($allFields)->first(function ($f) use ($label) {
            return isset($f['label']) && $f['label'] === $label;
        });
        if (! $existsLabel) {
            $allFields[] = ['label' => $label, 'value' => $v];
        }
    }

    $data = [
        'concours' => $concours,
        'candidate' => $candidate,
        'allFields' => $allFields,
        'locale' => app()->getLocale(),
    ];

    $filename = sprintf('%s_%s_%s.pdf',
        $concours->id,
        Str::slug($candidate->nom . '_' . $candidate->prenom),
        $candidate->cin ?? time()
    );

    $html = view('concours_ext.pdf', $data)->render();

    try {
        $mpdfTemp = storage_path('app/mpdf');
        if (! file_exists($mpdfTemp)) {
            @mkdir($mpdfTemp, 0755, true);
        }

        $locale = app()->getLocale();
        $rtlLanguages = ['ar','fa','he','ur'];
        $direction = in_array($locale, $rtlLanguages) ? 'rtl' : 'ltr';

        $mpdf = new \Mpdf\Mpdf([
            'mode' => 'utf-8',
            'format' => 'A4',
            'default_font' => 'dejavusans',
            'directionality' => $direction,
            'autoScriptToLang' => true,
            'autoLangToFont' => true,
            'tempDir' => $mpdfTemp,
        ]);

        $mpdf->SetFooter('{PAGENO} / {nbpg}');
        if (ob_get_length()) {
            @ob_end_clean();
        }
        $mpdf->WriteHTML($html);
        $force = $request->query('download') ? 'D' : 'I';
		return $mpdf->Output($filename, $force);


    } catch (\Throwable $e) {
        \Log::error('mPDF generation failed in downloadPdf(): ' . $e->getMessage(), [
            'concours_id' => $concours->id,
            'candidate_id' => $candidate->id ?? null,
            'exception' => (string) $e,
        ]);
        return response($html, 200, [
            'Content-Type' => 'application/octet-stream',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
            'X-PDF-Fallback' => 'true',
            'X-PDF-Fallback-Reason' => 'mPDF generation failed. The file contains HTML, not a proper PDF.',
        ]);
    }
}

public function show(Concours $concours)
{
    $fieldDefinitions = $concours->fields()
        ->where('field_definitions.is_visible', 1)
        ->orderBy('field_definitions.sort_order', 'asc')
        ->orderBy('field_definitions.id', 'asc')
        ->get();

    $gouvernorats = DB::table('tp_gouvernorat')
        ->select('id_gouver', 'nom_ar')
        ->orderBy('nom_ar')
        ->get();

    return view('concours_ext.create', compact('concours', 'fieldDefinitions', 'gouvernorats'));
}


public function store(Request $request, Concours $concours)
{
    if (! empty($concours->date_fin) && \Carbon\Carbon::parse($concours->date_fin)->endOfDay()->lt(\Carbon\Carbon::now())) {
        return back()
            ->withInput()
            ->withErrors(['general' => __('site.registration_closed')]);
    }

    $defs = $concours->fields()->where('field_definitions.is_visible', 1)->get()->keyBy('id');

    $rules = [
        'nom' => ['required','string','max:100'],
        'prenom' => ['required','string','max:100'],
        'cin' => ['nullable','regex:/^\d{8}$/'],
        'date_naissance' => ['nullable','date'],
        'tel' => ['nullable','regex:/^\d{8}$/'],
        'email' => ['nullable','email','max:255'],
        'adresse' => ['nullable','string','max:255'],
        'gouvernorat_id' => ['nullable','integer','exists:tp_gouvernorat,id_gouver'],
        'code_postale' => ['nullable','regex:/^\d{4}$/'],
    ];

    foreach ($defs as $def) {
        $fieldKey = 'field_'.$def->id;
        $r = [];
        $r[] = $def->is_required ? 'required' : 'nullable';

        switch ($def->input_type) {
            case 'date': $r[] = 'date'; break;
            case 'number': $r[] = 'numeric'; break;
            case 'file': $r[] = 'file'; break;
            case 'email': $r[] = 'email'; break;
            default:
                if (in_array($def->input_type, ['checkbox','multiselect'])) {
                    if ($def->is_required) {
                        $r[] = 'array';
                        $r[] = 'min:1';
                    } else {
                        $r[] = 'array';
                    }
                } else {
                    $r[] = 'string';
                    $r[] = 'max:2000';
                }
        }

        $rules[$fieldKey] = $r;
    }

    $messages = [
        'cin.regex' => 'يجب أن يتكون رقم بطاقة التعريف من 8 أرقام.',
        'tel.regex' => 'يجب أن يتكون رقم الهاتف من 8 أرقام.',
        'code_postale.regex' => 'يجب أن يتكون الرمز البريدي من 4 أرقام.',
    ];

    $validator = Validator::make($request->all(), $rules, $messages);

    $validator->after(function ($v) use ($request, $concours) {
        $cin = $request->input('cin');
        if ($cin) {
            $exists = DB::table('concours_candidate')
                ->join('candidates', 'concours_candidate.candidate_id', '=', 'candidates.id')
                ->where('concours_candidate.concours_id', $concours->id)
                ->where('candidates.cin', $cin)
                ->exists();

            if ($exists) {
                $v->errors()->add('cin', 'رقم بطاقة التعريف مستخدم مسبقاً لهذا المناظرة.');
            }
        }
    });

    if ($validator->fails()) {
        return back()->withErrors($validator)->withInput();
    }

    $validated = $validator->validated();

    $gouvName = null;
    if (!empty($validated['gouvernorat_id'])) {
        $gouvName = DB::table('tp_gouvernorat')
            ->where('id_gouver', $validated['gouvernorat_id'])
            ->value('nom_ar');
    }

    DB::beginTransaction();

    try {
        $candidate = null;
        if (!empty($validated['cin'])) {
            $candidate = Candidate::where('cin', $validated['cin'])->first();
        }

        if ($candidate) {
            $candidate->update([
                'nom' => $validated['nom'],
                'prenom' => $validated['prenom'],
                'date_naissance' => $validated['date_naissance'] ?? $candidate->date_naissance,
                'tel' => $validated['tel'] ?? $candidate->tel,
                'email' => $validated['email'] ?? $candidate->email,
                'adresse' => $validated['adresse'] ?? $candidate->adresse,
                'gouvernorat' => $gouvName ?? $candidate->gouvernorat,
                'code_postale' => $validated['code_postale'] ?? $candidate->code_postale,
            ]);
        } else {
            $candidate = Candidate::create([
                'nom' => $validated['nom'],
                'prenom' => $validated['prenom'],
                'cin' => $validated['cin'] ?? null,
                'date_naissance' => $validated['date_naissance'] ?? null,
                'tel' => $validated['tel'] ?? null,
                'email' => $validated['email'] ?? null,
                'adresse' => $validated['adresse'] ?? null,
                'gouvernorat' => $gouvName ?? null,
                'code_postale' => $validated['code_postale'] ?? null,
                'etat' => 'en_cours',
            ]);
        }

        if (method_exists($concours, 'candidates')) {
            if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
                $concours->candidates()->attach($candidate->id);
            }
        }

        foreach ($defs as $def) {
            $fieldKey = 'field_'.$def->id;

            try {
                if ($def->input_type === 'file') {
                    if ($request->hasFile($fieldKey)) {
                        $file = $request->file($fieldKey);
                        if ($file && $file->isValid()) {
                            $stream = fopen($file->getRealPath(), 'rb');
                            $content = stream_get_contents($stream);
                            fclose($stream);

                            FieldFile::create([
                                'candidate_id'         => $candidate->id,
                                'field_definition_id'  => $def->id,
                                'file_path'            => '',
                                'original_name'        => $file->getClientOriginalName(),
                                'mime_type'            => $file->getClientMimeType(),
                                'size'                 => $file->getSize(),
                                'content'              => $content,
                            ]);
                        }
                    }
                } else {
                    if ($request->has($fieldKey)) {
                        $val = $request->input($fieldKey);
                        if (is_array($val)) {
                            $stored = json_encode(array_values($val), JSON_UNESCAPED_UNICODE);
                            FieldValue::create([
                                'candidate_id' => $candidate->id,
                                'field_definition_id' => $def->id,
                                'value' => $stored,
                            ]);
                        } else {
                            if ($request->filled($fieldKey) || $def->is_required) {
                                FieldValue::create([
                                    'candidate_id' => $candidate->id,
                                    'field_definition_id' => $def->id,
                                    'value' => $val,
                                ]);
                            }
                        }
                    }
                }
            } catch (\Throwable $innerEx) {
                \Log::warning("Field save failed for candidate_id={$candidate->id} field={$def->id}: " . $innerEx->getMessage(), [
                    'exception' => $innerEx,
                    'field' => $def->id,
                    'candidate_id' => $candidate->id,
                ]);
            }
        }

        DB::commit();
        return redirect()
            ->route('concours.show', $concours)
            ->with('success', 'تم إرسال مطلب الترشّح بنجاح. شكرًا لك.')
            ->with('download_cin', $candidate->cin ?? '');

    } catch (QueryException $qe) {
        DB::rollBack();

        $errorId = strtoupper(substr(sha1(now()->timestamp . Str::random(8)), 0, 10));
        \Log::error("Candidate store QueryException [{$errorId}]: " . $qe->getMessage(), [
            'exception' => $qe,
            'concours_id' => $concours->id,
            'input' => $request->except(['_token']),
        ]);

        $msg = $qe->getMessage();
        if (str_contains(strtolower($msg), 'duplicate')) {
            return back()->withInput()->withErrors(['cin' => 'رقم بطاقة التعريف مستخدم بالفعل.']);
        }

        return back()->withInput()->withErrors(['general' => "حدث خطأ أثناء حفظ المطلب. رمز الخطأ: {$errorId}"]);
    } catch (\Throwable $e) {
        DB::rollBack();

        $errorId = strtoupper(substr(sha1(now()->timestamp . Str::random(8)), 0, 10));
        \Log::error("Candidate store Exception [{$errorId}]: " . $e->getMessage(), [
            'exception' => $e,
            'concours_id' => $concours->id,
            'input' => $request->except(['_token']),
        ]);

        return back()->withInput()->withErrors(['general' => "حدث خطأ أثناء حفظ المطلب. رمز الخطأ: {$errorId}"]);
    }
}


public function results(Request $request, Concours $concours)
{
    $q = (string) $request->input('q', '');
    $cin = preg_replace('/\D+/', '', $q);

    if ($cin === '') {
        $empty = new Collection([]);
        $candidates = new LengthAwarePaginator($empty, 0, 12, 1, [
            'path' => $request->url(),
            'query' => $request->query(),
        ]);

        return view('concours_ext.results', compact('concours', 'candidates', 'q'))
            ->with('searchRan', false);
    }

    $candidateQuery = $concours->candidates()
        ->with(['fieldValues', 'files'])
        ->where('cin', $cin)
        ->orderBy('created_at', 'desc');

    $items = $candidateQuery->get();
    $total = $items->count();

    $page = LengthAwarePaginator::resolveCurrentPage();
    $perPage = 12;
    $currentItems = $items->slice(($page - 1) * $perPage, $perPage)->values();

    $candidates = new LengthAwarePaginator(
        $currentItems,
        $total,
        $perPage,
        $page,
        [
            'path' => $request->url(),
            'query' => $request->query(),
        ]
    );

    return view('concours_ext.results', compact('concours', 'candidates', 'q'))
        ->with('searchRan', true);
}


  public function editByCin(Request $request, Concours $concours)
    {
        $cin = $request->query('cin');

        if (empty($cin) || !preg_match('/^\d{8}$/', $cin)) {
            return redirect()->route('concours.show', $concours)
                ->withErrors(['cin' => 'يرجى تزويد رقم بطاقة تعريف صالح (8 أرقام) للتعديل.']);
        }

        $candidate = Candidate::where('cin', $cin)
            ->whereHas('concours', function ($q) use ($concours) {
                $q->where('concours.id', $concours->id);
            })->first();

        if (! $candidate) {
            return redirect()->route('concours.show', $concours)
                ->withErrors(['cin' => 'لم يتم العثور على مطلب مطابق لهذا المناظرة ورقم بطاقة التعريف.']);
        }

        $fieldDefinitions = $concours->fields()
            ->where('field_definitions.is_visible', 1)
            ->orderBy('field_definitions.sort_order', 'asc')
            ->orderBy('field_definitions.id', 'asc')
            ->get();

        $gouvernorats = DB::table('tp_gouvernorat')
            ->select('id_gouver', 'nom_ar')
            ->orderBy('nom_ar')
            ->get();
        $values = $candidate->fieldValues()->get()->keyBy('field_definition_id');
        $files  = $candidate->files()->get()->groupBy('field_definition_id');

        return view('concours_ext.edit', compact(
            'concours','fieldDefinitions','gouvernorats','candidate','values','files'
        ));
    }


    public function updateByCin(Request $request, Concours $concours)
    {
    
        $candidateId = $request->input('candidate_id');
        $candidate = null;
        if ($candidateId) {
            $candidate = Candidate::find($candidateId);
        } elseif ($request->filled('cin')) {
            $candidate = Candidate::where('cin', $request->input('cin'))->first();
        }

        if (! $candidate) {
            return back()->withInput()->withErrors(['general' => 'لم نتمكن من تحديد المترشّح للتعديل.']);
        }

        $defs = $concours->fields()->where('field_definitions.is_visible', 1)->get()->keyBy('id');

        $rules = [
            'nom' => ['required','string','max:100'],
            'prenom' => ['required','string','max:100'],
            'cin' => ['nullable','regex:/^\d{8}$/'],
            'date_naissance' => ['nullable','date'],
            'tel' => ['nullable','regex:/^\d{8}$/'],
            'email' => ['nullable','email','max:255'],
            'adresse' => ['nullable','string','max:255'],
            'gouvernorat_id' => ['nullable','integer','exists:tp_gouvernorat,id_gouver'],
            'code_postale' => ['nullable','regex:/^\d{4}$/'],
        ];

        foreach ($defs as $def) {
            $fieldKey = 'field_'.$def->id;
            $r = [];
            $r[] = $def->is_required ? 'required' : 'nullable';

            switch ($def->input_type) {
                case 'date': $r[] = 'date'; break;
                case 'number': $r[] = 'numeric'; break;
                case 'file': $r[] = 'file'; break;
                case 'email': $r[] = 'email'; break;
                default:
                    if (in_array($def->input_type, ['checkbox','multiselect'])) {
                        if ($def->is_required) {
                            $r[] = 'array';
                            $r[] = 'min:1';
                        } else {
                            $r[] = 'array';
                        }
                    } else {
                        $r[] = 'string';
                        $r[] = 'max:2000';
                    }
            }
            $rules[$fieldKey] = $r;
        }

        $messages = [
            'cin.regex' => 'يجب أن يتكون رقم بطاقة التعريف من 8 أرقام.',
            'tel.regex' => 'يجب أن يتكون رقم الهاتف من 8 أرقام.',
            'code_postale.regex' => 'يجب أن يتكون الرمز البريدي من 4 أرقام.',
        ];

        $validator = Validator::make($request->all(), $rules, $messages);
        $validator->after(function ($v) use ($request, $concours, $candidate) {
            $cin = $request->input('cin');
            if ($cin) {
                $exists = DB::table('concours_candidate')
                    ->join('candidates', 'concours_candidate.candidate_id', '=', 'candidates.id')
                    ->where('concours_candidate.concours_id', $concours->id)
                    ->where('candidates.cin', $cin)
                    ->where('candidates.id', '!=', $candidate->id)
                    ->exists();

                if ($exists) {
                    $v->errors()->add('cin', 'رقم بطاقة التعريف مستخدم مسبقاً لهذا المناظرة.');
                }
            }
        });

        if ($validator->fails()) {
            return back()->withErrors($validator)->withInput();
        }

        $validated = $validator->validated();

        $gouvName = null;
        if (!empty($validated['gouvernorat_id'])) {
            $gouvName = DB::table('tp_gouvernorat')
                ->where('id_gouver', $validated['gouvernorat_id'])
                ->value('nom_ar');
        }

        DB::beginTransaction();
        try {
            $candidate->update([
                'nom' => $validated['nom'],
                'prenom' => $validated['prenom'],
                'cin' => $validated['cin'] ?? $candidate->cin,
                'date_naissance' => $validated['date_naissance'] ?? $candidate->date_naissance,
                'tel' => $validated['tel'] ?? $candidate->tel,
                'email' => $validated['email'] ?? $candidate->email,
                'adresse' => $validated['adresse'] ?? $candidate->adresse,
                'gouvernorat' => $gouvName ?? $candidate->gouvernorat,
                'code_postale' => $validated['code_postale'] ?? $candidate->code_postale,
            ]);

            foreach ($defs as $def) {
                $fieldKey = 'field_'.$def->id;

                if ($def->input_type === 'file') {
                    if ($request->hasFile($fieldKey)) {
                        $file = $request->file($fieldKey);
                        if ($file && $file->isValid()) {
                            $stream = fopen($file->getRealPath(), 'rb');
                            $content = stream_get_contents($stream);
                            fclose($stream);

                            FieldFile::where('candidate_id', $candidate->id)
                                ->where('field_definition_id', $def->id)
                                ->delete();

                            FieldFile::create([
                                'candidate_id'         => $candidate->id,
                                'field_definition_id'  => $def->id,
                                'file_path'            => '',
                                'original_name'        => $file->getClientOriginalName(),
                                'mime_type'            => $file->getClientMimeType(),
                                'size'                 => $file->getSize(),
                                'content'              => $content,
                            ]);
                        }
                    }
                } else {
					
                    if ($request->has($fieldKey)) {
                        $val = $request->input($fieldKey);
                        if (is_array($val)) {
                            $stored = json_encode(array_values($val), JSON_UNESCAPED_UNICODE);

                            FieldValue::updateOrCreate(
                                ['candidate_id' => $candidate->id, 'field_definition_id' => $def->id],
                                ['value' => $stored]
                            );
                        } else {
                            if ($request->filled($fieldKey) || $def->is_required) {
                                FieldValue::updateOrCreate(
                                    ['candidate_id' => $candidate->id, 'field_definition_id' => $def->id],
                                    ['value' => $val]
                                );
                            } else {
                                FieldValue::where('candidate_id', $candidate->id)
                                    ->where('field_definition_id', $def->id)
                                    ->delete();
                            }
                        }
                    } else {
						
                        if (! $def->is_required) {
                            FieldValue::where('candidate_id', $candidate->id)
                                ->where('field_definition_id', $def->id)
                                ->delete();
                        }
                    }
                }
            }

            DB::commit();

        return redirect()->route('concours.find', [
    'concours' => $concours->id,
    'cin' => $candidate->cin
])
->with('success', 'تم تحديث مطلب الترشّح بنجاح.');

        } catch (QueryException $qe) {
            DB::rollBack();
            $errorId = strtoupper(substr(sha1(now()->timestamp . Str::random(8)), 0, 10));
            \Log::error("Candidate update QueryException [{$errorId}] " . $qe->getMessage(), [
                'exception' => $qe, 'concours_id' => $concours->id, 'candidate_id' => $candidate->id
            ]);
            return back()->withInput()->withErrors(['general' => "حدث خطأ أثناء تحديث المطلب. رمز الخطأ: {$errorId}"]);
        } catch (\Throwable $e) {
            DB::rollBack();
            $errorId = strtoupper(substr(sha1(now()->timestamp . Str::random(8)), 0, 10));
            \Log::error("Candidate update Exception [{$errorId}] " . $e->getMessage(), [
                'exception' => $e, 'concours_id' => $concours->id, 'candidate_id' => $candidate->id
            ]);
            return back()->withInput()->withErrors(['general' => "حدث خطأ أثناء تحديث المطلب. رمز الخطأ: {$errorId}"]);
        }
    }
	
	public function findApplication(Request $request, Concours $concours)
{
    $cin = $request->query('cin');

    $fieldDefinitions = $concours->fields()
        ->where('field_definitions.is_visible', 1)
        ->orderBy('field_definitions.sort_order', 'asc')
        ->orderBy('field_definitions.id', 'asc')
        ->get();

    $gouvernorats = DB::table('tp_gouvernorat')
        ->select('id_gouver','nom_ar')
        ->orderBy('nom_ar')
        ->get();

    $candidate = null;
    $error = null;

    if ($cin !== null) {
        $cin = trim($cin);
        if (! preg_match('/^\d{8}$/', $cin)) {
            $error = 'يرجى إدخال رقم بطاقة تعريف صالح (8 أرقام).';
        } else {
            $candidate = Candidate::where('cin', $cin)
                ->whereHas('concours', function ($q) use ($concours) {
                    $q->where('concours.id', $concours->id);
                })
                ->first();

            if (! $candidate) {
                $error = 'لم يتم العثور على مطلب مطابق لهذا المناظرة ورقم بطاقة التعريف.';
            }
        }
    }

    $values = $candidate ? $candidate->fieldValues()->get()->keyBy('field_definition_id') : collect();
    $files  = $candidate ? $candidate->files()->get()->groupBy('field_definition_id') : collect();

    return view('concours_ext.find', compact(
        'concours','fieldDefinitions','gouvernorats','cin','candidate','error','values','files'
    ));
}


public function downloadApplication(Request $request, Concours $concours)
{
    $cin = trim($request->query('cin', ''));

    if ($cin === '') {
        return redirect()->back()->withErrors(['cin' => 'CIN manquant.']);
    }

    $candidate = Candidate::with(['fieldValues', 'files'])
        ->where('cin', $cin)
        ->first();

    if (! $candidate) {
        return redirect()->back()->withErrors(['cin' => 'المترشح غير موجود أو رقم البطاقة غير صحيح.']);
    }

    $attached = DB::table('concours_candidate')
        ->where('concours_id', $concours->id)
        ->where('candidate_id', $candidate->id)
        ->exists();

    if (! $attached) {
        return redirect()->back()->withErrors(['cin' => 'هذا المترشح لا ينتمي إلى هذه المناظرة.']);
    }

    $defs = $concours->fields()->orderBy('sort_order')->get();
    $fvMap = $candidate->fieldValues->keyBy('field_definition_id');
    $allFields = [];
    $allFields[] = [
        'label' => __('site.table_name'),
        'value' => trim("{$candidate->nom} {$candidate->prenom}"),
    ];

    $allFields[] = [
        'label' => __('site.table_cin'),
        'value' => $candidate->cin ?? '—',
    ];

    $allFields[] = [
        'label' => __('site.table_email'),
        'value' => $candidate->email ?? '—',
    ];

    $allFields[] = [
        'label' => __('site.table_status'),
        'value' => $candidate->etat_label ?? $candidate->etat,
    ];

    $allFields[] = [
        'label' => __('site.table_created_at'),
        'value' => optional($candidate->created_at)->format('Y-m-d') ?? '—',
    ];
    $permanentKeys = [
        'nom','prenom','cin','email','date_naissance','tel','adresse','gouvernorat','code_postale','etat','created_at'
    ];

    foreach ($defs as $def) {
        $label = $def->label ?: $def->key ?: 'field_'.$def->id;
        $key = $def->key ?? null;

        if ($key && in_array($key, $permanentKeys)) {
            $val = $candidate->{$key} ?? null;
            if ($key === 'date_naissance' && $val) $val = optional($val)->format('Y-m-d');
            if ($val === null || $val === '') $val = '—';

            $existsLabel = collect($allFields)->first(function ($f) use ($label) {
                return isset($f['label']) && $f['label'] === $label;
            });
            if (! $existsLabel) {
                $allFields[] = ['label' => $label, 'value' => $val];
            }
            continue;
        }

        $fv = $fvMap->get($def->id);
        $value = '—';
        if ($fv) {
            $raw = $fv->value;
            if (is_string($raw) && (str_starts_with($raw, '[') || str_starts_with($raw, '{'))) {
                $try = json_decode($raw, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    if (is_array($try)) {
                        $value = implode(', ', array_map(function ($v) { return (string) $v; }, $try));
                    } else {
                        $value = (string) $try;
                    }
                } else {
                    $value = (string) $raw;
                }
            } else {
                $value = (string) $raw;
            }

            if ($value === '') $value = '—';
        } else {
            $value = '—';
        }

        $allFields[] = [
            'label' => $label,
            'value' => $value,
        ];
    }

    $extras = [
        'date_naissance' => optional($candidate->date_naissance)->format('Y-m-d') ?? null,
        'tel' => $candidate->tel ?? null,
        'adresse' => $candidate->adresse ?? null,
        'gouvernorat' => $candidate->gouvernorat ?? null,
        'code_postale' => $candidate->code_postale ?? null,
    ];
    foreach ($extras as $k => $v) {
        if (empty($v)) continue;
        $label = __('site.'.$k) ?? ucfirst(str_replace('_', ' ', $k));
        $existsLabel = collect($allFields)->first(function ($f) use ($label) {
            return isset($f['label']) && $f['label'] === $label;
        });
        if (! $existsLabel) {
            $allFields[] = ['label' => $label, 'value' => $v];
        }
    }

    $data = [
        'concours' => $concours,
        'candidate' => $candidate,
        'allFields' => $allFields,
        'locale' => app()->getLocale(),
    ];

    $filename = sprintf('%s_%s_%s.pdf',
        $concours->id,
        Str::slug($candidate->nom . '_' . $candidate->prenom),
        $candidate->cin ?? time()
    );

    $html = view('concours_ext.download_candidature', $data)->render();

    try {
        $mpdfTemp = storage_path('app/mpdf');
        if (! file_exists($mpdfTemp)) {
            @mkdir($mpdfTemp, 0755, true);
        }

        $locale = app()->getLocale();
        $rtlLanguages = ['ar','fa','he','ur'];
        $direction = in_array($locale, $rtlLanguages) ? 'rtl' : 'ltr';

        $mpdf = new \Mpdf\Mpdf([
            'mode' => 'utf-8',
            'format' => 'A4',
            'default_font' => 'dejavusans',
            'directionality' => $direction,
            'autoScriptToLang' => true,
            'autoLangToFont' => true,
            'tempDir' => $mpdfTemp,
        ]);

        $mpdf->SetFooter('{PAGENO} / {nbpg}');
        if (ob_get_length()) {
            @ob_end_clean();
        }
        $mpdf->WriteHTML($html);
        $force = $request->query('download') ? 'D' : 'I';
		return $mpdf->Output($filename, $force);


    } catch (\Throwable $e) {
        \Log::error('mPDF generation failed in downloadPdf(): ' . $e->getMessage(), [
            'concours_id' => $concours->id,
            'candidate_id' => $candidate->id ?? null,
            'exception' => (string) $e,
        ]);
        return response($html, 200, [
            'Content-Type' => 'application/octet-stream',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
            'X-PDF-Fallback' => 'true',
            'X-PDF-Fallback-Reason' => 'mPDF generation failed. The file contains HTML, not a proper PDF.',
        ]);
    }
}

}
