<?php
/**
 * Payment Repository
 *
 * @package NemtlyBooking
 */

namespace NemtlyBooking\Repositories;

use NemtlyBooking\Interfaces\Payment_Repository_Interface;
use NemtlyBooking\Models\Payment;

/**
 * Class Payment_Repository
 */
class Payment_Repository implements Payment_Repository_Interface {
    /**
     * Table name
     *
     * @var string
     */
    private $table_name;

    /**
     * Constructor
     */
    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'nemtly_payments';
    }

    /**
     * Find payment by ID
     *
     * @param int $id Payment ID.
     *
     * @return Payment|null
     */
    public function find( $id ) {
        global $wpdb;
        $row = $wpdb->get_row(
            $wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE id = %d", $id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );

        return $row ? new Payment( $row ) : null;
    }

    /**
     * Find all payments
     *
     * @param array $args Query arguments.
     *
     * @return Payment[]
     */
    public function find_all( $args = [] ) {
        global $wpdb;
        $sql = "SELECT * FROM {$this->table_name} ORDER BY created_at DESC";

        $results = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

        return array_map(
            function ( $row ) {
                return new Payment( $row );
            },
            $results
        );
    }

    /**
     * Find payment by transaction ID
     *
     * @param string $transaction_id Transaction ID.
     *
     * @return Payment|null
     */
    public function find_by_transaction_id( $transaction_id ) {
        global $wpdb;
        $row = $wpdb->get_row(
            $wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE transaction_id = %s", $transaction_id ), // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            ARRAY_A
        );

        return $row ? new Payment( $row ) : null;
    }

    /**
     * Find payments by payable entity
     *
     * @param int    $payable_id   Payable ID.
     * @param string $payable_type Payable Type.
     *
     * @return Payment[]
     */
    public function find_by_payable( $payable_id, $payable_type ) {
        global $wpdb;
        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->table_name} WHERE payable_id = %d AND payable_type = %s ORDER BY created_at DESC", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $payable_id,
                $payable_type
            ),
            ARRAY_A
        );

        return array_map(
            function ( $row ) {
                return new Payment( $row );
            },
            $results
        );
    }

    /**
     * Find payments by booking ID
     *
     * @param int $booking_id Booking ID.
     *
     * @return Payment[]
     */
    public function find_by_booking_id( $booking_id ) {
        return $this->find_by_payable( $booking_id, 'booking' );
    }

    /**
     * Create payment
     *
     * @param mixed $data Payment object or data array.
     *
     * @return Payment|false
     */
    public function create( $data ) {
        global $wpdb;

        if ( $data instanceof Payment ) {
            $payment = $data;
        } else {
            $payment = new Payment( $data );
        }

        $data_array = [
            'payable_id'       => $payment->get_payable_id(),
            'payable_type'     => $payment->get_payable_type(),
            'booking_id'       => $payment->get_booking_id(),
            'transaction_id'   => $payment->get_transaction_id(),
            'amount'           => $payment->get_amount(),
            'currency'         => $payment->get_currency(),
            'status'           => $payment->get_status(),
            'gateway'          => $payment->get_gateway(),
            'gateway_response' => $payment->get_gateway_response(),
            'created_at'       => $payment->get_created_at(),
            'updated_at'       => $payment->get_updated_at(), // Initial insert needs updated_at too
        ];

        $format = [
            '%d', // payable_id
            '%s', // payable_type
            '%d', // booking_id
            '%s', // transaction_id
            '%f', // amount
            '%s', // currency
            '%s', // status
            '%s', // gateway
            '%s', // gateway_response
            '%s', // created_at
            '%s', // updated_at
        ];

        $inserted = $wpdb->insert( $this->table_name, $data_array, $format );

        if ( $inserted ) {
            $payment_id = $wpdb->insert_id;
            return $this->find( $payment_id );
        }

        return false;
    }

    /**
     * Update payment
     *
     * @param int   $id   Payment ID.
     * @param mixed $data Payment object or data array.
     *
     * @return Payment|false
     */
    public function update( $id, $data ) {
        global $wpdb;

        if ( $data instanceof Payment ) {
            $payment = $data;
        } else {
            // merge with existing? or just construct. For now construct.
            $data['id'] = $id;
            $payment    = new Payment( $data );
        }

        // Use getters to ensure we have the latest object state
        $update_data = [
            'payable_id'       => $payment->get_payable_id(),
            'payable_type'     => $payment->get_payable_type(),
            'booking_id'       => $payment->get_booking_id(),
            'transaction_id'   => $payment->get_transaction_id(),
            'amount'           => $payment->get_amount(),
            'currency'         => $payment->get_currency(),
            'status'           => $payment->get_status(),
            'gateway'          => $payment->get_gateway(),
            'gateway_response' => $payment->get_gateway_response(),
            'updated_at'       => current_time( 'mysql' ),
        ];

        $format = [
            '%d', // payable_id
            '%s', // payable_type
            '%d', // booking_id
            '%s', // transaction_id
            '%f', // amount
            '%s', // currency
            '%s', // status
            '%s', // gateway
            '%s', // gateway_response
            '%s', // updated_at
        ];

        $updated = $wpdb->update(
            $this->table_name,
            $update_data,
            [ 'id' => $id ], // Use $id passed to method
            $format,
            [ '%d' ]
        );

        if ( false !== $updated ) {
            return $this->find( $id );
        }

        return false;
    }

    /**
     * Delete payment
     *
     * @param int $id Payment ID.
     *
     * @return bool
     */
    public function delete( $id ) {
        global $wpdb;
        return (bool) $wpdb->delete( $this->table_name, [ 'id' => $id ], [ '%d' ] );
    }

    /**
     * Count payments
     *
     * @param array $args Query arguments.
     *
     * @return int
     */
    public function count( $args = [] ) {
        global $wpdb;

        $where_conditions = [];
        $where_values     = [];

        if ( ! empty( $args['status'] ) ) {
            $where_conditions[] = 'status = %s';
            $where_values[]     = $args['status'];
        }

        if ( ! empty( $args['payable_id'] ) ) {
            $where_conditions[] = 'payable_id = %d';
            $where_values[]     = (int) $args['payable_id'];
        }

        if ( ! empty( $args['payable_type'] ) ) {
            $where_conditions[] = 'payable_type = %s';
            $where_values[]     = $args['payable_type'];
        }

        if ( ! empty( $args['booking_id'] ) ) {
            $where_conditions[] = 'booking_id = %d';
            $where_values[]     = (int) $args['booking_id'];
        }

        $where_clause = ! empty( $where_conditions ) ? 'WHERE ' . implode( ' AND ', $where_conditions ) : '';

        if ( ! empty( $where_values ) ) {
            return (int) $wpdb->get_var(
                $wpdb->prepare(
                    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    "SELECT COUNT(*) FROM {$this->table_name} {$where_clause}",
                    $where_values
                )
            );
        } else {
            return (int) $wpdb->get_var(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                "SELECT COUNT(*) FROM {$this->table_name} {$where_clause}"
            );
        }
    }
}
