<?php
/**
 * Monitor wygasania tokenów i certyfikatów KSeF.
 *
 * - daily cron sprawdza wszystkie kredencjały (gksef_credentials)
 * - admin_notices banner gdy <= 14 dni do wygaśnięcia
 * - email do administratora w D-14, D-7, D-1 (deduplikacja w wp_options)
 *
 * @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 Expiry_Monitor {

	public const CRON_HOOK         = 'ksef_check_credentials_expiry';
	public const OPT_NOTIFIED_PFX  = 'ksef_expiry_notified_'; // ksef_expiry_notified_{credential_id}_{days}
	public const WARN_DAYS         = 14;
	public const EMAIL_THRESHOLDS  = [ 14, 7, 1 ];

	public static function register(): void {
		add_action( 'init', [ self::class, 'maybe_schedule_cron' ] );
		add_action( self::CRON_HOOK, [ self::class, 'run_check' ] );
		add_action( 'admin_notices', [ self::class, 'render_admin_notice' ] );
	}

	public static function maybe_schedule_cron(): void {
		if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
			wp_schedule_event( time() + 60, 'daily', self::CRON_HOOK );
		}
	}

	public static function clear_cron(): void {
		$timestamp = wp_next_scheduled( self::CRON_HOOK );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, self::CRON_HOOK );
		}
	}

	/**
	 * Daily check — analizuje wszystkie kredencjały, wysyła maila gdy próg osiągnięty.
	 */
	public static function run_check(): void {
		$expiring = self::get_expiring();
		if ( empty( $expiring ) ) {
			return;
		}
		$admin_email = (string) get_option( 'admin_email' );
		if ( ! is_email( $admin_email ) ) {
			return;
		}

		foreach ( $expiring as $row ) {
			$days = $row['days_left'];
			foreach ( self::EMAIL_THRESHOLDS as $th ) {
				if ( $days <= $th ) {
					$key = self::OPT_NOTIFIED_PFX . $row['credential_id'] . '_' . $th;
					if ( ! get_option( $key ) ) {
						self::send_email( $admin_email, $row, $th );
						update_option( $key, gmdate( 'Y-m-d' ), false );
					}
					break; // tylko najwyższy zaspokojony próg → jeden mail
				}
			}
		}
	}

	/**
	 * Zwraca listę kredencjałów które wygasają w ciągu WARN_DAYS dni.
	 * @return array<int,array{credential_id:int,nip:string,name:string,kind:string,expires_at:string,days_left:int}>
	 */
	public static function get_expiring(): array {
		global $wpdb;
		$rows = $wpdb->get_results(
			"SELECT id, nip, name, auth_method, certificate_valid_to, token_valid_to FROM {$wpdb->prefix}gksef_credentials",
			ARRAY_A
		);
		if ( ! is_array( $rows ) ) {
			return [];
		}
		$out = [];
		$today = time();
		foreach ( $rows as $r ) {
			$expires = self::credential_expires_at( $r );
			if ( null === $expires ) {
				continue; // brak daty (token bezterminowy?) — pomijamy
			}
			$days_left = (int) floor( ( $expires - $today ) / 86400 );
			if ( $days_left > self::WARN_DAYS ) {
				continue;
			}
			$out[] = [
				'credential_id' => (int) $r['id'],
				'nip'           => (string) $r['nip'],
				'name'          => (string) ( $r['name'] ?? '' ),
				'kind'          => self::credential_kind( $r ),
				'expires_at'    => gmdate( 'Y-m-d', $expires ),
				'days_left'     => $days_left,
			];
		}
		return $out;
	}

	private static function credential_expires_at( array $row ): ?int {
		$type = self::credential_kind( $row );
		if ( 'certificate' === $type && ! empty( $row['certificate_valid_to'] ) ) {
			$ts = strtotime( (string) $row['certificate_valid_to'] );
			return $ts ?: null;
		}
		// Token KSeF: data podawana ręcznie przy zapisie kredencjału (kolumna
		// token_valid_to). Backwards-compat: jeśli pusta, sprawdź wp_options.
		if ( ! empty( $row['token_valid_to'] ) ) {
			$ts = strtotime( (string) $row['token_valid_to'] );
			return $ts ?: null;
		}
		$token_exp = get_option( 'ksef_token_expires_' . (int) $row['id'] );
		if ( $token_exp ) {
			$ts = strtotime( (string) $token_exp );
			return $ts ?: null;
		}
		return null;
	}

	private static function credential_kind( array $row ): string {
		return ( $row['auth_method'] ?? '' ) === 'certificate' ? 'certificate' : 'token';
	}

	private static function send_email( string $to, array $row, int $threshold ): void {
		$site = get_bloginfo( 'name' );
		$type = $row['kind'] === 'certificate' ? 'Certyfikat KSeF' : 'Token KSeF';
		$subject = sprintf(
			/* translators: 1: certyfikat/token, 2: days */
			__( '[KSeF] %1$s wygasa za %2$d dni — odnów go aby uniknąć przerwy w wysyłce faktur', 'prostafaktura-ksef' ),
			$type,
			$row['days_left']
		);
		$body = sprintf(
			"%s wygasa %s (za %d dni).\n\nNIP: %s\nNazwa: %s\n\nOdnów go w panelu KSeF i zaktualizuj w sklepie %s:\n%s\n\n— wtyczka KSeF WordPress",
			$type,
			$row['expires_at'],
			$row['days_left'],
			$row['nip'],
			$row['name'],
			$site,
			admin_url( 'admin.php?page=ksef-credentials' )
		);
		wp_mail( $to, $subject, $body );
	}

	public static function render_admin_notice(): void {
		if ( ! current_user_can( 'ksef_view_settings' ) ) {
			return;
		}
		$expiring = self::get_expiring();
		if ( empty( $expiring ) ) {
			return;
		}
		foreach ( $expiring as $row ) {
			$class = $row['days_left'] <= 1 ? 'error' : ( $row['days_left'] <= 7 ? 'warning' : 'info' );
			$type  = $row['kind'] === 'certificate' ? __( 'Certyfikat', 'prostafaktura-ksef' ) : __( 'Token', 'prostafaktura-ksef' );
			printf(
				'<div class="notice notice-%1$s"><p><strong>KSeF:</strong> %2$s dla NIP <code>%3$s</code> wygasa <strong>%4$s</strong> — za %5$d dni. <a href="%6$s">Otwórz kredencjały</a>.</p></div>',
				esc_attr( $class ),
				esc_html( $type ),
				esc_html( $row['nip'] ),
				esc_html( $row['expires_at'] ),
				(int) $row['days_left'],
				esc_url( admin_url( 'admin.php?page=ksef-credentials' ) )
			);
		}
	}
}
