from editing import *
replace('app/Http/Requests/Admin/EntrepriseRequest.php',"if (! $user?->isAdmin() && $this->filled('user_id')) {", "if (! $user?->isAdmin() && $this->filled('user_id') && (int) $this->input('user_id') !== (int) ($this->route('entreprise')?->user_id ?? $user?->id)) {")
# Never apply a non-admin owner field, including an explicit null.
replace('app/Http/Controllers/Admin/EntrepriseController.php',"return Arr::only($request->validated(),", "$data = Arr::only($request->validated(),")
replace('app/Http/Controllers/Admin/EntrepriseController.php',"'twitter', 'visibilite_liens']);", "'twitter', 'visibilite_liens']);\n        if (! $request->user()->isAdmin()) { unset($data['user_id']); }\n        return $data;")
replace('app/Http/Requests/Admin/EntrepriseProduitRequest.php',"$many = ['specificite', 'conservation', 'conditionnement', 'type_contenant', 'duree_vie', 'code_barre', 'quantite_minimale_commande', 'disponibilite'];", "$many = ['specificite' => 'specificites', 'conservation' => 'conservations', 'conditionnement' => 'conditionnements', 'type_contenant' => 'type_contenants', 'duree_vie' => 'duree_vies', 'code_barre' => 'code_barres', 'quantite_minimale_commande' => 'quantite_minimale_commandes', 'disponibilite' => 'disponibilites'];")
replace('app/Http/Requests/Admin/EntrepriseProduitRequest.php','foreach ($many as $relation)', 'foreach ($many as $relation => $table)')
replace('app/Http/Requests/Admin/EntrepriseProduitRequest.php',"['integer'];", "['integer', 'distinct', 'exists:'.$table.',id'];")
replace('app/Http/Controllers/Admin/EntrepriseProduitController.php',"        $produit = $entreprise->entrepriseProduits()->create($request->safe()->only(['produit_id', 'volume_id', 'type_transaction_id']));\n        $this->sync($produit, $request->validated());", "        \\Illuminate\\Support\\Facades\\DB::transaction(function () use ($request, $entreprise): void {\n            $produit = $entreprise->entrepriseProduits()->create($request->safe()->only(['produit_id', 'volume_id', 'type_transaction_id']));\n            $this->sync($produit, $request->validated());\n        });")
replace('app/Http/Controllers/Admin/EntrepriseProduitController.php',"        $entrepriseProduit->update($request->safe()->only(['produit_id', 'volume_id', 'type_transaction_id']));\n        $this->sync($entrepriseProduit, $request->validated());", "        \\Illuminate\\Support\\Facades\\DB::transaction(function () use ($request, $entrepriseProduit): void {\n            $entrepriseProduit->update($request->safe()->only(['produit_id', 'volume_id', 'type_transaction_id']));\n            $this->sync($entrepriseProduit, $request->validated());\n        });")
replace('app/Http/Requests/Admin/UpsertReferenceRequest.php',"'mrc_id' => ['nullable',", "'mrc_id' => [$this->route('reference') === 'villes' ? 'required' : 'nullable',")
replace('app/Policies/UserPolicy.php',"return $user->isAdmin() || $user->is($subject);", "return $user->isAdmin() || ($user->hasPermission('manageUsers') && ! $subject->isAdmin()) || $user->is($subject);")
replace('app/Policies/UserPolicy.php',"&& ! $user->is($subject);", "&& ! $user->is($subject) && ($user->isAdmin() || ! $subject->isAdmin());")
write('app/Http/Requests/Admin/UserRequest.php',r'''<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class UserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->can($this->route('user') ? 'update' : 'create', $this->route('user') ?? \App\Models\User::class) ?? false;
    }
    public function rules(): array
    {
        $role = Rule::exists('roles', 'id');
        if (! $this->user()->isAdmin()) { $role->whereIn('slug', ['acheteur', 'entreprise', 'mrc']); }
        return [
            'name' => ['required', 'string', 'max:255'], 'firstName' => ['nullable', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->route('user'))],
            'password' => ['nullable', 'string', 'min:12', 'confirmed'],
            'mrc_id' => ['nullable', 'integer', 'exists:mrcs,id'],
            'role_ids' => ['required', 'array', 'min:1'], 'role_ids.*' => ['integer', 'distinct', $role],
        ];
    }
    public function after(): array
    {
        return [function (Validator $v): void {
            $roles = \App\Models\Role::whereIn('id', (array) $this->input('role_ids', []))->pluck('slug');
            if ($roles->contains('mrc') && ! $this->filled('mrc_id')) { $v->errors()->add('mrc_id', 'Choisissez la MRC du gestionnaire.'); }
            $subject = $this->route('user');
            if ($subject?->isAdmin() && ! $roles->contains('admin') && ($this->user()->is($subject) || ! \App\Models\User::whereKeyNot($subject->id)->whereHas('roles', fn ($q) => $q->where('slug', 'admin'))->exists())) {
                $v->errors()->add('role_ids', 'Conservez votre rôle administrateur et au moins un administrateur actif.');
            }
        }];
    }
}''')
replace('app/Http/Controllers/Admin/UserController.php',"$this->options()", "$this->options($request->user())")
replace('app/Http/Controllers/Admin/UserController.php',"        $user = User::create(Arr::except($request->validated(), ['role_ids']));\n        $user->roles()->sync($request->input('role_ids', []));",r'''        $user = \Illuminate\Support\Facades\DB::transaction(function () use ($request): User {
            $user = User::create([...Arr::except($request->validated(), ['role_ids', 'password']), 'password' => \Illuminate\Support\Str::password(40)]);
            $user->forceFill(['invitation_pending' => true])->save();
            $user->roles()->sync($request->validated('role_ids'));
            return $user;
        });
        app(\App\Services\AccountInvitation::class)->send($user);''')
replace('app/Http/Controllers/Admin/UserController.php',"'Utilisateur créé.'", "'Utilisateur créé. Une invitation lui a été envoyée pour activer son compte et choisir son mot de passe.'")
replace('app/Http/Controllers/Admin/UserController.php',"if ($request->user()->isAdmin()) {", "if ($request->user()->isAdmin() || $request->user()->hasPermission('manageUsers')) {")
replace('app/Http/Controllers/Admin/UserController.php','private function options(): array','private function options(User $actor): array')
replace('app/Http/Controllers/Admin/UserController.php',"Role::orderBy('name')->get", "Role::when(! $actor->isAdmin(), fn ($q) => $q->whereIn('slug', ['acheteur', 'entreprise', 'mrc']))->orderBy('name')->get")
replace('app/Http/Controllers/Admin/UserController.php','    /** @return array<string, mixed> */',r'''    public function invite(Request $request, User $user): RedirectResponse
    {
        $this->authorize('update', $user);
        abort_unless($user->invitation_pending, 422, 'Ce compte est déjà activé.');
        app(\App\Services\AccountInvitation::class)->send($user);
        return back()->with('success', 'Invitation renvoyée.');
    }

    /** @return array<string, mixed> */''')
replace('routes/web.php',"Route::resource('users', AdminUserController::class)->except('show');", "Route::resource('users', AdminUserController::class)->except('show');\n        Route::post('users/{user}/invitation', [AdminUserController::class, 'invite'])->middleware('throttle:5,1')->name('users.invite');")
replace('app/Http/Controllers/Admin/AdminController.php','->latest()->limit(6)',"->latest('updated_at')->orderByDesc('id')->limit(6)")
write('app/Http/Controllers/Public/EntrepriseExportController.php',r'''<?php
namespace App\Http\Controllers\Public;
use App\Http\Controllers\Controller;
use App\Http\Resources\PublicEntrepriseResource;
use App\Models\Entreprise;
use App\Services\CatalogueSearch;
use App\Services\CsvCell;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
class EntrepriseExportController extends Controller
{
    public function __construct(private readonly CatalogueSearch $catalogueSearch) {}
    public function csv(Request $request): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $rows = $this->selected($request);
        return response()->streamDownload(function () use ($rows): void {
            $out = fopen('php://output', 'wb');
            fwrite($out, "\xEF\xBB\xBF");
            fputcsv($out, ['Entreprise', 'Ville', 'MRC', 'Courriel', 'Site web', 'Produits'], ';');
            foreach ($rows as $e) {
                fputcsv($out, array_map([CsvCell::class, 'text'], [$e['nom'], $e['ville']['nom'] ?? '', $e['ville']['mrc']['nom'] ?? '', $e['courriel'], $e['site_web'], collect($e['produits'])->pluck('produit.nom')->filter()->unique()->implode(', ')]), ';');
            }
            fclose($out);
        }, 'entreprises-raca-'.now()->format('Y-m-d').'.csv', ['Content-Type' => 'text/csv; charset=UTF-8']);
    }
    public function pdf(Request $request): \Illuminate\Http\Response
    {
        return Pdf::loadView('pdf.entreprises', ['entreprises' => $this->selected($request), 'criteria' => $this->criteria($request)])
            ->setPaper('a4', 'landscape')->download('entreprises-raca-'.now()->format('Y-m-d').'.pdf');
    }
    public function show(Entreprise $entreprise): \Illuminate\Http\Response
    {
        abort_unless(Entreprise::query()->published()->whereKey($entreprise->id)->exists(), 404);
        $entreprise->load(CatalogueSearch::RELATIONS);
        $data = (new PublicEntrepriseResource($entreprise))->resolve(request());
        return Pdf::loadView('pdf.entreprise', ['entreprise' => $data])->setPaper('letter')->download('entreprise-'.str($entreprise->nom)->slug().'.pdf');
    }
    private function selected(Request $request): \Illuminate\Support\Collection
    {
        $data = $request->validate(['ids' => ['nullable', 'array', 'max:500'], 'ids.*' => ['integer']]);
        $q = $this->catalogueSearch->query((string) $request->input('q', ''), $this->catalogueSearch->filtersFromRequest($request));
        if ($data['ids'] ?? []) { $q->whereKey($data['ids']); }
        return $q->get()->map(fn ($e) => (new PublicEntrepriseResource($e))->resolve($request));
    }
    private function criteria(Request $request): string
    {
        $parts = $this->catalogueSearch->describeFilters($this->catalogueSearch->filtersFromRequest($request));
        if ($request->filled('q')) { array_unshift($parts, 'Recherche : '.$request->string('q')->toString()); }
        return implode(' · ', $parts) ?: 'Toutes les entreprises publiées';
    }
}''')
replace('app/Services/CatalogueSearch.php','    /** @return array<string, mixed> */\n    public function filterOptions()',r'''    public function describeFilters(array $filters): array
    {
        $options = $this->filterOptions();
        $labels = ['mrcs' => 'MRC', 'produits' => 'Produits', 'delivery_modes' => 'Livraison', 'type_productions' => 'Productions', 'type_clienteles' => 'Clientèles', 'volumes' => 'Volumes', 'types_transactions' => 'Transactions', 'specificites' => 'Spécificités', 'certifications' => 'Certifications', 'conditionnements' => 'Conditionnements', 'type_contenants' => 'Contenants', 'duree_vies' => 'Durées de vie', 'code_barres' => 'Codes-barres', 'quantite_minimale_commandes' => 'Quantités minimales', 'disponibilites' => 'Disponibilités', 'permis' => 'Permis', 'certifications_installations' => 'Installations'];
        $result = [];
        foreach ($this->normalizeFilters($filters) as $key => $ids) {
            $names = collect($options[$key] ?? [])->whereIn('id', $ids)->pluck('nom')->implode(', ');
            if ($names !== '') { $result[] = ($labels[$key] ?? 'Critère').' : '.$names; }
        }
        return $result;
    }

    /** @return array<string, mixed> */
    public function filterOptions()''')
write('resources/views/public/entreprise.blade.php',r'''<x-public.layout :title="$entreprise['nom']" :description="'Fiche de '.$entreprise['nom']">
<article class="hero"><h1>{{ $entreprise['nom'] }}</h1>
<p>{{ $entreprise['adresse'] }} @if($entreprise['ville'])<br>{{ $entreprise['ville']['nom'] }} @endif {{ $entreprise['code_postal'] }}</p>
@if($entreprise['nomContact'])<p>{{ $entreprise['nomContact'] }}</p>@endif
@foreach(['courriel'=>'Courriel','site_web'=>'Site web','facebook'=>'Facebook','instagram'=>'Instagram','twitter'=>'X / Twitter'] as $key=>$label)
@if($entreprise[$key])<p><a href="{{ $key === 'courriel' ? 'mailto:'.$entreprise[$key] : $entreprise[$key] }}" rel="noreferrer">{{ $label }} : {{ $entreprise[$key] }}</a></p>@endif
@endforeach
<p><a href="{{ route('entreprises.export.show', $entreprise['id']) }}">Télécharger la fiche en PDF</a> · <a href="{{ route('public.catalogue') }}">Retour au répertoire</a></p>
</article><section>
@foreach(['delivery_modes'=>'Modes de livraison','type_productions'=>'Types de production','type_clienteles'=>'Clientèles','certifications'=>'Certifications','permis'=>'Permis','certifications_installations'=>'Certifications des installations'] as $key=>$label)
@if(count($entreprise[$key]))<h2>{{ $label }}</h2><p>{{ collect($entreprise[$key])->pluck('nom')->implode(', ') }}</p>@endif
@endforeach
<h2>Produits</h2>@forelse($entreprise['produits'] as $product)<article class="card"><h3>{{ $product['produit']['nom'] ?? 'Produit' }}</h3>@include('public.product-details', ['product'=>$product])</article>@empty<p>Aucun produit détaillé.</p>@endforelse
</section></x-public.layout>''')
write('resources/views/public/product-details.blade.php',r'''<dl>
@foreach(['volume'=>'Volume','type_transaction'=>'Transaction'] as $key=>$label)
@if($product[$key] ?? null)<dt>{{ $label }}</dt><dd>{{ $product[$key]['nom'] }}</dd>@endif
@endforeach
@foreach(['specificites'=>'Spécificités','conservations'=>'Conservation','conditionnements'=>'Conditionnement','type_contenants'=>'Contenants','duree_vies'=>'Durée de vie','code_barres'=>'Codes-barres','quantite_minimale_commande'=>'Quantité minimale','disponibilites'=>'Disponibilités'] as $key=>$label)
@if(count($product[$key] ?? []))<dt>{{ $label }}</dt><dd>{{ collect($product[$key])->pluck('nom')->implode(', ') }}</dd>@endif
@endforeach</dl>''')
write('resources/views/pdf/entreprises.blade.php',r'''<!doctype html><html lang="fr"><head><meta charset="utf-8"><title>Répertoire RACA</title><style>
@page { margin: 32px 25px 45px; } body { font-family: DejaVu Sans, sans-serif; color:#193747;font-size:10px; } h1{color:#005577;font-size:20px}th{background:#005577;color:white;text-align:left}td,th{padding:6px;border:1px solid #dce5ea;vertical-align:top}table{width:100%;border-collapse:collapse}tr{page-break-inside:avoid}tr:nth-child(even){background:#f0f5f7}.meta{color:#526b78;margin:8px 0 16px}.logo{width:230px;height:50px}footer{position:fixed;bottom:-26px;color:#526b78;font-size:9px}.page:after{content:counter(page)}</style></head><body>
<img class="logo" src="{{ public_path('images/raca/logo.svg') }}" alt="RACA"><h1>Répertoire des entreprises</h1><div class="meta">Export du {{ now()->timezone('America/Toronto')->translatedFormat('j F Y') }} · {{ $entreprises->count() }} entreprise(s)<br>{{ $criteria }}</div>
<footer>Répertoire agroalimentaire Chaudière-Appalaches · Page <span class="page"></span></footer>
<table><thead><tr><th>Entreprise</th><th>Localité</th><th>Coordonnées</th><th>Produits</th></tr></thead><tbody>@foreach($entreprises as $e)<tr><td><strong>{{ $e['nom'] }}</strong><br>{{ collect($e['type_productions'])->pluck('nom')->implode(', ') }}</td><td>{{ $e['adresse'] }}<br>{{ $e['ville']['nom'] ?? '' }} {{ $e['ville']['mrc']['nom'] ?? '' }}</td><td>{{ $e['courriel'] }}<br>{{ $e['site_web'] }}</td><td>{{ collect($e['produits'])->pluck('produit.nom')->filter()->unique()->implode(', ') }}</td></tr>@endforeach</tbody></table></body></html>''')
write('resources/views/pdf/entreprise.blade.php',r'''<!doctype html><html lang="fr"><head><meta charset="utf-8"><title>{{ $entreprise['nom'] }} · RACA</title><style>@page{margin:30px 30px 45px}body{font-family:DejaVu Sans,sans-serif;color:#193747;font-size:11px;line-height:1.5}h1,h2{color:#005577}h1{border-bottom:3px solid #839900}h2{font-size:15px}.product{border:1px solid #dce5ea;padding:10px;margin:10px 0;page-break-inside:avoid}dt{font-weight:bold}dd{margin:0 0 4px}.logo{width:230px;height:50px}footer{position:fixed;bottom:-25px;font-size:9px;color:#526b78}.page:after{content:counter(page)}</style></head><body>
<img class="logo" src="{{ public_path('images/raca/logo.svg') }}" alt="RACA"><h1>{{ $entreprise['nom'] }}</h1><p>{{ $entreprise['adresse'] }}<br>{{ $entreprise['ville']['nom'] ?? '' }} {{ $entreprise['code_postal'] }} {{ $entreprise['ville']['mrc']['nom'] ?? '' }}</p><p>{{ $entreprise['courriel'] }}<br>{{ $entreprise['site_web'] }}</p>
@foreach(['delivery_modes'=>'Modes de livraison','type_productions'=>'Types de production','type_clienteles'=>'Clientèles','certifications'=>'Certifications','permis'=>'Permis','certifications_installations'=>'Certifications des installations'] as $key=>$label)@if(count($entreprise[$key]))<h2>{{ $label }}</h2><p>{{ collect($entreprise[$key])->pluck('nom')->implode(', ') }}</p>@endif @endforeach
<h2>Produits</h2>@forelse($entreprise['produits'] as $product)<div class="product"><strong>{{ $product['produit']['nom'] ?? 'Produit' }}</strong>@include('public.product-details', ['product'=>$product])</div>@empty<p>Aucun produit détaillé.</p>@endforelse
<footer>Répertoire agroalimentaire Chaudière-Appalaches · {{ now()->timezone('America/Toronto')->translatedFormat('j F Y') }} · Page <span class="page"></span></footer></body></html>''')
print('Phase 2 applied')
