<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Models\Concours;
use App\Models\Candidate;
use App\Models\FieldDefinition;
use App\Models\FieldValue;
use App\Models\FieldFile;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Illuminate\Support\Str;

class ConcoursExtController extends Controller
{
    /**
     * List candidates for a concours (admin view).
     */
// in App\Http\Controllers\ConcoursExtController.php

/*public function index(Request $request, Concours $concours)
{
    $q = (string)$request->input('q', '');

    // status label + badge class mappings (centralized)
    $statusOptions = [
        'en_cours' => 'قيد الدراسة',
        'refuse' => 'مرفوض',
        'acceptation_primaire' => 'قبول أولي',
        'acceptation_definitif' => 'قبول نهائي',
    ];

    $badgeClass = [
        'en_cours' => 'badge bg-info text-white',
        'refuse' => 'badge bg-danger',
        'acceptation_primaire' => 'badge bg-warning text-dark',
        'acceptation_definitif' => 'badge bg-success',
    ];

    $query = $concours->candidates()->orderBy('created_at', 'desc');

    if ($q !== '') {
        $query->where(function ($qb) use ($q) {
            $qb->where('nom', 'like', "%{$q}%")
               ->orWhere('prenom', 'like', "%{$q}%")
               ->orWhere('cin', 'like', "%{$q}%")
               ->orWhere('email', 'like', "%{$q}%");
        });
    }

    $candidates = $query->paginate(15)->withQueryString();

    // pass the mappings to the view so the labels and badges render correctly
    return view('backoffice.concours_ext.index', compact('concours', 'candidates', 'q', 'statusOptions', 'badgeClass'));
}*/


public function index(Request $request, Concours $concours)
{
    // Récupération de la recherche et nettoyage
    $q = trim((string) $request->input('q', ''));

    // Bloquer les recherches vides ou contenant uniquement des %
    if ($q === '' || preg_match('/^%+$/', $q)) {
        $q = null;
    }

    // Status label + badge class mappings
    $statusOptions = [
        'en_cours' => 'قيد الدراسة',
        'refuse' => 'مرفوض',
        'acceptation_primaire' => 'قبول أولي',
        'acceptation_definitif' => 'قبول نهائي',
    ];

    $badgeClass = [
        'en_cours' => 'badge bg-info text-white',
        'refuse' => 'badge bg-danger',
        'acceptation_primaire' => 'badge bg-warning text-dark',
        'acceptation_definitif' => 'badge bg-success',
    ];

    // Construction de la requête
    $query = $concours->candidates()->orderBy('created_at', 'desc');

    if ($q) {
        // Découper la recherche par espaces pour chercher chaque mot
        $words = preg_split('/\s+/', $q);

        $query->where(function ($qb) use ($words) {
            foreach ($words as $word) {
                // Échapper les caractères spéciaux pour LIKE
                $word = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $word);

                $qb->where(function ($q) use ($word) {
                    $q->where('nom', 'like', "%{$word}%")
                      ->orWhere('prenom', 'like', "%{$word}%")
                      ->orWhere('cin', 'like', "%{$word}%")
                      ->orWhere('email', 'like', "%{$word}%");
                });
            }
        });
    }

    // Pagination avec conservation des paramètres de recherche
    $candidates = $query->paginate(15)->withQueryString();

    // Retour à la vue avec les données
    return view('backoffice.concours_ext.index', compact(
        'concours', 'candidates', 'q', 'statusOptions', 'badgeClass'
    ));
}


public function file(Request $request, Concours $concours, Candidate $candidate, FieldFile $file)
{
    // ensure candidate belongs to this concours
    if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
        abort(404);
    }

    // ensure the file belongs to this candidate
    if ((int)$file->candidate_id !== (int)$candidate->id) {
        abort(404);
    }

    // decide disposition
    $forceDownload = (bool)$request->query('download');
    $disposition = $forceDownload ? 'attachment' : 'inline';

    $mime = $file->mime_type ?: 'application/octet-stream';
    $filename = $file->original_name ?: ('file_'.$file->id);

    // headers (include filename* for utf8 safety)
    $headers = [
        'Content-Type' => $mime,
        'Cache-Control' => 'private, max-age=86400',
        'Content-Disposition' => $disposition . '; filename="'. addslashes($filename) .'"',
        // add filename* for unicode names (percent-encoded UTF-8)
        'Content-Disposition-Alternate' => "filename*=UTF-8''" . rawurlencode($filename),
    ];

    // If file content column is stored as binary string, stream it:
    // Use StreamedResponse to avoid memory pressure with very large blobs.
    return response()->stream(function () use ($file) {
        // if content is a resource already, echo directly
        if (is_resource($file->content)) {
            // rewind then stream
            try {
                rewind($file->content);
            } catch (\Throwable $e) { /* ignore */ }
            while (! feof($file->content)) {
                echo fread($file->content, 8192);
                flush();
            }
            return;
        }

        // otherwise it's a string (binary)
        echo $file->content;
    }, 200, $headers);
}
    /**
     * Show the public form to apply for a concours.
     */
    public function create(Concours $concours)
    {
        // only show fields attached to this concours and visible
        $fieldDefinitions = $concours->fields()->where('is_visible', 1)->orderBy('sort_order')->get();
        return view('concours_ext.create', compact('concours', 'fieldDefinitions'));
    }

    /**
     * Store a candidate submission for the given concours.
     */
    public function store(Request $request, Concours $concours)
    {
        // load definitions used by this concours
        $defs = $concours->fields()->where('is_visible', 1)->get()->keyBy('id');

        // base rules for fixed fields
        $rules = [
            'nom' => ['required','string','max:150'],
            'prenom' => ['required','string','max:150'],
            'cin' => ['nullable','string','max:30', Rule::unique('candidates','cin')],
            'date_naissance' => ['nullable','date'],
            'tel' => ['nullable','string','max:50'],
            'email' => ['nullable','email','max:255'],
            'adresse' => ['nullable','string'],
            'gouvernorat' => ['nullable','string','max:150'],
            'code_postale' => ['nullable','string','max:20'],
        ];

        // dynamic fields validation
        foreach ($defs as $def) {
            $key = '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[$key] = $r;
        }

        $validated = $request->validate($rules);

        DB::beginTransaction();
        try {
            $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' => $validated['gouvernorat'] ?? null,
                'code_postale' => $validated['code_postale'] ?? null,
                'etat' => 'en_cours',
            ]);

            // attach pivot
            $concours->candidates()->attach($candidate->id);

            // save dynamic values & files
            foreach ($defs as $def) {
                $fieldKey = 'field_'.$def->id;

                if ($def->input_type === 'file') {
                    if ($request->hasFile($fieldKey)) {
                        $file = $request->file($fieldKey);
                        $path = $file->store('candidate_files');
                        FieldFile::create([
                            'candidate_id' => $candidate->id,
                            'field_definition_id' => $def->id,
                            'file_path' => $path,
                            'original_name' => $file->getClientOriginalName(),
                            'mime_type' => $file->getClientMimeType(),
                            'size' => $file->getSize(),
                        ]);
                    }
                } 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,
                                ]);
                            }
                        }
                    }
                }
            }

            DB::commit();
            return redirect()->route('concours.show', $concours)->with('success', 'تم التسجيل بنجاح.');
        } catch (\Throwable $e) {
            DB::rollBack();
            \Log::error('Candidate store error: '.$e->getMessage());
            return back()->withInput()->withErrors(['general' => 'حدث خطأ أثناء حفظ المطلب.']);
        }
    }

    /**
     * Show a candidate (submission) for a concours (admin).
     */
    public function show(Concours $concours, Candidate $candidate)
    {
        // ensure candidate belongs to this concours
        if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            abort(404);
        }

        $concours_ext = $candidate;
        $concours_ext->load(['fieldValues.definition', 'files.definition']);

        $dynamic = [];
        foreach ($concours_ext->fieldValues as $fv) {
            $label = $fv->definition ? ($fv->definition->label ?: $fv->definition->key) : 'Field #'.$fv->field_definition_id;
            $dynamic[] = ['label' => $label, 'value' => $fv->value];
        }

      $files = [];
foreach ($concours_ext->files as $f) {
    $files[] = [
        'definition_label' => $f->definition ? ($f->definition->label ?: $f->definition->key) : null,
        'original_name'    => $f->original_name,
        'path'             => $f->file_path, // optional, kept for reference
        // route to open inline
        'url'              => route('concours_ext.file', [
                                'concours' => $concours->id,
                                'candidate' => $concours_ext->id,
                                'file' => $f->id
                              ]),
        // optional download link (forces attachment)
        'download_url'     => route('concours_ext.file', [
                                'concours' => $concours->id,
                                'candidate' => $concours_ext->id,
                                'file' => $f->id
                              ]) . '?download=1',
        'mime'             => $f->mime_type,
        'size'             => $f->size,
    ];
}


        return view('backoffice.concours_ext.show', compact('concours_ext', 'dynamic', 'files', 'concours'));
    }

    public function edit(Concours $concours, Candidate $candidate)
    {
        if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            abort(404);
        }

        $concours_ext = $candidate;
        $fieldDefinitions = FieldDefinition::orderBy('sort_order')->get();
        $valuesByDef = $concours_ext->fieldValues()->get()->keyBy('field_definition_id');

        $prefill = [];
        foreach ($fieldDefinitions as $def) {
            $fv = $valuesByDef->get($def->id);
            $prefill['field_'.$def->id] = $fv ? $fv->value : null;
        }

        return view('backoffice.concours_ext.edit', compact('concours_ext', 'fieldDefinitions', 'prefill', 'concours'));
    }

    public function update(Request $request, Concours $concours, Candidate $candidate)
    {
        if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            abort(404);
        }

        $defs = FieldDefinition::orderBy('sort_order')->get();

        $rules = [
            'nom' => ['required','string','max:150'],
            'prenom' => ['required','string','max:150'],
            'cin' => ['nullable','string','max:30', Rule::unique('candidates','cin')->ignore($candidate->id)],
            'date_naissance' => ['nullable','date'],
            'tel' => ['nullable','string','max:50'],
            'email' => ['nullable','email','max:255'],
            'adresse' => ['nullable','string'],
            'gouvernorat' => ['nullable','string','max:150'],
            'code_postale' => ['nullable','string','max:20'],
        ];

        foreach ($defs as $def) {
            $fieldKey = 'field_'.$def->id;
            $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: $r[] = 'string'; $r[] = 'max:2000';
            }
            $rules[$fieldKey] = $r;
        }

        $validated = $request->validate($rules);

        DB::beginTransaction();
        try {
            $candidate->update([
                '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' => $validated['gouvernorat'] ?? null,
                'code_postale' => $validated['code_postale'] ?? null,
            ]);

            // update dynamic fields
            foreach ($defs as $def) {
                $fieldKey = 'field_'.$def->id;
                if ($def->input_type === 'file') {
                    if ($request->hasFile($fieldKey)) {
                        // delete existing files for this def+candidate
                        $existingFiles = FieldFile::where('candidate_id', $candidate->id)
                            ->where('field_definition_id', $def->id)
                            ->get();
                        foreach ($existingFiles as $ef) {
                            if ($ef->file_path && Storage::exists($ef->file_path)) {
                                Storage::delete($ef->file_path);
                            }
                            $ef->delete();
                        }

                        $file = $request->file($fieldKey);
                        $path = $file->store('candidate_files');
                        FieldFile::create([
                            'candidate_id' => $candidate->id,
                            'field_definition_id' => $def->id,
                            'file_path' => $path,
                            'original_name' => $file->getClientOriginalName(),
                            'mime_type' => $file->getClientMimeType(),
                            'size' => $file->getSize(),
                        ]);
                    }
                } else {
                    if ($request->filled($fieldKey)) {
                        FieldValue::updateOrCreate(
                            ['candidate_id' => $candidate->id, 'field_definition_id' => $def->id],
                            ['value' => $request->input($fieldKey)]
                        );
                    } else {
                        FieldValue::where('candidate_id', $candidate->id)
                            ->where('field_definition_id', $def->id)
                            ->delete();
                    }
                }
            }

            DB::commit();
            return redirect()->route('concours_ext.index', ['concours' => $concours->id])->with('success', 'تم تحديث الطلب بنجاح.');
        } catch (\Throwable $e) {
            DB::rollBack();
            \Log::error('Candidate update error: '.$e->getMessage());
            return back()->withInput()->withErrors(['general' => 'خطأ أثناء التحديث.']);
        }
    }

    /**
     * Change the etat (status) of a candidate.
     * Route: POST concours/{concours}/candidates/{candidate}/state
     */
    public function changeState(Request $request, Concours $concours, Candidate $candidate)
    {
        // ensure candidate belongs to this concours
        if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            return response()->json(['error' => 'Candidate not found for this concours.'], 404);
        }

        $allowed = [
            'refuse' => 'مرفوض',
            'en_cours' => 'قيد الدراسة',
            'acceptation_primaire' => 'قبول أولي',
            'acceptation_definitif' => 'قبول نهائي',
        ];

        $data = $request->validate([
            'etat' => ['required', 'string', function ($attr, $value, $fail) use ($allowed) {
                if (!array_key_exists($value, $allowed)) {
                    $fail('حالة غير صالحة.');
                }
            }],
        ]);

        $candidate->etat = $data['etat'];
        $candidate->save();

        $payload = [
            'success' => true,
            'etat' => $candidate->etat,
            'label' => $allowed[$candidate->etat] ?? $candidate->etat,
            'message' => 'تم تغيير حالة الطلب إلى: ' . ($allowed[$candidate->etat] ?? $candidate->etat),
        ];

        return $request->wantsJson() || $request->acceptsJson()
            ? response()->json($payload)
            : redirect()->back()->with('success', $payload['message']);
    }

    public function destroy(Concours $concours, Candidate $candidate)
    {
        if (! $concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            abort(404);
        }

        try {
            // delete files on disk
            foreach ($candidate->files as $f) {
                if ($f->file_path && Storage::exists($f->file_path)) {
                    Storage::delete($f->file_path);
                }
            }

            // detach pivot and delete candidate
            $concours->candidates()->detach($candidate->id);
            $candidate->delete();

            return redirect()->route('concours_ext.index', ['concours' => $concours->id])->with('success', 'تم حذف الطلب.');
        } catch (\Throwable $e) {
            \Log::error('Candidate destroy error: '.$e->getMessage());
            return back()->with('error', 'فشل الحذف.');
        }
    }
}
