<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Models\Lot;
use App\Models\Commande;
use App\Services\PrestashopService;
use App\Services\MailService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Illuminate\Database\Capsule\Manager as DB;

class FabController
{
    use ControllerTrait;

    public function __construct(
        private PrestashopService $ps,
        private MailService $mail
    ) {}

    private function requireFab(Request $request, Response $response): ?Response
    {
        return $this->requireRole($request, $response, ['operateur_fab', 'admin_fab', 'super_admin']);
    }

    private function getAtelierId(Request $request): ?int
    {
        $payload = $this->getAuthPayload($request);
        return $payload ? ($payload->atelier_id ?? null) : null;
    }

    // ── Commandes fabricant ──

    public function commandes(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $params    = $request->getQueryParams();
        [$page, $limit, $offset] = $this->paginate($request);

        $query = DB::table('commandes')
            ->join('produits', 'produits.id', '=', 'commandes.produit_id')
            ->leftJoin('lots', function($j) {
                $j->join('commande_lot', 'commande_lot.lot_id', '=', 'lots.id')
                  ->on('commande_lot.commande_id', '=', 'commandes.id');
            })
            ->where('commandes.atelier_id', $atelierId)
            ->select(['commandes.*', 'produits.nom as produit_nom', 'lots.id as lot_id', 'lots.statut as lot_statut'])
            ->orderBy('commandes.created_at', 'desc');

        if (!empty($params['statut'])) $query->where('commandes.statut', $params['statut']);

        $total    = $query->count();
        $commandes = $query->skip($offset)->take($limit)->get();

        return $this->successList($response, $commandes->toArray(), ['page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    public function commandeDetail(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $commande  = Commande::with(['produit', 'user', 'lots'])
            ->where('id', (int)$args['id'])
            ->where('atelier_id', $atelierId)
            ->first();

        if (!$commande) return $this->error($response, 'NOT_FOUND', 'Commande introuvable', 404);

        return $this->success($response, $commande->toArray());
    }

    // ── Lots ──

    public function lots(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $params    = $request->getQueryParams();
        [$page, $limit, $offset] = $this->paginate($request);

        $query = DB::table('lots')
            ->join('produits', 'produits.id', '=', 'lots.produit_id')
            ->where('lots.atelier_id', $atelierId)
            ->select(['lots.*', 'produits.nom as produit_nom'])
            ->orderBy('lots.created_at', 'desc');

        if (!empty($params['statut'])) $query->where('lots.statut', $params['statut']);

        $total = $query->count();
        $lots  = $query->skip($offset)->take($limit)->get();

        return $this->successList($response, $lots->toArray(), ['page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    public function creerLot(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $body      = (array)$request->getParsedBody();
        $produitId = (int)($body['produit_id'] ?? 0);

        // Trouver commandes validées non encore en lot pour ce produit/atelier
        $commandes = DB::table('commandes')
            ->where('produit_id', $produitId)
            ->where('atelier_id', $atelierId)
            ->where('statut', 'validee')
            ->whereNotExists(function($q) {
                $q->select(DB::raw(1))
                  ->from('commande_lot')
                  ->whereColumn('commande_lot.commande_id', 'commandes.id');
            })
            ->get();

        if ($commandes->isEmpty()) {
            return $this->error($response, 'VALIDATION_ERROR', 'Aucune commande validée disponible pour ce produit', 422);
        }

        $lotId = DB::table('lots')->insertGetId([
            'produit_id'  => $produitId,
            'atelier_id'  => $atelierId,
            'statut'      => 'en_attente_generation',
            'nb_commandes'=> $commandes->count(),
            'notes'       => $body['notes'] ?? null,
            'created_at'  => date('Y-m-d H:i:s'),
        ]);

        // Lier commandes au lot
        foreach ($commandes as $c) {
            DB::table('commande_lot')->insert(['commande_id' => $c->id, 'lot_id' => $lotId]);
            DB::table('commandes')->where('id', $c->id)->update(['statut' => 'en_lot']);
        }

        return $this->success($response, [
            'id'           => $lotId,
            'produit_id'   => $produitId,
            'nb_commandes' => $commandes->count(),
            'statut'       => 'en_attente_generation',
        ], 201);
    }

    public function lotDetail(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $lot       = Lot::with(['produit', 'commandes.user'])->where('id', (int)$args['id'])->where('atelier_id', $atelierId)->first();
        if (!$lot)  return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        return $this->success($response, $lot->toArray());
    }

    public function updateStatutLot(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $body      = (array)$request->getParsedBody();
        $statut    = $body['statut'] ?? '';
        $lot       = Lot::where('id', (int)$args['id'])->where('atelier_id', $atelierId)->first();

        if (!$lot) return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        $lot->update(['statut' => $statut]);

        // Si passage en_gravure → push PS statut 24 sur toutes les commandes du lot
        if ($statut === 'en_gravure') {
            $commandes = DB::table('commande_lot')
                ->join('commandes', 'commandes.id', '=', 'commande_lot.commande_id')
                ->where('commande_lot.lot_id', $lot->id)
                ->pluck('commandes.presta_order_id');

            foreach ($commandes as $orderId) {
                try {
                    $this->ps->pushStatutCommande($orderId, (int)($_ENV['PRESTASHOP_STATUT_GRAVAGE_ID'] ?? 24));
                } catch (\Exception $e) {
                    error_log('[FabController] Push PS lot échoué: ' . $e->getMessage());
                }
            }

            DB::table('commandes')
                ->whereIn('id', DB::table('commande_lot')->where('lot_id', $lot->id)->pluck('commande_id'))
                ->update(['statut' => 'en_gravure']);
        }

        return $this->success($response, ['id' => $lot->id, 'statut' => $statut]);
    }

    public function exportCsv(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $lot       = Lot::where('id', (int)$args['id'])->where('atelier_id', $atelierId)->first();
        if (!$lot)  return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        $commandes = DB::table('commande_lot')
            ->join('commandes', 'commandes.id', '=', 'commande_lot.commande_id')
            ->leftJoin('users', 'users.id', '=', 'commandes.user_id')
            ->where('commande_lot.lot_id', $lot->id)
            ->select(['commandes.presta_order_ref', 'users.nom', 'users.prenom', 'commandes.texte_gravure'])
            ->get();

        $csv  = "reference,nom,prenom,texte_gravure\n";
        foreach ($commandes as $c) {
            $csv .= implode(',', [
                '"' . $c->presta_order_ref . '"',
                '"' . ($c->nom ?? '') . '"',
                '"' . ($c->prenom ?? '') . '"',
                '"' . str_replace('"', '""', $c->texte_gravure ?? '') . '"',
            ]) . "\n";
        }

        $response->getBody()->write($csv);
        return $response
            ->withHeader('Content-Type', 'text/csv; charset=UTF-8')
            ->withHeader('Content-Disposition', 'attachment; filename="lot_' . $lot->id . '.csv"');
    }

    public function dxfArchive(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        // Retourner la liste des DXF — sur OVH mutualisé, les fichiers sont sur le PC fabricant
        // L'agent les génère localement. L'API retourne juste les métadonnées.
        $lotId = (int)$args['id'];
        $files = DB::table('fichiers_gravure')->where('lot_id', $lotId)->get();

        return $this->success($response, ['lot_id' => $lotId, 'fichiers' => $files->toArray()]);
    }

    // ── Gravure ──

    public function prendreEnCharge(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $body      = (array)$request->getParsedBody();
        $lotId     = (int)($body['lot_id'] ?? 0);

        $lot = Lot::where('id', $lotId)->where('atelier_id', $atelierId)->first();
        if (!$lot) return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        $lot->update(['statut' => 'en_gravure']);

        DB::table('commandes')
            ->whereIn('id', DB::table('commande_lot')->where('lot_id', $lotId)->pluck('commande_id'))
            ->update(['statut' => 'en_gravure']);

        // Push PS
        $commandeIds = DB::table('commande_lot')->where('lot_id', $lotId)->pluck('commande_id');
        $prestaIds   = DB::table('commandes')->whereIn('id', $commandeIds)->pluck('presta_order_id');
        foreach ($prestaIds as $orderId) {
            try { $this->ps->pushStatutCommande($orderId, (int)($_ENV['PRESTASHOP_STATUT_GRAVAGE_ID'] ?? 24)); }
            catch (\Exception $e) {}
        }

        return $this->success($response, ['lot_id' => $lotId, 'statut' => 'en_gravure']);
    }

    public function terminerGravure(Request $request, Response $response, array $args): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $lot       = Lot::where('id', (int)$args['id'])->where('atelier_id', $atelierId)->first();
        if (!$lot)  return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        $lot->update(['statut' => 'grave']);

        DB::table('commandes')
            ->whereIn('id', DB::table('commande_lot')->where('lot_id', $lot->id)->pluck('commande_id'))
            ->update(['statut' => 'grave']);

        return $this->success($response, ['lot_id' => $lot->id, 'statut' => 'grave']);
    }

    // ── Expédition ──

    public function expedition(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId   = $this->getAtelierId($request);
        $body        = (array)$request->getParsedBody();
        $commandeId  = (int)($body['commande_id'] ?? 0);
        $transporteur= $body['transporteur'] ?? '';
        $numeroSuivi = $body['numero_suivi'] ?? '';
        $dateExp     = $body['date_expedition'] ?? date('Y-m-d H:i:s');

        $commande = Commande::where('id', $commandeId)->where('atelier_id', $atelierId)->first();
        if (!$commande) return $this->error($response, 'NOT_FOUND', 'Commande introuvable', 404);

        $commande->update([
            'statut'          => 'expedie',
            'transporteur'    => $transporteur,
            'numero_suivi'    => $numeroSuivi,
            'date_expedition' => $dateExp,
        ]);

        // Push PS statut Expédié
        try {
            $this->ps->pushStatutCommande($commande->presta_order_id, (int)($_ENV['STATUT_EXPEDITION_ID'] ?? 4));
        } catch (\Exception $e) {
            error_log('[FabController] Push PS expédition échoué: ' . $e->getMessage());
        }

        // Email sportif
        $user = DB::table('users')->find($commande->user_id);
        if ($user && $user->email) {
            $this->mail->expeditionConfirmee(
                $user->email,
                $user->prenom ?? 'Sportif',
                $commande->presta_order_ref ?? 'MKO-' . $commande->id,
                $transporteur,
                $numeroSuivi
            );
        }

        return $this->success($response, [
            'commande_id'  => $commandeId,
            'statut'       => 'expedie',
            'numero_suivi' => $numeroSuivi,
        ]);
    }

    public function expeditionBatch(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId   = $this->getAtelierId($request);
        $body        = (array)$request->getParsedBody();
        $lotId       = (int)($body['lot_id'] ?? 0);
        $transporteur= $body['transporteur'] ?? '';
        $dateExp     = $body['date_expedition'] ?? date('Y-m-d H:i:s');

        $lot = Lot::where('id', $lotId)->where('atelier_id', $atelierId)->first();
        if (!$lot) return $this->error($response, 'NOT_FOUND', 'Lot introuvable', 404);

        $commandeIds = DB::table('commande_lot')->where('lot_id', $lotId)->pluck('commande_id');
        $nb          = 0;

        foreach ($commandeIds as $cId) {
            $commande = Commande::find($cId);
            if (!$commande) continue;

            $commande->update([
                'statut'          => 'expedie',
                'transporteur'    => $transporteur,
                'date_expedition' => $dateExp,
            ]);

            try { $this->ps->pushStatutCommande($commande->presta_order_id, (int)($_ENV['STATUT_EXPEDITION_ID'] ?? 4)); }
            catch (\Exception $e) {}

            $user = DB::table('users')->find($commande->user_id);
            if ($user && $user->email) {
                $this->mail->expeditionConfirmee($user->email, $user->prenom ?? 'Sportif',
                    $commande->presta_order_ref ?? 'MKO-' . $commande->id, $transporteur, '');
            }
            $nb++;
        }

        $lot->update(['statut' => 'expedie']);

        return $this->success($response, ['lot_id' => $lotId, 'nb_expeditions' => $nb]);
    }

    // ── SAV ──

    public function savList(Request $request, Response $response): Response
    {
        $guard  = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $params = $request->getQueryParams();
        [$page, $limit, $offset] = $this->paginate($request);

        $query = DB::table('sav_tickets')
            ->join('commandes', 'commandes.id', '=', 'sav_tickets.commande_id')
            ->select(['sav_tickets.*', 'commandes.presta_order_ref'])
            ->orderBy('sav_tickets.created_at', 'desc');

        if (!empty($params['statut'])) $query->where('sav_tickets.statut', $params['statut']);

        $total   = $query->count();
        $tickets = $query->skip($offset)->take($limit)->get();

        return $this->successList($response, $tickets->toArray(), ['page' => $page, 'limit' => $limit, 'total' => $total]);
    }

    public function savCreate(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $body       = (array)$request->getParsedBody();
        $commandeId = (int)($body['commande_id'] ?? 0);

        $id = DB::table('sav_tickets')->insertGetId([
            'commande_id' => $commandeId,
            'type'        => $body['type'] ?? 'autre',
            'statut'      => 'ouvert',
            'description' => $body['description'] ?? '',
            'created_at'  => date('Y-m-d H:i:s'),
        ]);

        // Notifier admin
        DB::table('notifications')->insert([
            'user_id'   => 1, // super_admin
            'type'      => 'sav_ouvert',
            'titre'     => 'Ticket SAV ouvert — Commande #' . $commandeId,
            'message'   => $body['description'] ?? '',
            'lien'      => 'https://fab.mykollector.com/admin/sav-admin.html',
            'lu'        => 0,
            'created_at'=> date('Y-m-d H:i:s'),
        ]);

        return $this->success($response, ['id' => $id, 'statut' => 'ouvert'], 201);
    }

    public function savUpdate(Request $request, Response $response, array $args): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $body = (array)$request->getParsedBody();
        DB::table('sav_tickets')->where('id', (int)$args['id'])->update([
            'statut'     => $body['statut'] ?? 'en_cours',
            'resolution' => $body['resolution'] ?? null,
            'updated_at' => date('Y-m-d H:i:s'),
        ]);

        return $this->success($response, ['id' => (int)$args['id']]);
    }

    public function savExpress(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $body       = (array)$request->getParsedBody();
        $commandeId = (int)($body['commande_id'] ?? 0);

        $savId = DB::table('sav_tickets')->insertGetId([
            'commande_id' => $commandeId,
            'type'        => $body['motif'] ?? 'defaut',
            'statut'      => 'ouvert',
            'description' => $body['description'] ?? '',
            'created_at'  => date('Y-m-d H:i:s'),
        ]);

        // Notifier admin + sportif
        $commande = Commande::find($commandeId);
        if ($commande) {
            $user = DB::table('users')->find($commande->user_id);
            if ($user && $user->email) {
                $this->mail->rejetCommande($user->email, $user->prenom ?? 'Sportif',
                    $commande->presta_order_ref ?? 'MKO-' . $commandeId, $body['description'] ?? '');
            }
        }

        return $this->success($response, ['sav_id' => $savId, 'notification_envoyee' => true], 201);
    }

    public function signalerDefaut(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $body = (array)$request->getParsedBody();
        $id   = DB::table('defauts')->insertGetId([
            'commande_id' => (int)($body['commande_id'] ?? 0),
            'type'        => $body['type'] ?? '',
            'description' => $body['description'] ?? '',
            'created_at'  => date('Y-m-d H:i:s'),
        ]);

        return $this->success($response, ['id' => $id], 201);
    }

    // ── Notifications ──

    public function notifications(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $user    = $this->getAuthUser($request);
        $params  = $request->getQueryParams();

        $notifs = DB::table('notifications')
            ->where('user_id', $user->id)
            ->orderBy('created_at', 'desc')
            ->limit(50)
            ->get();

        $nbNonLus = DB::table('notifications')
            ->where('user_id', $user->id)
            ->where('lu', 0)
            ->count();

        return $this->success($response, [
            'notifications' => $notifs->toArray(),
            'nb_non_lus'    => $nbNonLus,
        ]);
    }

    // ── Stocks ──

    public function stocks(Request $request, Response $response): Response
    {
        $guard     = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $atelierId = $this->getAtelierId($request);
        $params    = $request->getQueryParams();

        $query = DB::table('stocks')->where('atelier_id', $atelierId);
        if (!empty($params['atelier_id'])) {
            $query->where('atelier_id', (int)$params['atelier_id']);
        }

        return $this->success($response, $query->get()->toArray());
    }

    public function stockMouvement(Request $request, Response $response): Response
    {
        $guard = $this->requireFab($request, $response);
        if ($guard) return $guard;

        $body      = (array)$request->getParsedBody();
        $atelierId = $this->getAtelierId($request);
        $stockId   = (int)($body['stock_id'] ?? 0);
        $quantite  = (int)($body['quantite'] ?? 0);
        $type      = $body['type'] ?? 'entree'; // entree ou sortie

        $stock = DB::table('stocks')->where('id', $stockId)->where('atelier_id', $atelierId)->first();
        if (!$stock) return $this->error($response, 'NOT_FOUND', 'Stock introuvable', 404);

        $newQty = $type === 'entree'
            ? $stock->quantite + $quantite
            : max(0, $stock->quantite - $quantite);

        DB::table('stocks')->where('id', $stockId)->update([
            'quantite'  => $newQty,
            'updated_at'=> date('Y-m-d H:i:s'),
        ]);

        return $this->success($response, ['stock_id' => $stockId, 'quantite' => $newQty]);
    }
}
