<?php
/**
 * Repository dla płatności faktury (multi-rata).
 *
 * @package KSEF\WordPress
 */

declare(strict_types=1);

namespace KSEF\WordPress;

// phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.SlowDBQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Wlasne tabele gksef_*: wartosci przez $wpdb->prepare()/esc_like(), nazwy tabel z $wpdb->prefix, paginacja rzutowana (int); nonce weryfikowany w handlerze (check_ajax_referer/check_admin_referer).

class Payments_Repository {

	public static function table(): string {
		global $wpdb;
		return $wpdb->prefix . 'gksef_invoice_payments';
	}

	public static function invoices_table(): string {
		global $wpdb;
		return $wpdb->prefix . 'gksef_outgoing_invoices';
	}

	/**
	 * @return array<int,array<string,mixed>>
	 */
	public static function list_for_invoice( int $invoice_id ): array {
		global $wpdb;
		$rows = $wpdb->get_results( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			$wpdb->prepare(
				'SELECT * FROM ' . self::table() . ' WHERE invoice_id = %d ORDER BY payment_date ASC, id ASC', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
				$invoice_id
			),
			ARRAY_A
		);
		return is_array( $rows ) ? $rows : [];
	}

	/**
	 * @param array<string,mixed> $data
	 * @return int|\WP_Error
	 */
	public static function add( int $invoice_id, array $data ) {
		global $wpdb;
		$amount = round( (float) ( $data['amount'] ?? 0 ), 2 );
		if ( $amount <= 0 ) {
			return new \WP_Error( 'invalid_amount', 'Kwota płatności musi być > 0.' );
		}
		$date = (string) ( $data['payment_date'] ?? gmdate( 'Y-m-d' ) );
		$ts   = strtotime( $date );
		if ( ! $ts ) {
			return new \WP_Error( 'invalid_date', 'Nieprawidłowa data płatności.' );
		}
		$ok = $wpdb->insert(
			self::table(),
			[
				'invoice_id'     => $invoice_id,
				'amount'         => $amount,
				'currency'       => (string) ( $data['currency'] ?? 'PLN' ),
				'payment_date'   => gmdate( 'Y-m-d', $ts ),
				'payment_method' => (string) ( $data['payment_method'] ?? '' ),
				'reference'      => (string) ( $data['reference'] ?? '' ),
				'note'           => (string) ( $data['note'] ?? '' ),
			]
		);
		if ( false === $ok ) {
			return new \WP_Error( 'db_insert_failed', 'Nie udało się zapisać płatności.' );
		}
		$id = (int) $wpdb->insert_id;
		self::recompute_status( $invoice_id );
		return $id;
	}

	public static function delete( int $payment_id ): bool {
		global $wpdb;
		$row = $wpdb->get_row( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			$wpdb->prepare( 'SELECT invoice_id FROM ' . self::table() . ' WHERE id = %d', $payment_id ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			ARRAY_A
		);
		if ( ! $row ) {
			return false;
		}
		$wpdb->delete( self::table(), [ 'id' => $payment_id ], [ '%d' ] );
		self::recompute_status( (int) $row['invoice_id'] );
		return true;
	}

	/**
	 * Po dodaniu/usunięciu płatności przelicza status faktury (unpaid/partial/paid) i sumę paid_amount.
	 */
	public static function recompute_status( int $invoice_id ): void {
		global $wpdb;
		$paid = (float) $wpdb->get_var( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			$wpdb->prepare( 'SELECT COALESCE(SUM(amount), 0) FROM ' . self::table() . ' WHERE invoice_id = %d', $invoice_id ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
		);
		$gross = (float) $wpdb->get_var( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			$wpdb->prepare( 'SELECT gross_amount FROM ' . self::invoices_table() . ' WHERE id = %d', $invoice_id ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
		);
		$status = 'unpaid';
		if ( $gross > 0 && $paid >= $gross - 0.005 ) {
			$status = 'paid';
		} elseif ( $paid > 0 ) {
			$status = 'partial';
		}
		$wpdb->update(
			self::invoices_table(),
			[
				'paid_amount'    => round( $paid, 2 ),
				'payment_status' => $status,
			],
			[ 'id' => $invoice_id ],
			[ '%f', '%s' ],
			[ '%d' ]
		);
	}

	public static function summary( int $invoice_id ): array {
		global $wpdb;
		$inv = $wpdb->get_row( // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
			$wpdb->prepare(
				'SELECT gross_amount, paid_amount, payment_status, currency, due_date FROM ' . self::invoices_table() . ' WHERE id = %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- nazwy tabel z $wpdb->prefix, wartosci przez $wpdb->prepare().
				$invoice_id
			),
			ARRAY_A
		);
		if ( ! $inv ) {
			return [];
		}
		$gross    = (float) $inv['gross_amount'];
		$paid     = (float) $inv['paid_amount'];
		return [
			'gross_amount'   => $gross,
			'paid_amount'    => $paid,
			'outstanding'    => round( max( 0.0, $gross - $paid ), 2 ),
			'payment_status' => (string) $inv['payment_status'],
			'currency'       => (string) ( $inv['currency'] ?: 'PLN' ),
			'due_date'       => (string) ( $inv['due_date'] ?? '' ),
		];
	}
}
