<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Services\StockService;
use PDO;
use RuntimeException;

final class BonLivraisonRepository extends BaseRepository
{
    public function all(): array
    {
        if (!$this->tableExists('bons_livraison')) {
            return [];
        }

        return $this->db->query("\n            SELECT bl.*, c.raison_sociale AS client_nom\n            FROM bons_livraison bl\n            INNER JOIN clients c ON c.id = bl.client_id\n            WHERE bl.actif = 1\n            ORDER BY bl.date_creation DESC, bl.id DESC\n        ")->fetchAll(PDO::FETCH_ASSOC);
    }

    public function find(int $id): ?array
    {
        if (!$this->tableExists('bons_livraison')) {
            return null;
        }

        $stmt = $this->db->prepare("\n            SELECT bl.*, c.raison_sociale AS client_nom, c.adresse, c.ville, c.telephone, c.mobile, c.email\n            FROM bons_livraison bl\n            INNER JOIN clients c ON c.id = bl.client_id\n            WHERE bl.id = :id AND bl.actif = 1\n            LIMIT 1\n        ");
        $stmt->execute(['id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ?: null;
    }

    public function lines(int $bonLivraisonId): array
    {
        if (!$this->tableExists('bons_livraison_lignes')) {
            return [];
        }

        $stmt = $this->db->prepare("\n            SELECT *\n            FROM bons_livraison_lignes\n            WHERE bon_livraison_id = :id\n            ORDER BY ordre ASC, id ASC\n        ");
        $stmt->execute(['id' => $bonLivraisonId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function nextNumber(): string
    {
        $year = date('Y');
        $prefix = 'BL' . $year . '-';
        $stmt = $this->db->prepare('SELECT numero FROM bons_livraison WHERE numero LIKE :prefix ORDER BY id DESC LIMIT 1');
        $stmt->execute(['prefix' => $prefix . '%']);
        $last = (string) ($stmt->fetchColumn() ?: '');
        if ($last === '') {
            return $prefix . '000001';
        }
        $number = (int) substr($last, -6);
        return $prefix . str_pad((string) ($number + 1), 6, '0', STR_PAD_LEFT);
    }

    public function createFromDevis(int $devisId): int
    {
        if (!$this->tableExists('bons_livraison') || !$this->tableExists('bons_livraison_lignes')) {
            throw new RuntimeException('Les tables bons de livraison ne sont pas installées. Importez database/migrations/006_bons_livraison.sql.');
        }

        $devisModel = new Devis();
        $devis = $devisModel->find($devisId);
        if (!$devis) {
            throw new RuntimeException('Devis introuvable.');
        }

        $existing = $this->findByDevis($devisId);
        if ($existing) {
            return (int) $existing['id'];
        }

        $lines = $devisModel->lines($devisId);
        if (empty($lines)) {
            throw new RuntimeException('Impossible de transformer un devis sans ligne.');
        }

        $this->db->beginTransaction();
        try {
            $stmt = $this->db->prepare("\n                INSERT INTO bons_livraison (client_id, devis_id, numero, date_bl, statut, total_ht, total_tva, total_ttc, observations)\n                VALUES (:client_id, :devis_id, :numero, :date_bl, :statut, :total_ht, :total_tva, :total_ttc, :observations)\n            ");
            $stmt->execute([
                'client_id' => (int) $devis['client_id'],
                'devis_id' => $devisId,
                'numero' => $this->nextNumber(),
                'date_bl' => date('Y-m-d'),
                'statut' => 'préparé',
                'total_ht' => (float) $devis['total_ht'],
                'total_tva' => (float) $devis['total_tva'],
                'total_ttc' => (float) $devis['total_ttc'],
                'observations' => trim((string) ($devis['observations'] ?? '')),
            ]);
            $blId = (int) $this->db->lastInsertId();

            $lineStmt = $this->db->prepare("\n                INSERT INTO bons_livraison_lignes (\n                    bon_livraison_id, article_id, reference, designation, unite, quantite,\n                    prix_achat_ht, prix_vente_ht, remise, taux_tva, total_ht, total_tva, total_ttc, ordre\n                ) VALUES (\n                    :bon_livraison_id, :article_id, :reference, :designation, :unite, :quantite,\n                    :prix_achat_ht, :prix_vente_ht, :remise, :taux_tva, :total_ht, :total_tva, :total_ttc, :ordre\n                )\n            ");

            $stockService = new StockService();
            foreach ($lines as $index => $line) {
                $lineStmt->execute([
                    'bon_livraison_id' => $blId,
                    'article_id' => $line['article_id'] ?: null,
                    'reference' => $line['reference'] ?? '',
                    'designation' => $line['designation'] ?? '',
                    'unite' => $line['unite'] ?? 'unité',
                    'quantite' => (float) ($line['quantite'] ?? 0),
                    'prix_achat_ht' => (float) ($line['prix_achat_ht'] ?? 0),
                    'prix_vente_ht' => (float) ($line['prix_vente_ht'] ?? 0),
                    'remise' => (float) ($line['remise'] ?? 0),
                    'taux_tva' => (float) ($line['taux_tva'] ?? 20),
                    'total_ht' => (float) ($line['total_ht'] ?? 0),
                    'total_tva' => (float) ($line['total_tva'] ?? 0),
                    'total_ttc' => (float) ($line['total_ttc'] ?? 0),
                    'ordre' => $index + 1,
                ]);

                if (!empty($line['article_id']) && (float)($line['quantite'] ?? 0) > 0) {
                    $stockService->sortie((int)$line['article_id'], (float)$line['quantite'], 'BL', $blId, 'Sortie automatique BL');
                }
            }

            $this->db->prepare("UPDATE devis SET statut = 'transformé' WHERE id = :id")->execute(['id' => $devisId]);
            $this->db->commit();
            return $blId;
        } catch (\Throwable $exception) {
            $this->db->rollBack();
            throw $exception;
        }
    }

    public function markDelivered(int $id, string $receptionnaire = ''): void
    {
        $stmt = $this->db->prepare("\n            UPDATE bons_livraison\n            SET statut = 'livré', receptionnaire = :receptionnaire, date_livraison = NOW()\n            WHERE id = :id\n        ");
        $stmt->execute(['id' => $id, 'receptionnaire' => $receptionnaire]);
    }

    private function findByDevis(int $devisId): ?array
    {
        $stmt = $this->db->prepare('SELECT * FROM bons_livraison WHERE devis_id = :devis_id AND actif = 1 LIMIT 1');
        $stmt->execute(['devis_id' => $devisId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ?: null;
    }

    protected function tableExists(string $table): bool
    {
        $stmt = $this->db->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = :table');
        $stmt->execute(['table' => $table]);
        return (int) $stmt->fetchColumn() > 0;
    }
}
