<?php
namespace Bot\Services;
use Bot\Database;
use PDO;

class WithdrawalService
{
    private PDO $pdo;

    public function __construct()
    {
        $this->pdo = Database::connection();
    }

    public function create(
        int $userId,
        int $walletId,
        string $currency,
        string $network,
        string $amount,
        string $toAddress,
        float $fee = 0.0
    ): int {
        $stmt = $this->pdo->prepare(
            "INSERT INTO withdrawals (user_id, wallet_id, currency, network, amount, fee, to_address, status, review_status, created_at)
             VALUES (:user, :wallet, :currency, :network, :amount, :fee, :address, 'pending', 'pending', NOW())"
        );
        $stmt->execute([
            ':user'    => $userId,
            ':wallet'  => $walletId,
            ':currency'=> strtoupper($currency),
            ':network' => strtoupper($network),
            ':amount'  => $amount,
            ':fee'     => $fee,
            ':address' => $toAddress,
        ]);
        return (int) $this->pdo->lastInsertId();
    }

    public function getById(int $id): ?array
    {
        $stmt = $this->pdo->prepare('SELECT * FROM withdrawals WHERE id = ?');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ?: null;
    }

    /**
     * @return array<int,array<string,mixed>>
     */
    public function listByReviewStatus(string $status = 'pending', int $limit = 20): array
    {
        $stmt = $this->pdo->prepare(
            'SELECT * FROM withdrawals WHERE review_status = ? ORDER BY id ASC LIMIT ?'
        );
        $stmt->bindValue(1, $status, PDO::PARAM_STR);
        $stmt->bindValue(2, $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function approve(int $id, int $adminId): ?array
    {
        $row = $this->getById($id);
        if (!$row || !in_array($row['review_status'], ['pending', 'held'], true)) {
            return null;
        }
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals
             SET review_status = 'approved', review_reason = NULL, reviewed_by = :admin, reviewed_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':admin' => $adminId,
            ':id'    => $id,
        ]);
        return $this->getById($id);
    }

    public function reject(int $id, int $adminId, string $reason): ?array
    {
        $row = $this->getById($id);
        if (!$row || $row['review_status'] === 'rejected') {
            return null;
        }
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals
             SET review_status = 'rejected', review_reason = :reason, reviewed_by = :admin, reviewed_at = NOW(), status = 'cancelled', updated_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':reason' => $reason,
            ':admin'  => $adminId,
            ':id'     => $id,
        ]);
        return $this->getById($id);
    }

    public function hold(int $id, int $adminId, string $reason): ?array
    {
        $row = $this->getById($id);
        if (!$row || $row['review_status'] === 'rejected') {
            return null;
        }
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals
             SET review_status = 'held', review_reason = :reason, reviewed_by = :admin, reviewed_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':reason' => $reason,
            ':admin'  => $adminId,
            ':id'     => $id,
        ]);
        return $this->getById($id);
    }

    public function releaseHold(int $id, int $adminId): ?array
    {
        $row = $this->getById($id);
        if (!$row || $row['review_status'] !== 'held') {
            return null;
        }
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals
             SET review_status = 'pending', review_reason = NULL, reviewed_by = :admin, reviewed_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':admin' => $adminId,
            ':id'    => $id,
        ]);
        return $this->getById($id);
    }

    public function markProcessing(int $withdrawalId): bool
    {
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals SET status = 'processing', updated_at = NOW()
             WHERE id = :id AND status = 'pending' AND review_status = 'approved'"
        );
        $stmt->execute([':id' => $withdrawalId]);
        return $stmt->rowCount() === 1;
    }

    public function markBroadcasted(int $withdrawalId, string $txid): void
    {
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals SET status = 'sent', txid = :txid, updated_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':id'   => $withdrawalId,
            ':txid' => $txid,
        ]);
    }

    public function markFailed(int $withdrawalId, string $reason): void
    {
        $stmt = $this->pdo->prepare(
            "UPDATE withdrawals SET status = 'failed', notes = :reason, updated_at = NOW()
             WHERE id = :id"
        );
        $stmt->execute([
            ':id'     => $withdrawalId,
            ':reason' => $reason,
        ]);
    }

    /**
     * @return array<int,array<string,mixed>>
     */
    public function getPending(int $limit = 5): array
    {
        $stmt = $this->pdo->prepare(
            "SELECT w.*, wa.address AS wallet_address, wa.private_key_encrypted
             FROM withdrawals w
             JOIN wallets wa ON wa.id = w.wallet_id
             WHERE w.status = 'pending' AND w.review_status = 'approved'
             ORDER BY w.id ASC
             LIMIT :limit"
        );
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}
