<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;
use App\Services\StripeService;
use Stripe\Webhook;
use Stripe\Exception\SignatureVerificationException;
use AppConfig;
use Exception;
use PDO;

/**
 * Controller pour gérer les webhooks Stripe
 * Point d'entrée pour tous les événements Stripe
 */
class StripeWebhookController extends Controller {
    private StripeService $stripeService;
    private AppConfig $config;
    
    public function __construct() {
        parent::__construct();
        $this->stripeService = StripeService::getInstance();
        $this->config = AppConfig::getInstance();
    }
    
    /**
     * POST /api/stripe/webhook
     * Point d'entrée principal pour les webhooks Stripe
     */
    public function handleWebhook(): void {
        try {
            // Récupérer le payload et la signature
            $payload = file_get_contents('php://input');
            $sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'] ?? '';
            
            if (empty($sigHeader)) {
                http_response_code(400);
                echo 'Missing Stripe signature';
                exit;
            }
            
            // Récupérer le secret webhook selon le mode
            $stripeConfig = $this->config->getStripeConfig();
            $webhookSecret = $this->stripeService->isTestMode()
                ? $stripeConfig['webhook_secret_test']
                : $stripeConfig['webhook_secret_live'];
            
            if (empty($webhookSecret) || strpos($webhookSecret, 'XXXX') !== false) {
                http_response_code(500);
                echo 'Webhook secret not configured';
                exit;
            }
            
            // Vérifier la signature
            try {
                $event = Webhook::constructEvent($payload, $sigHeader, $webhookSecret);
            } catch (SignatureVerificationException $e) {
                http_response_code(400);
                echo 'Invalid signature';
                exit;
            }
            
            // Vérifier si l'événement a déjà été traité (idempotence)
            $stmt = $this->db->prepare(
                "SELECT id FROM stripe_webhooks WHERE stripe_event_id = :event_id"
            );
            $stmt->execute(['event_id' => $event->id]);
            
            if ($stmt->fetch()) {
                // Événement déjà traité
                http_response_code(200);
                echo 'Event already processed';
                exit;
            }
            
            // Enregistrer l'événement
            $stmt = $this->db->prepare(
                "INSERT INTO stripe_webhooks (stripe_event_id, event_type, livemode, payload, created_at)
                 VALUES (:event_id, :event_type, :livemode, :payload, NOW())"
            );
            $stmt->execute([
                'event_id' => $event->id,
                'event_type' => $event->type,
                'livemode' => $event->livemode ? 1 : 0,
                'payload' => $payload
            ]);
            $webhookId = $this->db->lastInsertId();
            
            // Traiter l'événement selon son type
            try {
                switch ($event->type) {
                    case 'account.updated':
                        $this->handleAccountUpdated($event->data->object);
                        break;

                    case 'account.application.authorized':
                        $this->handleAccountAuthorized($event->data->object);
                        break;

                    case 'payment_intent.succeeded':
                        $this->handlePaymentIntentSucceeded($event->data->object);
                        break;

                    case 'payment_intent.payment_failed':
                        $this->handlePaymentIntentFailed($event->data->object);
                        break;

                    case 'checkout.session.completed':
                        $this->handleCheckoutSessionCompleted($event->data->object);
                        break;

                    case 'charge.dispute.created':
                        $this->handleChargeDisputeCreated($event->data->object);
                        break;

                    case 'terminal.reader.action_succeeded':
                        $this->handleTerminalReaderActionSucceeded($event->data->object);
                        break;

                    case 'terminal.reader.action_failed':
                        $this->handleTerminalReaderActionFailed($event->data->object);
                        break;

                    default:
                        // Événement non géré mais valide
                        error_log("Unhandled Stripe event type: {$event->type}");
                }
                
                // Marquer comme traité
                $stmt = $this->db->prepare(
                    "UPDATE stripe_webhooks 
                     SET processed = 1, processed_at = NOW() 
                     WHERE id = :id"
                );
                $stmt->execute(['id' => $webhookId]);
                
            } catch (Exception $e) {
                // Enregistrer l'erreur
                $stmt = $this->db->prepare(
                    "UPDATE stripe_webhooks 
                     SET error_message = :error 
                     WHERE id = :id"
                );
                $stmt->execute([
                    'error' => $e->getMessage(),
                    'id' => $webhookId
                ]);
                
                throw $e;
            }
            
            // Réponse de succès
            http_response_code(200);
            echo 'Webhook handled';
            
        } catch (Exception $e) {
            error_log('Stripe webhook error: ' . $e->getMessage());
            http_response_code(500);
            echo 'Webhook handler error';
        }
    }
    
    /**
     * Gérer la mise à jour d'un compte Connect
     */
    private function handleAccountUpdated($account): void {
        $stmt = $this->db->prepare(
            "UPDATE stripe_accounts 
             SET charges_enabled = :charges_enabled,
                 payouts_enabled = :payouts_enabled,
                 onboarding_completed = :onboarding_completed,
                 updated_at = NOW()
             WHERE stripe_account_id = :account_id"
        );
        
        $stmt->execute([
            'charges_enabled' => $account->charges_enabled ? 1 : 0,
            'payouts_enabled' => $account->payouts_enabled ? 1 : 0,
            'onboarding_completed' => ($account->charges_enabled && $account->payouts_enabled) ? 1 : 0,
            'account_id' => $account->id
        ]);
        
        // Log pour suivi
        error_log("Account updated: {$account->id}, charges: {$account->charges_enabled}, payouts: {$account->payouts_enabled}");
    }
    
    /**
     * Gérer l'autorisation d'un compte Connect
     */
    private function handleAccountAuthorized($account): void {
        // Similaire à account.updated mais spécifique à l'autorisation
        $this->handleAccountUpdated($account);
        
        // Potentiellement envoyer un email de confirmation
        // TODO: Implémenter notification email
    }
    
    /**
     * Gérer un paiement réussi
     */
    private function handlePaymentIntentSucceeded($paymentIntent): void {
        // Pour Tap to Pay, le paiement est instantané et déjà enregistré dans ope_pass
        // Ce webhook sert principalement pour les Payment Links (QR Code) asynchrones

        // Vérifier si le passage existe et mettre à jour si nécessaire
        $stmt = $this->db->prepare("
            SELECT id FROM ope_pass
            WHERE stripe_payment_id = :pi_id
        ");
        $stmt->execute(['pi_id' => $paymentIntent->id]);
        $passage = $stmt->fetch();

        if ($passage) {
            error_log("Payment succeeded: {$paymentIntent->id} for {$paymentIntent->amount} {$paymentIntent->currency} - Passage {$passage['id']}");

            // TODO: Envoyer un reçu par email
            // TODO: Mettre à jour les statistiques en temps réel
        } else {
            error_log("Payment succeeded: {$paymentIntent->id} but no passage found in ope_pass");
        }
    }
    
    /**
     * Gérer un paiement échoué
     */
    private function handlePaymentIntentFailed($paymentIntent): void {
        // Vérifier si le passage existe
        $stmt = $this->db->prepare("
            SELECT id FROM ope_pass
            WHERE stripe_payment_id = :pi_id
        ");
        $stmt->execute(['pi_id' => $paymentIntent->id]);
        $passage = $stmt->fetch();

        if ($passage) {
            // Optionnel : Marquer le passage comme échec ou supprimer le stripe_payment_id
            // Pour l'instant on log seulement
            error_log("Payment failed: {$paymentIntent->id} for passage {$passage['id']}, reason: " . json_encode($paymentIntent->last_payment_error));
        } else {
            error_log("Payment failed: {$paymentIntent->id}, reason: " . json_encode($paymentIntent->last_payment_error));
        }
    }

    /**
     * Gérer la complétion d'une session de paiement (Payment Link / Checkout)
     */
    private function handleCheckoutSessionCompleted($session): void {
        $metadata = $session->metadata;

        // Logger l'événement
        error_log("Checkout session completed: {$session->id}, payment_intent: {$session->payment_intent}");

        // Vérifier si un passage_id est présent dans les metadata
        if (isset($metadata->passage_id) && !empty($metadata->passage_id)) {
            $passageId = (int)$metadata->passage_id;

            // Mettre à jour le passage avec le stripe_payment_id
            $stmt = $this->db->prepare("
                UPDATE ope_pass
                SET stripe_payment_id = :payment_intent_id,
                    updated_at = NOW()
                WHERE id = :passage_id
            ");
            $stmt->execute([
                'payment_intent_id' => $session->payment_intent,
                'passage_id' => $passageId
            ]);

            // Vérifier si la mise à jour a réussi
            if ($stmt->rowCount() > 0) {
                error_log("Passage {$passageId} updated with payment_intent {$session->payment_intent}");

                // TODO: Envoyer un email de confirmation avec le reçu fiscal
                // TODO: Mettre à jour les statistiques en temps réel
            } else {
                error_log("Warning: Passage {$passageId} not found or already updated");
            }
        } else {
            error_log("Warning: checkout.session.completed without passage_id in metadata");
        }

        // Enregistrer l'historique de la session dans stripe_payment_history si nécessaire
        if (isset($metadata->passage_id)) {
            $stmt = $this->db->prepare("
                SELECT id FROM ope_pass WHERE id = :passage_id
            ");
            $stmt->execute(['passage_id' => $metadata->passage_id]);
            $passage = $stmt->fetch();

            if ($passage) {
                // Log dans l'historique
                error_log("Checkout session completed for passage {$metadata->passage_id}: amount={$session->amount_total}, currency={$session->currency}");
            }
        }
    }

    /**
     * Gérer un litige (chargeback)
     */
    private function handleChargeDisputeCreated($dispute): void {
        // Trouver le paiement concerné
        $chargeId = $dispute->charge;
        
        // TODO: Implémenter la gestion des litiges
        // - Notifier l'admin
        // - Bloquer les fonds si nécessaire
        // - Créer une tâche de suivi
        
        error_log("Dispute created: {$dispute->id} for charge {$chargeId}, amount: {$dispute->amount}");
        
        // Envoyer une alerte urgente
        // TODO: Implémenter système d'alertes
    }
    
    /**
     * Gérer une action réussie sur un Terminal reader
     */
    private function handleTerminalReaderActionSucceeded($reader): void {
        // Note: Pour Tap to Pay, il n'y a pas de readers physiques
        // Ce webhook concerne uniquement les terminaux externes (non utilisés pour l'instant)
        error_log("Terminal reader action succeeded: {$reader->id}");
    }
    
    /**
     * Gérer une action échouée sur un Terminal reader
     */
    private function handleTerminalReaderActionFailed($reader): void {
        // Note: Pour Tap to Pay, il n'y a pas de readers physiques
        // Ce webhook concerne uniquement les terminaux externes (non utilisés pour l'instant)
        error_log("Terminal reader action failed: {$reader->id}");
    }
}