<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Concours;
use App\Models\FieldDefinition;
use App\Models\Candidate;
use Illuminate\Validation\Rule;
use Illuminate\Support\Str;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use App\Models\FieldValue;
use App\Models\FieldFile;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Fill;
class ConcoursController extends Controller
{
	
/**
 * Export candidates of a concours to Excel (.xlsx).
 */
/**
 * Export candidates of a concours to Excel (.xlsx).
 */
public function exportCandidates(Concours $concours)
{
    // Allowed states (key => Arabic label)
    $allowed = [
        'refuse' => 'مرفوض',
        'en_cours' => 'قيد الدراسة',
        'acceptation_primaire' => 'قبول أولي',
        'acceptation_definitif' => 'قبول نهائي',
    ];

    // Load candidates (from candidates table)
    $candidates = $concours->candidates()->get();

    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $sheet->setTitle('المترشحون');

    // Top header: concours title (merged) - Now only K columns (A-K)
    $sheet->mergeCells('A1:K1');
    $sheet->setCellValue('A1', "قائمة المترشحين — {$concours->titre_ar}");
    $sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14);
    $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);

    // Leave row 2 blank (visual spacing)
    $startRow = 3;

    // Header row (row 3) - Removed full name column
    $headers = [
        'ID',                 // A
        'الاسم (nom)',        // B
        'اللقب (prenom)',     // C
        'CIN',                // D
        'تاريخ الميلاد',     // E
        'الهاتف',            // F
        'البريد الإلكتروني', // G
        'العنوان',           // H
        'الولاية',           // I
        'الرمز البريدي',     // J
        'الحالة'              // K
    ];

    $col = 'A';
    foreach ($headers as $h) {
        $sheet->setCellValue($col . $startRow, $h);
        $sheet->getStyle($col . $startRow)->getFont()->setBold(true);
        $sheet->getStyle($col . $startRow)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
        $sheet->getColumnDimension($col)->setAutoSize(true);
        $col++;
    }

    // Create a hidden sheet for validation list (states)
    $validationSheet = $spreadsheet->createSheet();
    $validationSheet->setTitle('Validation');
    
    // Store Arabic labels for dropdown
    $rowV = 1;
    foreach ($allowed as $key => $label) {
        $validationSheet->setCellValue("A{$rowV}", $label); // Store labels in column A
        $rowV++;
    }
    // Hide the validation sheet
    $validationSheet->setSheetState(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::SHEETSTATE_HIDDEN);

    // Fill data rows
    $row = $startRow + 1;
    foreach ($candidates as $cand) {
        $sheet->setCellValue("A{$row}", $cand->id);
        $sheet->setCellValue("B{$row}", $cand->nom);
        $sheet->setCellValue("C{$row}", $cand->prenom);
        // Skipped column D (full name)
        $sheet->setCellValue("D{$row}", $cand->cin);
        // date
        if ($cand->date_naissance) {
            $sheet->setCellValue("E{$row}", $cand->date_naissance->format('Y-m-d'));
        } else {
            $sheet->setCellValue("E{$row}", '');
        }
        $sheet->setCellValue("F{$row}", $cand->tel);
        $sheet->setCellValue("G{$row}", $cand->email);
        $sheet->setCellValue("H{$row}", $cand->adresse);
        $sheet->setCellValue("I{$row}", $cand->gouvernorat);
        $sheet->setCellValue("J{$row}", $cand->code_postale);
        
        // Set the current state LABEL (not code) in the column
        $currentStateLabel = $allowed[$cand->etat] ?? $cand->etat;
        $sheet->setCellValue("K{$row}", $currentStateLabel);

        // date format cell (if date present)
        $sheet->getStyle("E{$row}")->getNumberFormat()
              ->setFormatCode('yyyy-mm-dd');

        // add thin border for the row (now A-K instead of A-L)
        $sheet->getStyle("A{$row}:K{$row}")->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);

        $row++;
    }

    // Add data validation (dropdown) for the 'الحالة' column (now K instead of L)
    // Use the hidden sheet range for dropdown options
    $highestRow = $sheet->getHighestRow();
    $optionCount = count($allowed);
    
    for ($r = $startRow + 1; $r <= $highestRow; $r++) {
        $validation = $sheet->getCell("K{$r}")->getDataValidation();
        $validation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST);
        $validation->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_STOP);
        $validation->setAllowBlank(false);
        $validation->setShowInputMessage(true);
        $validation->setShowErrorMessage(true);
        $validation->setShowDropDown(true);
        $validation->setPromptTitle('اختر حالة');
        $validation->setPrompt('الرجاء اختيار حالة المترشح من القائمة');
        $validation->setErrorTitle('خطأ في الإدخال');
        $validation->setError('القيمة المدخلة غير صالحة. الرجاء الاختيار من القائمة.');
        
        // Use the hidden sheet range for dropdown options
        $validation->setFormula1("Validation!\$A\$1:\$A\${$optionCount}");
    }

    // Freeze header
    $sheet->freezePane('A' . ($startRow + 1));

    // Auto filter (now A-K instead of A-L)
    $sheet->setAutoFilter("A{$startRow}:K{$startRow}");

    // Set column width for status column (now K instead of L)
    $sheet->getColumnDimension('K')->setWidth(20);

    // Output to browser
    $writer = new Xlsx($spreadsheet);
    
    // Generate filename: concours name + creation date
    $concoursName = $concours->titre_ar;
    
    // Clean filename: remove invalid characters and replace spaces with underscores
    $cleanName = preg_replace('/[^\p{L}\p{N}\s\-]/u', '', $concoursName); // Keep letters, numbers, spaces, hyphens
    $cleanName = str_replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], '', $cleanName); // Remove Windows invalid chars
    $cleanName = trim($cleanName);
    
    // Use created_at date or current date if not available
    $creationDate = $concours->created_at ? $concours->created_at->format('Y-m-d') : date('Y-m-d');
    
    // Create filename with concours name and creation date
    $filename = "{$cleanName}_{$creationDate}.xlsx";
    
    // If filename is too long, truncate it
    if (strlen($filename) > 100) {
        $filename = substr($cleanName, 0, 50) . "_{$creationDate}.xlsx";
    }

    // Send headers
    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
    header('Cache-Control: max-age=0');
    header('Expires: 0');
    header('Pragma: public');

    $writer->save('php://output');
    exit;
}
/**
 * Import an edited Excel file and update candidate 'etat' values.
 */
/**
 * Import an edited Excel file and update candidate 'etat' values.
 */
public function importCandidates(Request $request, Concours $concours)
{
    $request->validate([
        'file' => 'required|file|mimes:xlsx,xls,csv|max:5120',
    ]);

    $file = $request->file('file');

    $spreadsheet = IOFactory::load($file->getPathname());
    $sheet = $spreadsheet->getActiveSheet();

    // Allowed states mapping (Arabic label => code)
    $allowedReverse = [
        'مرفوض' => 'refuse',
        'قيد الدراسة' => 'en_cours',
        'قبول أولي' => 'acceptation_primaire',
        'قبول نهائي' => 'acceptation_definitif',
    ];

    // Get the start row based on the file structure
    // Row 1: Title, Row 2: Blank, Row 3: Headers, Row 4: First data row
    $startRow = 4;
    $highestRow = $sheet->getHighestRow();

    $updated = 0;
    $skipped = 0;
    $errors = [];

    // Get the header row to verify we're reading the right columns
    // Now we have only A-K columns (11 columns)
    $headers = [];
    for ($col = 'A'; $col <= 'K'; $col++) {
        $headers[$col] = trim((string)$sheet->getCell($col . '3')->getValue());
    }

    // Verify this is the correct format by checking key headers
    // Now column K is the status column (previously L)
    if ($headers['A'] !== 'ID' || $headers['K'] !== 'الحالة') {
        return redirect()->back()->with('error', 'ملف غير صالح. يرجى استخدام الملف الذي تم تصديره من النظام.');
    }

    for ($r = $startRow; $r <= $highestRow; $r++) {
        // Read all data from the row - Adjusted for removed full name column
        $id = trim((string)$sheet->getCell("A{$r}")->getValue());
        $nom = trim((string)$sheet->getCell("B{$r}")->getValue());
        $prenom = trim((string)$sheet->getCell("C{$r}")->getValue());
        $cin = trim((string)$sheet->getCell("D{$r}")->getValue()); // Now column D (was E)
        $dateNaissance = trim((string)$sheet->getCell("E{$r}")->getValue()); // Now column E (was F)
        $tel = trim((string)$sheet->getCell("F{$r}")->getValue()); // Now column F (was G)
        $email = trim((string)$sheet->getCell("G{$r}")->getValue()); // Now column G (was H)
        $adresse = trim((string)$sheet->getCell("H{$r}")->getValue()); // Now column H (was I)
        $gouvernorat = trim((string)$sheet->getCell("I{$r}")->getValue()); // Now column I (was J)
        $codePostale = trim((string)$sheet->getCell("J{$r}")->getValue()); // Now column J (was K)
        $newEtatLabel = trim((string)$sheet->getCell("K{$r}")->getValue()); // Now column K (was L)

        // Skip empty rows
        if (empty($id) && empty($nom) && empty($prenom) && empty($cin)) {
            continue;
        }

        // Skip if no state is provided
        if (empty($newEtatLabel)) {
            $skipped++;
            $errors[] = "السطر {$r}: لم يتم تحديد حالة جديدة للمرشح: {$nom} {$prenom} (ID: {$id}, CIN: {$cin})";
            continue;
        }

        // Validate the state label
        if (!array_key_exists($newEtatLabel, $allowedReverse)) {
            $errors[] = "السطر {$r}: حالة غير صالحة ({$newEtatLabel}). يجب أن تكون واحدة من: " . implode('، ', array_keys($allowedReverse));
            continue;
        }

        // Convert Arabic label to code
        $newEtatCode = $allowedReverse[$newEtatLabel];

        // Try to find candidate by ID first (most reliable)
        $candidate = null;
        if (!empty($id)) {
            $candidate = \App\Models\Candidate::find($id);
        }
        
        // If not found by ID, try by CIN
        if (!$candidate && !empty($cin)) {
            $candidate = \App\Models\Candidate::where('cin', $cin)->first();
        }
        
        // If still not found, try by name and email combination
        if (!$candidate && !empty($nom) && !empty($prenom) && !empty($email)) {
            $candidate = \App\Models\Candidate::where('nom', $nom)
                ->where('prenom', $prenom)
                ->where('email', $email)
                ->first();
        }

        if (!$candidate) {
            $errors[] = "السطر {$r}: لم يتم العثور على المرشح في قاعدة البيانات (ID: {$id}, CIN: {$cin}, الاسم: {$nom} {$prenom})";
            continue;
        }

        // Ensure the candidate belongs to this concours
        if (!$concours->candidates()->where('candidates.id', $candidate->id)->exists()) {
            $errors[] = "السطر {$r}: المرشح (ID: {$candidate->id}, الاسم: {$candidate->nom} {$candidate->prenom}) لا ينتمي إلى هذه المناظرة.";
            continue;
        }

        // Check if the state has actually changed
        if ($candidate->etat === $newEtatCode) {
            $skipped++;
            continue; // No change needed
        }

        // Update the candidate's state
        try {
            $candidate->etat = $newEtatCode;
            $candidate->save();
            $updated++;
            
            // Log the change (optional)
            \Log::info('Candidate state updated', [
                'candidate_id' => $candidate->id,
                'concours_id' => $concours->id,
                'old_state' => $candidate->getOriginal('etat'),
                'new_state' => $newEtatCode,
                'new_state_label' => $newEtatLabel,
                'updated_by' => auth()->id() ?? 'system',
                'updated_at' => now(),
            ]);
            
        } catch (\Exception $e) {
            $errors[] = "السطر {$r}: فشل تحديث حالة المرشح (ID: {$candidate->id}): " . $e->getMessage();
        }
    }

    // Prepare response message
    $message = "تم استيراد النتائج بنجاح.";
    $details = [];
    
    if ($updated > 0) {
        $details[] = "تم تحديث {$updated} مترشح(ـة).";
    }
    
    if ($skipped > 0) {
        $details[] = "تم تخطي {$skipped} مترشح(ـة) (إما لم تتغير حالتهم أو لم يتم تحديد حالة جديدة).";
    }
    
    if (!empty($details)) {
        $message .= ' ' . implode(' ', $details);
    }

    // Handle errors
    if (!empty($errors)) {
        // Store errors in session with a limit to avoid session overflow
        $errorCount = count($errors);
        $displayErrors = array_slice($errors, 0, 10); // Show first 10 errors
        
        $errorMessage = "تمت العملية مع {$errorCount} خطأ(أخطاء):<br><br>";
        $errorMessage .= implode('<br>', $displayErrors);
        
        if ($errorCount > 10) {
            $errorMessage .= "<br><br>...و " . ($errorCount - 10) . " خطأ آخر.";
        }
        
        $request->session()->flash('warning', $errorMessage);
    }

    return redirect()->back()->with('success', $message);
}


/**
 * Preview import file to verify format
 */
/**
 * Preview import file to verify format
 */
public function previewImport(Request $request, Concours $concours)
{
    $request->validate([
        'file' => 'required|file|mimes:xlsx,xls,csv|max:5120',
    ]);

    $file = $request->file('file');
    
    try {
        $spreadsheet = IOFactory::load($file->getPathname());
        $sheet = $spreadsheet->getActiveSheet();
        
        // Get headers - Now A to K (11 columns)
        $headers = [];
        for ($col = 'A'; $col <= 'K'; $col++) {
            $headers[$col] = trim((string)$sheet->getCell($col . '3')->getValue());
        }
        
        // Get first few rows of data for preview
        $previewData = [];
        $startRow = 4;
        $previewRows = min(10, $sheet->getHighestRow() - $startRow + 1);
        
        for ($r = $startRow; $r < $startRow + $previewRows; $r++) {
            $rowData = [
                'id' => trim((string)$sheet->getCell("A{$r}")->getValue()),
                'nom' => trim((string)$sheet->getCell("B{$r}")->getValue()),
                'prenom' => trim((string)$sheet->getCell("C{$r}")->getValue()),
                'etat' => trim((string)$sheet->getCell("K{$r}")->getValue()), // Now column K
            ];
            
            if (!empty($rowData['id']) || !empty($rowData['nom'])) {
                $previewData[] = $rowData;
            }
        }
        
        return view('concours.import-preview', [
            'concours' => $concours,
            'headers' => $headers,
            'previewData' => $previewData,
            'totalRows' => $sheet->getHighestRow() - $startRow + 1,
            'filename' => $file->getClientOriginalName(),
        ]);
        
    } catch (\Exception $e) {
        return redirect()->back()->with('error', 'فشل قراءة الملف: ' . $e->getMessage());
    }
}


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

    $query = Concours::withCount('candidates')
                     ->with('user:id,nom_prenom'); 

    if ($q !== '') {
        $query->where(function ($qb) use ($q) {
            $qb->where('titre_ar', 'like', "%{$q}%")
               ->orWhere('titre_fr', 'like', "%{$q}%")
               ->orWhere('titre_en', 'like', "%{$q}%")
               ->orWhere('etat', 'like', "%{$q}%");
        });
    }

    $concours = $query->orderBy('date_debut', 'desc')->paginate(15)->withQueryString();

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


    // Show create form
    public function create()
    {
        // In create mode we do NOT pass the global list of fields.
        // The admin will create fields inline; show core fields only.
        return view('backoffice.concours.form', [
            'concours' => new Concours(),
            'fields' => collect(), // empty collection
            'method' => 'create'
        ]);
    }

    /**
 * Normalize an options input that may be an array or a string (comma/newline separated).
 * Returns an array of trimmed non-empty values.
 */
protected function normalizeOptions($input): array
{
    if (is_array($input)) {
        $items = $input;
    } else {
        $items = preg_split('/\r\n|\n|\r|,/', (string) $input);
    }

    return array_values(array_filter(array_map('trim', $items), fn($v) => $v !== ''));
}


// Persist new concours
public function store(Request $request)
{
    $data = $request->validate([
        
        'titre'     => ['nullable','string','max:255'],
        'titre_ar'  => ['nullable','string','max:255'],
        'titre_fr'  => ['nullable','string','max:255'],
        'titre_en'  => ['nullable','string','max:255'],
        'remarques' => ['nullable','string','max:2000'],
        'etat' => ['required','string', Rule::in(['draft','published','closed'])],
        'date_debut' => ['nullable','date'],
        'date_fin' => ['nullable','date'],
        'fields' => ['nullable','array'],
        'fields.*' => ['integer','exists:field_definitions,id'],
        'core_fields' => ['nullable','array'],
        'core_fields.*' => ['string'],
        'core_required' => ['nullable','array'],
        'core_required.*' => ['string'],
        'new_fields' => ['nullable','array'],
        'new_fields.*.label' => ['required_with:new_fields','string','max:255'],
        'new_fields.*.input_type' => ['required_with:new_fields','string'],
        'new_fields.*.is_required' => ['nullable'],
        'new_fields.*.is_visible' => ['nullable'],
        'new_fields.*.options' => ['nullable'],
        'new_fields.*.sort_order' => ['nullable','integer'],
        'new_fields_payload' => ['nullable','string'],
    ]);

    if (empty($data['titre_ar'] ?? '') && empty($data['titre_fr'] ?? '') && empty($data['titre_en'] ?? '') && empty($data['titre'] ?? '')) {
        return back()->withInput()->withErrors(['titre' => 'Please provide at least one title (Arabic, French or English).']);
    }

    \DB::beginTransaction();
    try {
        $data['id_user'] = auth()->id() ?? $request->input('id_user');

        // choose sensible fallbacks:
        $titre_ar = $data['titre_ar'] ?? ($data['titre'] ?? null);
        $titre_fr = $data['titre_fr'] ?? ($data['titre'] ?? null);
        $titre_en = $data['titre_en'] ?? ($data['titre'] ?? null);

		$concours = new Concours();
		$concours->fill([
			'titre_ar' => $titre_ar,
			'titre_fr' => $titre_fr,
			'titre_en' => $titre_en,
			'etat' => $data['etat'],
			'date_debut' => $data['date_debut'] ?? null,
			'date_fin' => $data['date_fin'] ?? null,
			'id_user' => $data['id_user'] ?? null,
		]);
		$concours->remarques = $data['remarques'] ?? null;
		$concours->save();

        // Defensive normalizer (arrays, json string, comma/newline string, nested arrays)
        $normalizeOptions = function($input) {
            if (is_string($input)) {
                $decoded = json_decode($input, true);
                if (is_array($decoded)) {
                    $items = $decoded;
                } else {
                    $items = preg_split('/\r\n|\n|\r|,/', $input);
                }
            } elseif (is_array($input)) {
                $items = $input;
            } else {
                $items = [];
            }

            $flat = [];
            array_walk_recursive($items, function($v) use (&$flat) {
                $v = is_null($v) ? '' : trim((string)$v);
                if ($v !== '') $flat[] = $v;
            });

            $seen = [];
            $out = [];
            foreach ($flat as $v) {
                if (!isset($seen[$v])) {
                    $seen[$v] = true;
                    $out[] = $v;
                }
            }
            return $out;
        };

        $newFieldsPayload = [];
        if ($request->filled('new_fields_payload')) {
            $decoded = json_decode($request->input('new_fields_payload'), true);
            if (is_array($decoded)) {
                $newFieldsPayload = $decoded;
            } else {
                \Log::warning('Concours.store: new_fields_payload present but invalid JSON', ['payload' => substr($request->input('new_fields_payload'),0,1000)]);
            }
        }
        // fallback to classic form inputs
        if (empty($newFieldsPayload) && $request->has('new_fields')) {
            $newFieldsPayload = $request->input('new_fields', []);
        }

        \Log::debug('Concours.store: incoming new_fields (raw)', ['raw' => $request->input('new_fields')]);
        \Log::debug('Concours.store: incoming new_fields_payload (decoded)', ['decoded_payload' => $newFieldsPayload]);

        $newFieldIds = [];

        foreach ($newFieldsPayload as $idx => $nf) {
            $label = isset($nf['label']) ? trim((string)$nf['label']) : '';
            if ($label === '') {
                \Log::debug("Concours.store: skipping new_field[$idx] because label empty", ['nf' => $nf]);
                continue;
            }

            $input_type = isset($nf['input_type']) ? (string)$nf['input_type'] : 'text';
            $is_required = (!empty($nf['is_required']) && (int)$nf['is_required'] === 1) ? 1 : 0;
            $is_visible = isset($nf['is_visible']) ? ((int)$nf['is_visible'] === 1 ? 1 : 0) : 1;

            // raw options value (could be array or string)
            $rawOptions = $nf['options'] ?? [];
            \Log::debug("Concours.store: new_field[$idx] rawOptions type", ['type' => gettype($rawOptions), 'raw' => $rawOptions]);

            $opts = $normalizeOptions($rawOptions);
            \Log::debug("Concours.store: new_field[$idx] normalized options", ['opts' => $opts]);

            // keep options only for multiple-choice types
            if (!in_array($input_type, ['select','radio','checkbox'])) {
                $opts = [];
            }

            // compute sort order BEFORE building the data array
            $sortOrder = 100;
            if (isset($nf['sort_order']) && $nf['sort_order'] !== '') {
                $sortOrder = (int) $nf['sort_order'];
            }

            $key = Str::slug(mb_substr($label,0,40) . '-' . time() . '-' . Str::random(4), '_');

            $fdData = [
                'key' => $key,
                'label' => $label,
                'input_type' => $input_type,
                'is_visible' => (bool)$is_visible,
                'is_required' => (bool)$is_required,
                'sort_order' => $sortOrder,
            ];

            if (!empty($opts)) {
                $fdData['options_json'] = json_encode(array_values($opts), JSON_UNESCAPED_UNICODE);
            } else {
                $fdData['options_json'] = null;
            }

            \Log::debug("Concours.store: creating FieldDefinition", ['index' => $idx, 'fdData' => $fdData]);

            $fd = FieldDefinition::create($fdData);

            if ($fd && $fd->id) {
                $newFieldIds[] = $fd->id;
                \Log::debug("Concours.store: FieldDefinition created", ['id' => $fd->id, 'options_json' => $fd->options_json]);
            } else {
                \Log::warning("Concours.store: FieldDefinition create returned falsy", ['index' => $idx, 'fd' => $fd]);
            }
        }

        // attach existing selected fields
        $selectedFieldIds = array_values(array_unique((array)$request->input('fields', [])));

        // If admin toggled visibility/required for existing fields, update FieldDefinition rows accordingly.
        $fieldVisibleMap = $request->input('field_visible', []);
        $fieldRequiredMap = $request->input('field_required', []);

        foreach ($selectedFieldIds as $fid) {
            $update = [];
            if (isset($fieldVisibleMap[$fid])) $update['is_visible'] = (int)$fieldVisibleMap[$fid] === 1 ? 1 : 0;
            if (isset($fieldRequiredMap[$fid])) $update['is_required'] = (int)$fieldRequiredMap[$fid] === 1 ? 1 : 0;
            if (!empty($update)) {
                FieldDefinition::where('id', $fid)->update($update);
            }
        }

        $allFieldIds = array_values(array_unique(array_merge($selectedFieldIds, $newFieldIds)));
        $concours->fields()->sync($allFieldIds);

        \DB::commit();

        return redirect()->route('concours.index')->with('success', 'تم إنشاء المسابقة بنجاح.');
    } catch (\Throwable $e) {
        \DB::rollBack();
        \Log::error('Concours store error: ' . $e->getMessage(), ['trace' => $e->getTraceAsString(), 'request_all' => substr(json_encode($request->all()),0,2000)]);
        return back()->withInput()->withErrors(['general' => 'حدث خطأ أثناء إنشاء المسابقة. حاول مرة أخرى أو تواصل مع الدعم.']);
    }
}


    // Show a concours with fields and candidates
    public function show(Concours $concours)
    {
        $concours->load(['fields', 'candidates']);
        $candidates = $concours->candidates()->paginate(25);
        return view('backoffice.concours.show', compact('concours','candidates'));
    }

    // Edit form
    public function edit(Concours $concours)
    {
        // Load only fields attached to this concours, ordered by sort_order
        $concours->load('fields');
        $attachedFields = $concours->fields()->orderBy('sort_order')->get();

        return view('backoffice.concours.form', [
            'concours' => $concours,
            'fields' => $attachedFields, // only attached fields
            'method' => 'edit'
        ]);
    }

    // Update concours

public function update(Request $request, Concours $concours)
{
    $data = $request->validate([
        'titre'     => ['nullable', 'string', 'max:255'],
        'titre_ar'  => ['nullable', 'string', 'max:255'],
        'titre_fr'  => ['nullable', 'string', 'max:255'],
        'titre_en'  => ['nullable', 'string', 'max:255'],

        'etat' => ['required', 'string', Rule::in(['draft','published','closed'])],
        'date_debut' => ['nullable', 'date'],
        'date_fin' => ['nullable', 'date'],
        'remarques' => ['nullable','string','max:2000'],
        'fields' => ['nullable', 'array'],
        'fields.*' => ['integer', 'exists:field_definitions,id'],
        'edit_fields' => ['nullable', 'array'],
        'edit_fields.*.label' => ['sometimes', 'string', 'max:255'],
        'edit_fields.*.input_type' => ['sometimes', 'string'],
        'edit_fields.*.is_required' => ['nullable'],
        'edit_fields.*.is_visible' => ['nullable'],
        'edit_fields.*.sort_order' => ['nullable', 'integer'],
        'new_fields' => ['nullable', 'array'],
        'new_fields.*.label' => ['required_with:new_fields', 'string', 'max:255'],
        'new_fields.*.input_type' => ['required_with:new_fields', 'string'],
        'new_fields.*.is_required' => ['nullable'],
        'new_fields.*.is_visible' => ['nullable'],
        'new_fields.*.options' => ['nullable'],
        'new_fields.*.sort_order' => ['nullable', 'integer'],
        'delete_fields' => ['nullable', 'array'],
        'delete_fields.*' => ['integer'],
    ]);

    if (empty($data['titre_ar'] ?? '') && empty($data['titre_fr'] ?? '') && empty($data['titre_en'] ?? '') && empty($data['titre'] ?? '')) {
        return back()->withInput()->withErrors(['titre' => 'Please provide at least one title (Arabic, French or English).']);
    }

    DB::beginTransaction();
    try {
        $titre_ar = $data['titre_ar'] ?? ($data['titre'] ?? $concours->titre_ar);
        $titre_fr = $data['titre_fr'] ?? ($data['titre'] ?? $concours->titre_fr);
        $titre_en = $data['titre_en'] ?? ($data['titre'] ?? $concours->titre_en);

        $concours->update([
            'titre_ar' => $titre_ar,
            'titre_fr' => $titre_fr,
            'titre_en' => $titre_en,
            'etat' => $data['etat'],
            'date_debut' => $data['date_debut'] ?? null,
            'date_fin' => $data['date_fin'] ?? null,
			'remarques' => $data['remarques'] ?? null,
        ]);

        if ($request->filled('edit_fields')) {
            foreach ($request->input('edit_fields') as $fid => $ef) {
                $fid = (int)$fid;
                $fd = FieldDefinition::find($fid);
                if (!$fd) continue;

                $update = [];

                if (isset($ef['label'])) $update['label'] = trim((string)$ef['label']);
                if (isset($ef['input_type'])) $update['input_type'] = (string)$ef['input_type'];
                if (isset($ef['is_required'])) $update['is_required'] = (int)$ef['is_required'] === 1 ? 1 : 0;
                if (isset($ef['is_visible'])) $update['is_visible'] = (int)$ef['is_visible'] === 1 ? 1 : 0;

                if (isset($ef['sort_order']) && $ef['sort_order'] !== '') {
                    $update['sort_order'] = (int) $ef['sort_order'];
                }

                if (array_key_exists('options', $ef)) {
                    $rawOptions = $ef['options'];
                    $opts = $this->normalizeOptions($rawOptions);
                    $effectiveType = $update['input_type'] ?? $fd->input_type;
                    if (!in_array($effectiveType, ['select','radio','checkbox'])) {
                        $opts = [];
                    }
                    $update['options_json'] = empty($opts) ? null : json_encode(array_values($opts), JSON_UNESCAPED_UNICODE);
                }

                if (!empty($update)) {
                    $fd->update($update);
                }
            }
        }

        if ($request->filled('delete_fields')) {
            $delIds = array_values(array_unique(array_map('intval', (array)$request->input('delete_fields', []))));
            if (!empty($delIds)) {
                DB::table('concours_field')->whereIn('field_definition_id', $delIds)->delete();

                $files = FieldFile::whereIn('field_definition_id', $delIds)->get();
                foreach ($files as $f) {
                    if (!empty($f->file_path) && Storage::exists($f->file_path)) {
                        try { Storage::delete($f->file_path); } catch (\Throwable $e) { \Log::warning('Failed deleting stored file: '.$f->file_path); }
                    }
                }
                FieldFile::whereIn('field_definition_id', $delIds)->delete();
                FieldValue::whereIn('field_definition_id', $delIds)->delete();
                FieldDefinition::whereIn('id', $delIds)->delete();
            }
        }
		
        $newFieldIds = [];
        if ($request->filled('new_fields')) {
            foreach ($request->input('new_fields') as $nf) {
                $label = trim($nf['label'] ?? '');
                if ($label === '') continue;
                $input_type = $nf['input_type'] ?? 'text';
                $is_required = (!empty($nf['is_required']) && (int)$nf['is_required'] === 1) ? 1 : 0;
                $options_input = $nf['options'] ?? '';

                $opts = $this->normalizeOptions($options_input);
                if (!in_array($input_type, ['select','radio','checkbox'])) {
                    $opts = [];
                }

                $sortOrderForNew = (isset($nf['sort_order']) && $nf['sort_order'] !== '') ? (int)$nf['sort_order'] : 100;

                $fd = FieldDefinition::create([
                    'key' => Str::slug(mb_substr($label, 0, 40) . '-' . time(), '_'),
                    'label' => $label,
                    'input_type' => $input_type,
                    'options_json' => empty($opts) ? null : json_encode($opts, JSON_UNESCAPED_UNICODE),
                    'is_visible' => true,
                    'is_required' => $is_required,
                    'sort_order' => $sortOrderForNew,
                ]);

                if ($fd && $fd->id) $newFieldIds[] = $fd->id;
            }
        }

        // -------------------------
        // Update visibility/required for selected existing fields
        // -------------------------
        $selectedFieldIds = array_values(array_unique((array)$request->input('fields', [])));
        $fieldVisibleMap = $request->input('field_visible', []);
        $fieldRequiredMap = $request->input('field_required', []);
        foreach ($selectedFieldIds as $fid) {
            $update = [];
            if (isset($fieldVisibleMap[$fid])) $update['is_visible'] = (int)$fieldVisibleMap[$fid] === 1 ? 1 : 0;
            if (isset($fieldRequiredMap[$fid])) $update['is_required'] = (int)$fieldRequiredMap[$fid] === 1 ? 1 : 0;
            if (!empty($update)) {
                FieldDefinition::where('id', (int)$fid)->update($update);
            }
        }

        // -------------------------
        // Final attach list: keep only selected + newly created
        // -------------------------
        $finalAttach = array_values(array_unique(array_merge($selectedFieldIds, $newFieldIds)));
        $concours->fields()->sync($finalAttach);

        DB::commit();

        return redirect()->route('concours.index')->with('success', 'تم حفظ التغييرات.');
    } catch (\Throwable $e) {
        DB::rollBack();
        \Log::error('Concours update error: ' . $e->getMessage(), ['trace' => $e->getTraceAsString(), 'request_all' => substr(json_encode($request->all()), 0, 2000)]);
        return back()->withInput()->withErrors(['general' => 'حدث خطأ أثناء حفظ التغييرات. حاول مرة أخرى أو تواصل مع الدعم.']);
    }
}

/**
 * Remove the specified FieldDefinition from storage (safe cascade).
 *
 * @param  \App\Models\FieldDefinition  $fieldDefinition
 * @return \Illuminate\Http\RedirectResponse
 */
 public function destroy(Concours $concours)
    {
        // detach candidates relation if you want keep candidates rows but unlink:
        $concours->candidates()->detach();

        // optionally delete pivot entries with fields (sync([]) will remove)
        $concours->fields()->sync([]);

        $concours->delete();

        return redirect()->route('concours.index')->with('success', 'تم حذف المناظرة بنجاح');
    }
   // Quick change state (AJAX or normal form)
    public function changeState(Request $request, Concours $concours)
    {
        $data = $request->validate([
            'etat' => ['required', Rule::in(['draft','published','closed'])],
        ]);

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

        if ($request->wantsJson() || $request->acceptsJson()) {
            return response()->json(['success' => true, 'etat' => $concours->etat, 'label' => $concours->etat_label]);
        }

        return redirect()->back()->with('success', 'تم تحديث الحالة.');
    }
}
