<?php

declare(strict_types=1);

namespace App\Models;

use App\Core\Model;
use PDO;
use RuntimeException;

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

        return $this->db->query("\n            SELECT e.*, f.numero AS facture_numero, c.raison_sociale AS client_nom\n            FROM encaissements e\n            INNER JOIN factures f ON f.id = e.facture_id\n            INNER JOIN clients c ON c.id = e.client_id\n            WHERE e.actif = 1\n            ORDER BY e.date_encaissement DESC, e.id DESC\n        ")->fetchAll(PDO::FETCH_ASSOC);
    }

    public function forFacture(int $factureId): array
    {
        if (!$this->tableExists('encaissements')) {
            return [];
        }

        $stmt = $this->db->prepare("\n            SELECT e.*, f.numero AS facture_numero, c.raison_sociale AS client_nom\n            FROM encaissements e\n            INNER JOIN factures f ON f.id = e.facture_id\n            INNER JOIN clients c ON c.id = e.client_id\n            WHERE e.facture_id = :facture_id AND e.actif = 1\n            ORDER BY e.date_encaissement DESC, e.id DESC\n        ");
        $stmt->execute(['facture_id' => $factureId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function store(array $data): int
    {
        if (!$this->tableExists('encaissements')) {
            throw new RuntimeException('La table encaissements n’est pas installée. Importez database/migrations/008_encaissements.sql.');
        }

        $factureId = (int)($data['facture_id'] ?? 0);
        $factureModel = new Facture();
        $facture = $factureModel->find($factureId);
        if (!$facture) {
            throw new RuntimeException('Facture introuvable.');
        }

        $montant = round((float)($data['montant'] ?? 0), 2);
        if ($montant <= 0) {
            throw new RuntimeException('Le montant encaissé doit être supérieur à zéro.');
        }

        $resteDu = round((float)($facture['reste_du'] ?? $facture['total_ttc'] ?? 0), 2);
        if ($montant > $resteDu) {
            throw new RuntimeException('Le montant encaissé ne peut pas être supérieur au reste dû.');
        }

        $date = trim((string)($data['date_encaissement'] ?? '')) ?: date('Y-m-d');
        $mode = trim((string)($data['mode_reglement'] ?? 'Espèces')) ?: 'Espèces';

        $this->db->beginTransaction();
        try {
            $stmt = $this->db->prepare("\n                INSERT INTO encaissements (\n                    client_id, facture_id, date_encaissement, mode_reglement, reference_reglement, montant, observations\n                ) VALUES (\n                    :client_id, :facture_id, :date_encaissement, :mode_reglement, :reference_reglement, :montant, :observations\n                )\n            ");
            $stmt->execute([
                'client_id' => (int)$facture['client_id'],
                'facture_id' => $factureId,
                'date_encaissement' => $date,
                'mode_reglement' => $mode,
                'reference_reglement' => trim((string)($data['reference_reglement'] ?? '')) ?: null,
                'montant' => $montant,
                'observations' => trim((string)($data['observations'] ?? '')) ?: null,
            ]);
            $id = (int)$this->db->lastInsertId();

            $totalRegle = $this->totalPaidForFacture($factureId);
            $factureModel->updatePaymentStatus($factureId, $totalRegle);

            $this->db->commit();
            return $id;
        } catch (\Throwable $exception) {
            $this->db->rollBack();
            throw $exception;
        }
    }

    public function totalPaidForFacture(int $factureId): float
    {
        if (!$this->tableExists('encaissements')) {
            return 0.0;
        }

        $stmt = $this->db->prepare("\n            SELECT COALESCE(SUM(montant), 0)\n            FROM encaissements\n            WHERE facture_id = :facture_id AND actif = 1\n        ");
        $stmt->execute(['facture_id' => $factureId]);
        return round((float)$stmt->fetchColumn(), 2);
    }

    public function unpaidInvoices(): array
    {
        if (!$this->tableExists('factures')) {
            return [];
        }

        return $this->db->query("\n            SELECT f.*, c.raison_sociale AS client_nom\n            FROM factures f\n            INNER JOIN clients c ON c.id = f.client_id\n            WHERE f.actif = 1 AND COALESCE(f.reste_du, f.total_ttc) > 0\n            ORDER BY f.date_echeance ASC, f.date_facture DESC\n        ")->fetchAll(PDO::FETCH_ASSOC);
    }

    private 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;
    }
}
