<?php
/**
 * Admin page: Konfiguracja KSeF (tokeny + certyfikaty per NIP)
 *
 * Brakujący element z v1.0 — UI do tabeli `gksef_credentials`.
 * Pozwala dodać/edytować/usunąć wpis z tokenem KSeF lub certyfikatem XAdES.
 *
 * @package KSEF\WordPress\Admin
 */

declare(strict_types=1);

namespace KSEF\WordPress\Admin;

// phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders, WordPress.DB.SlowDBQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.Security.NonceVerification -- 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).

use KSEF\WordPress\Crypto;
use KSEF\WordPress\V2\Bootstrap as V2_Bootstrap;

class Credentials_Page {

	private const TABLE = 'gksef_credentials';

	public static function register_ajax(): void {
		add_action( 'wp_ajax_ksef_test_connection', [ self::class, 'ajax_test_connection' ] );
	}

	/**
	 * AJAX: testuje połączenie do KSeF API v2 dla danego credential.
	 * Wykonuje pełen Flow B: challenge → ksef-token → poll → token redeem.
	 * Zwraca JSON {success, message, details}.
	 */
	public static function ajax_test_connection(): void {
		if ( ! current_user_can( 'ksef_view_settings' ) ) {
			wp_send_json_error( [ 'message' => 'Brak uprawnień.' ], 403 );
		}
		check_ajax_referer( 'ksef_test_connection', 'nonce' );

		$credential_id = isset( $_POST['credential_id'] ) ? (int) $_POST['credential_id'] : 0;
		if ( $credential_id <= 0 ) {
			wp_send_json_error( [ 'message' => 'Brak credential_id.' ], 400 );
		}

		global $wpdb;
		$credential = $wpdb->get_row(
			$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}gksef_credentials WHERE id = %d", $credential_id ),
			ARRAY_A
		);
		if ( ! $credential ) {
			wp_send_json_error( [ 'message' => 'Kredencjał nie istnieje.' ], 404 );
		}

		// Walidacja zależna od metody uwierzytelniania:
		// - 'token'       wymaga token_encrypted w bazie
		// - 'certificate' wymaga certificate_pem + private_key_pem_encrypted
		$method = (string) ( $credential['auth_method'] ?? 'token' );
		if ( $method === 'token' && empty( $credential['token_encrypted'] ) ) {
			wp_send_json_error( [
				'message' => 'Brak tokenu KSeF w tym kredencjale. Wpisz token i zapisz, zanim przetestujesz połączenie.',
			], 400 );
		}
		if ( $method === 'certificate' && ( empty( $credential['certificate_pem'] ) || empty( $credential['private_key_pem_encrypted'] ) ) ) {
			wp_send_json_error( [
				'message' => 'Brak certyfikatu lub klucza prywatnego. Wgraj oba pliki i zapisz, zanim przetestujesz połączenie.',
			], 400 );
		}

		// Sprawdź czy klasy V2 są dostępne (load_dependencies w Plugin)
		if ( ! class_exists( '\\KSEF\\WordPress\\V2\\Bootstrap' ) ) {
			wp_send_json_error( [ 'message' => 'Moduł V2 nie został załadowany. Sprawdź pliki includes/v2/.' ], 500 );
		}

		try {
			$auth   = V2_Bootstrap::auth_for_credential( $credential );
			$result = $auth->authenticate( $credential );
		} catch ( \Throwable $e ) {
			wp_send_json_error( [
				'message' => 'Wyjątek: ' . $e->getMessage(),
				'file'    => basename( $e->getFile() ) . ':' . $e->getLine(),
			], 500 );
		}

		if ( is_wp_error( $result ) ) {
			$data    = $result->get_error_data();
			$raw_msg = $result->get_error_message();
			// Rozpoznaj transientny błąd sieci/DNS (KSeF/MF chwilowo nieosiągalny lub
			// resolver DNS hostingu zwisł) — pokaż czytelny komunikat zamiast "cURL error 28".
			$lc = strtolower( $raw_msg );
			$is_net = (
				false !== strpos( $lc, 'timed out' ) || false !== strpos( $lc, 'timeout' )
				|| false !== strpos( $lc, 'curl error 28' ) || false !== strpos( $lc, 'curl error 6' )
				|| false !== strpos( $lc, 'curl error 7' ) || false !== strpos( $lc, 'resolv' )
				|| false !== strpos( $lc, 'could not resolve' )
			);
			$message = $is_net
				? 'KSeF/MF chwilowo nie odpowiada (timeout połączenia lub DNS). To problem po stronie sieci, nie tokenu — spróbowaliśmy automatycznie ponownie. Poczekaj chwilę i kliknij „Testuj połączenie" jeszcze raz.'
				: $raw_msg;
			// Dorzuć ostatnie wpisy debug-loga (5 ostatnich) — żeby od razu widać
			// było czy environment/cert SHA256/timestamp_ms jest spójny.
			$debug_log = (array) get_option( 'ksef_auth_debug_log', [] );
			$last_debug = array_slice( $debug_log, -5 );
			wp_send_json_error( [
				'message'    => $message,
				'raw_message' => $raw_msg,
				'is_network' => $is_net,
				'error_code' => $result->get_error_code(),
				'details'    => is_array( $data ) ? $data : null,
				'debug_tail' => $last_debug,
			], 200 ); // 200 z {success:false} żeby JS łatwiej obsłużył
		}

		wp_send_json_success( [
			'message'                 => '✓ Połączenie z KSeF działa. Pomyślnie wykonano pełny flow autoryzacji.',
			'environment'             => (string) ( $credential['ksef_environment'] ?? 'test' ),
			'nip'                     => (string) ( $credential['nip'] ?? '' ),
			'reference_number'        => (string) ( $result['reference_number'] ?? '' ),
			'access_token_expires_at' => (string) ( $result['access_token_expires_at'] ?? '' ),
			'refresh_token_expires_at' => (string) ( $result['refresh_token_expires_at'] ?? '' ),
			// Kontekst do komunikatu zależnego od metody auth.
			'auth_method'             => (string) ( $credential['auth_method'] ?? 'token' ),
			'cert_valid_to'           => (string) ( $credential['certificate_valid_to'] ?? '' ),
			'cert_subject'            => (string) ( $credential['certificate_subject'] ?? '' ),
			'token_valid_to'          => (string) ( $credential['token_valid_to'] ?? '' ),
		] );
	}

	public function render_page() {
		if ( ! current_user_can( 'ksef_view_settings' ) ) {
			wp_die( esc_html__( 'Brak uprawnień.', 'prostafaktura-ksef' ) );
		}

		$action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : 'list';

		// POST handlery
		if ( 'POST' === sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) && isset( $_POST['ksef_credential_nonce'] ) ) {
			if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ksef_credential_nonce'] ) ), 'ksef_save_credential' ) ) {
				$this->handle_save();
			} else {
				wp_die( 'Invalid nonce.' );
			}
		}

		if ( 'delete' === $action && isset( $_GET['id'], $_GET['_wpnonce'] ) ) {
			if ( wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'ksef_delete_credential' ) ) {
				$this->handle_delete( (int) $_GET['id'] );
			}
		}

		$is_form  = in_array( $action, [ 'add', 'edit' ], true );
		$id_get   = isset( $_GET['id'] ) ? (int) $_GET['id'] : 0;

		$add_link = esc_url( add_query_arg( 'action', 'add', remove_query_arg( 'id' ) ) );
		$hero_btn = '<a href="' . $add_link . '" class="btn btn-primary btn-sm">+ ' . esc_html__( 'Dodaj kredencjał', 'prostafaktura-ksef' ) . '</a>';
		Modern_Shell::open( 'ustawienia', [
			'crumbs'       => [ __( 'Konfiguracja', 'prostafaktura-ksef' ), __( 'Kredencjały KSeF', 'prostafaktura-ksef' ), $is_form ? ( 'add' === $action ? __( 'Nowy', 'prostafaktura-ksef' ) : __( 'Edycja', 'prostafaktura-ksef' ) ) : __( 'Lista', 'prostafaktura-ksef' ) ],
			'title'        => 'Kredencjały | KSeF',
			'subtitle'     => __( 'Klucze API, tokeny tożsamości i środowiska (test/prod). Możesz mieć wiele kont KSeF per NIP — wtyczka wybiera właściwe automatycznie.', 'prostafaktura-ksef' ),
			'hero_actions' => $is_form ? '' : $hero_btn,
		] );
		try {
			$this->render_notices();
			if ( $is_form ) {
				$this->render_form( $id_get );
			} else {
				$this->render_list();
			}
		} finally {
			Modern_Shell::close();
		}
	}

	private function render_notices(): void {
		if ( isset( $_GET['saved'] ) ) {
			echo '<div class="notice notice-success"><p>' . esc_html__( 'Zapisano kredencjał KSeF.', 'prostafaktura-ksef' ) . '</p></div>';
		}
		if ( isset( $_GET['deleted'] ) ) {
			echo '<div class="notice notice-success"><p>' . esc_html__( 'Usunięto kredencjał.', 'prostafaktura-ksef' ) . '</p></div>';
		}
		if ( isset( $_GET['error'] ) ) {
			echo '<div class="notice notice-error"><p>' . esc_html( sanitize_text_field( wp_unslash( $_GET['error'] ) ) ) . '</p></div>';
		}
	}

	private function render_list(): void {
		global $wpdb;
		$table = $wpdb->prefix . self::TABLE;
		$rows  = $wpdb->get_results( "SELECT id, name, nip, ksef_environment, auth_method, certificate_subject, certificate_valid_to, auto_send_enabled, auto_sync_enabled, seller_name, seller_city, seller_postal FROM {$table} ORDER BY id DESC", ARRAY_A );

		echo '<p>' . esc_html__( 'Tu dodajesz dane uwierzytelniające do API KSeF — albo token autoryzacyjny z e-Mikrofirmy / KSeF, albo certyfikat XAdES z kluczem prywatnym.', 'prostafaktura-ksef' ) . '</p>';

		echo '<table class="wp-list-table widefat fixed striped">';
		echo '<thead><tr>';
		echo '<th>NIP</th><th>' . esc_html__( 'Nazwa', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Środowisko', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Metoda', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Sprzedawca', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Certyfikat', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Auto-wysyłka', 'prostafaktura-ksef' ) . '</th>';
		echo '<th>' . esc_html__( 'Akcje', 'prostafaktura-ksef' ) . '</th>';
		echo '</tr></thead><tbody>';

		if ( empty( $rows ) ) {
			echo '<tr><td colspan="8"><em>' . esc_html__( 'Brak skonfigurowanych kredencjałów. Dodaj pierwszy aby rozpocząć wystawianie faktur.', 'prostafaktura-ksef' ) . '</em></td></tr>';
		} else {
			foreach ( $rows as $r ) {
				$edit_url   = add_query_arg(
					[ 'action' => 'edit', 'id' => $r['id'] ],
					admin_url( 'admin.php?page=ksef-credentials' )
				);
				$delete_url = wp_nonce_url(
					add_query_arg(
						[ 'action' => 'delete', 'id' => $r['id'] ],
						admin_url( 'admin.php?page=ksef-credentials' )
					),
					'ksef_delete_credential'
				);
				echo '<tr>';
				echo '<td><strong>' . esc_html( $r['nip'] ) . '</strong></td>';
				echo '<td>' . esc_html( $r['name'] ?: '—' ) . '</td>';
				echo '<td>' . ( 'prod' === $r['ksef_environment'] ? '<span style="color:#c33;font-weight:600">PROD</span>' : '<span style="color:#888">TEST</span>' ) . '</td>';
				echo '<td>' . esc_html( $r['auth_method'] === 'certificate' ? 'XAdES cert' : 'token' ) . '</td>';
				echo '<td>';
				if ( ! empty( $r['seller_name'] ) ) {
					echo esc_html( $r['seller_name'] );
					if ( ! empty( $r['seller_city'] ) ) {
						echo '<br><small>' . esc_html( trim( ( $r['seller_postal'] ?? '' ) . ' ' . $r['seller_city'] ) ) . '</small>';
					}
				} else {
					echo '<span style="color:#c33;">' . esc_html__( '⚠ uzupełnij', 'prostafaktura-ksef' ) . '</span>';
				}
				echo '</td>';
				echo '<td>';
				if ( ! empty( $r['certificate_subject'] ) ) {
					$valid_to = $r['certificate_valid_to'] ? mysql2date( get_option( 'date_format' ), $r['certificate_valid_to'] ) : '—';
					echo esc_html( substr( $r['certificate_subject'], 0, 40 ) ) . '<br><small>' . esc_html__( 'do', 'prostafaktura-ksef' ) . ' ' . esc_html( $valid_to ) . '</small>';
				} else {
					echo '—';
				}
				echo '</td>';
				echo '<td>' . ( $r['auto_send_enabled'] ? '✅' : '❌' ) . '</td>';
				echo '<td>';
				echo '<a href="' . esc_url( $edit_url ) . '" class="button button-small">' . esc_html__( 'Edytuj', 'prostafaktura-ksef' ) . '</a> ';
				echo '<a href="' . esc_url( $delete_url ) . '" class="button button-small button-link-delete" onclick="return confirm(\'' . esc_js( __( 'Usunąć ten kredencjał?', 'prostafaktura-ksef' ) ) . '\');">' . esc_html__( 'Usuń', 'prostafaktura-ksef' ) . '</a>';
				echo '</td>';
				echo '</tr>';
			}
		}
		echo '</tbody></table>';
	}

	private function render_form( int $id ): void {
		global $wpdb;
		$table = $wpdb->prefix . self::TABLE;
		$row   = $id ? $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table} WHERE id = %d", $id ), ARRAY_A ) : null;

		$values = [
			'id'                    => $row['id'] ?? 0,
			'name'                  => $row['name'] ?? '',
			'nip'                   => $row['nip'] ?? '',
			'ksef_environment'      => $row['ksef_environment'] ?? 'test',
			'auth_method'           => $row['auth_method'] ?? 'token',
			'auto_send_enabled'     => (int) ( $row['auto_send_enabled'] ?? 0 ),
			'auto_sync_enabled'     => (int) ( $row['auto_sync_enabled'] ?? 0 ),
			'auto_create_expenses'  => (int) ( $row['auto_create_expenses'] ?? 0 ),
			'sync_incoming'         => (int) ( $row['sync_incoming'] ?? 1 ),
			'sync_outgoing'         => (int) ( $row['sync_outgoing'] ?? 0 ),
			'certificate_subject'   => $row['certificate_subject'] ?? '',
			'certificate_valid_to'  => $row['certificate_valid_to'] ?? '',
			'token_valid_to'        => $row['token_valid_to'] ?? '',
			'seller_name'           => $row['seller_name']    ?? get_option( 'gksef_seller_name', '' ),
			'seller_email'          => $row['seller_email']   ?? get_option( 'gksef_seller_email', '' ),
			'seller_phone'          => $row['seller_phone']   ?? get_option( 'gksef_seller_phone', '' ),
			'seller_street'         => $row['seller_street']  ?? get_option( 'gksef_seller_street', '' ),
			'seller_building'       => $row['seller_building'] ?? get_option( 'gksef_seller_street_number', '' ),
			'seller_postal'         => $row['seller_postal']  ?? get_option( 'gksef_seller_postal_code', '' ),
			'seller_city'           => $row['seller_city']    ?? get_option( 'gksef_seller_city', '' ),
			'seller_country'        => $row['seller_country'] ?? get_option( 'gksef_seller_country', 'PL' ),
		];

		?>
		<form method="post" enctype="multipart/form-data" style="max-width:760px">
			<?php wp_nonce_field( 'ksef_save_credential', 'ksef_credential_nonce' ); ?>
			<input type="hidden" name="id" value="<?php echo esc_attr( (string) $values['id'] ); ?>">

			<table class="form-table">
				<tr>
					<th><label for="ksef_nip"><?php esc_html_e( 'NIP sprzedawcy', 'prostafaktura-ksef' ); ?> *</label></th>
					<td>
						<input id="ksef_nip" name="nip" type="text" class="regular-text" value="<?php echo esc_attr( $values['nip'] ); ?>" required pattern="\d{10}" maxlength="10">
						<p class="description"><?php esc_html_e( '10 cyfr bez kresek.', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>
				<tr>
					<th><label for="ksef_env"><?php esc_html_e( 'Środowisko', 'prostafaktura-ksef' ); ?></label></th>
					<td>
						<select id="ksef_env" name="ksef_environment">
							<option value="test" <?php selected( $values['ksef_environment'], 'test' ); ?>>TEST (ksef-test.mf.gov.pl)</option>
							<option value="demo" <?php selected( $values['ksef_environment'], 'demo' ); ?>>DEMO (ksef-demo.mf.gov.pl)</option>
							<option value="prod" <?php selected( $values['ksef_environment'], 'prod' ); ?>>PRODUKCJA (ksef.mf.gov.pl)</option>
						</select>
						<p class="description"><?php esc_html_e( 'Zacznij od TEST — to środowisko nie wysyła nic do Twoich kontrahentów.', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>
				<tr>
					<th><?php esc_html_e( 'Metoda uwierzytelniania', 'prostafaktura-ksef' ); ?></th>
					<td>
						<label><input type="radio" name="auth_method" value="token" <?php checked( $values['auth_method'], 'token' ); ?> onclick="document.getElementById('ksef-token-box').style.display='';document.getElementById('ksef-token-expiry-box').style.display='';document.getElementById('ksef-cert-box').style.display='none';"> <?php esc_html_e( 'Token autoryzacyjny (z e-Mikrofirmy / panelu KSeF)', 'prostafaktura-ksef' ); ?></label><br>
						<label><input type="radio" name="auth_method" value="certificate" <?php checked( $values['auth_method'], 'certificate' ); ?> onclick="document.getElementById('ksef-token-box').style.display='none';document.getElementById('ksef-token-expiry-box').style.display='none';document.getElementById('ksef-cert-box').style.display='';"> <?php esc_html_e( 'Certyfikat XAdES (do podpisu kwalifikowanego)', 'prostafaktura-ksef' ); ?></label>
					</td>
				</tr>
				<tr id="ksef-token-box" style="<?php echo $values['auth_method'] === 'token' ? '' : 'display:none'; ?>">
					<th><label for="ksef_token"><?php esc_html_e( 'Token KSeF', 'prostafaktura-ksef' ); ?></label></th>
					<td>
						<textarea id="ksef_token" name="token" rows="3" class="large-text" placeholder="<?php echo $id ? esc_attr__( '— bez zmian (zostaw puste żeby zachować obecny) —', 'prostafaktura-ksef' ) : esc_attr__( 'Wklej token z panelu KSeF', 'prostafaktura-ksef' ); ?>"></textarea>
						<p class="description"><?php esc_html_e( 'Token wygenerowany w panelu KSeF (lub e-Mikrofirma → Uprawnienia → Generuj token). Trzymany w bazie zaszyfrowany AES-256-GCM.', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>
				<tr id="ksef-token-expiry-box" style="<?php echo $values['auth_method'] === 'token' ? '' : 'display:none'; ?>">
					<th><label for="ksef_token_valid_to"><?php esc_html_e( 'Token ważny do', 'prostafaktura-ksef' ); ?></label></th>
					<td>
						<input id="ksef_token_valid_to" name="token_valid_to" type="date" value="<?php echo esc_attr( $values['token_valid_to'] ? substr( (string) $values['token_valid_to'], 0, 10 ) : '' ); ?>">
						<p class="description"><?php esc_html_e( 'Data wygaśnięcia tokenu z panelu KSeF (widoczna przy generowaniu w MF). Wtyczka monitoruje ją i wysyła e-mail do administratora 14, 7 i 1 dzień przed wygaśnięciem oraz wyświetla banner w panelu admina. Zostaw puste jeśli token jest bezterminowy.', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>
				<tr id="ksef-cert-box" style="<?php echo $values['auth_method'] === 'certificate' ? '' : 'display:none'; ?>">
					<th><?php esc_html_e( 'Certyfikat + klucz prywatny', 'prostafaktura-ksef' ); ?></th>
					<td>
						<?php if ( ! empty( $values['certificate_subject'] ) ) : ?>
							<p>
								<strong><?php esc_html_e( 'Aktualnie:', 'prostafaktura-ksef' ); ?></strong> <?php echo esc_html( $values['certificate_subject'] ); ?><br>
								<small><?php esc_html_e( 'ważny do', 'prostafaktura-ksef' ); ?> <?php echo esc_html( $values['certificate_valid_to'] ?: '—' ); ?></small>
							</p>
						<?php endif; ?>
						<p><label><?php esc_html_e( 'Certyfikat (.crt / .pem)', 'prostafaktura-ksef' ); ?>: <input type="file" name="certificate_file" accept=".crt,.pem,.cer"></label></p>
						<p><label><?php esc_html_e( 'Klucz prywatny (.key / .pem)', 'prostafaktura-ksef' ); ?>: <input type="file" name="private_key_file" accept=".key,.pem"></label></p>
						<p><label><?php esc_html_e( 'Hasło do klucza prywatnego (jeśli zaszyfrowany)', 'prostafaktura-ksef' ); ?>: <input type="password" name="private_key_password" class="regular-text" autocomplete="new-password"></label></p>
						<p class="description"><?php esc_html_e( 'Klucz prywatny przechowywany w bazie zaszyfrowany AES-256-GCM (kluczem opartym na AUTH_KEY z wp-config.php).', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>

				<tr>
					<th><?php esc_html_e( 'Synchronizacja z KSeF', 'prostafaktura-ksef' ); ?></th>
					<td>
						<label><input type="checkbox" name="auto_sync_enabled" value="1" <?php checked( $values['auto_sync_enabled'], 1 ); ?>> <strong><?php esc_html_e( 'Włącz codzienną synchronizację z KSeF', 'prostafaktura-ksef' ); ?></strong></label>
						<div style="margin-left:24px;margin-top:6px;padding-left:10px;border-left:2px solid #e3e5e8;">
							<label><input type="checkbox" name="sync_incoming" value="1" <?php checked( (int) ( $values['sync_incoming'] ?? 1 ), 1 ); ?>> <?php esc_html_e( 'pobieraj faktury zakupowe (Subject2 = nasz NIP jako nabywca)', 'prostafaktura-ksef' ); ?></label><br>
							<label><input type="checkbox" name="sync_outgoing" value="1" <?php checked( (int) ( $values['sync_outgoing'] ?? 0 ), 1 ); ?>> <?php esc_html_e( 'pobieraj faktury sprzedaży (Subject1 = nasz NIP jako wystawca, z innych źródeł niż ten plugin)', 'prostafaktura-ksef' ); ?></label>
						</div>
						<p class="description" style="margin-top:8px;"><?php esc_html_e( 'Auto-wysyłka nowych faktur ustawiana jest globalnie w Ustawienia → KSeF (jedna polityka dla wszystkich kredencjałów).', 'prostafaktura-ksef' ); ?></p>
					</td>
				</tr>
			</table>

			<h2 style="margin-top:28px;"><?php esc_html_e( 'Dane sprzedawcy (na fakturze)', 'prostafaktura-ksef' ); ?></h2>
			<p class="description"><?php esc_html_e( 'Te dane trafiają do każdej faktury wystawionej z tego kredencjału (Podmiot1 w XML, nagłówek PDF). Wymagane.', 'prostafaktura-ksef' ); ?></p>
			<table class="form-table">
				<tr>
					<th><label for="seller_name"><?php esc_html_e( 'Nazwa firmy', 'prostafaktura-ksef' ); ?> *</label></th>
					<td><input id="seller_name" name="seller_name" type="text" class="regular-text" required value="<?php echo esc_attr( (string) $values['seller_name'] ); ?>"></td>
				</tr>
				<tr>
					<th><label for="seller_email"><?php esc_html_e( 'Email', 'prostafaktura-ksef' ); ?> *</label></th>
					<td><input id="seller_email" name="seller_email" type="email" class="regular-text" required value="<?php echo esc_attr( (string) $values['seller_email'] ); ?>"></td>
				</tr>
				<tr>
					<th><label for="seller_phone"><?php esc_html_e( 'Telefon', 'prostafaktura-ksef' ); ?> *</label></th>
					<td><input id="seller_phone" name="seller_phone" type="text" class="regular-text" required value="<?php echo esc_attr( (string) $values['seller_phone'] ); ?>"></td>
				</tr>
				<tr>
					<th><?php esc_html_e( 'Adres (ulica / numer)', 'prostafaktura-ksef' ); ?> *</th>
					<td>
						<input name="seller_street" type="text" required value="<?php echo esc_attr( (string) $values['seller_street'] ); ?>" placeholder="<?php esc_attr_e( 'Ulica', 'prostafaktura-ksef' ); ?>" style="width:58%;">
						<input name="seller_building" type="text" required value="<?php echo esc_attr( (string) $values['seller_building'] ); ?>" placeholder="<?php esc_attr_e( 'Numer', 'prostafaktura-ksef' ); ?>" style="width:30%;">
					</td>
				</tr>
				<tr>
					<th><?php esc_html_e( 'Kod / miasto / kraj', 'prostafaktura-ksef' ); ?> *</th>
					<td>
						<input name="seller_postal" type="text" required value="<?php echo esc_attr( (string) $values['seller_postal'] ); ?>" placeholder="00-000" style="width:100px;">
						<input name="seller_city" type="text" required value="<?php echo esc_attr( (string) $values['seller_city'] ); ?>" placeholder="<?php esc_attr_e( 'Miasto (= Miejsce wystawienia)', 'prostafaktura-ksef' ); ?>">
						<input name="seller_country" type="text" maxlength="2" required value="<?php echo esc_attr( (string) $values['seller_country'] ); ?>" style="width:60px;">
					</td>
				</tr>
			</table>

			<p class="submit">
				<button type="submit" class="button button-primary"><?php esc_html_e( 'Zapisz', 'prostafaktura-ksef' ); ?></button>
				<a href="<?php echo esc_url( admin_url( 'admin.php?page=ksef-credentials' ) ); ?>" class="button"><?php esc_html_e( 'Anuluj', 'prostafaktura-ksef' ); ?></a>
				<?php if ( $values['id'] ) : ?>
					<button type="button" id="ksef-test-btn" class="button" data-credential-id="<?php echo (int) $values['id']; ?>">
						<?php esc_html_e( 'Testuj połączenie', 'prostafaktura-ksef' ); ?>
					</button>
					<span id="ksef-test-spinner" class="spinner" style="float:none;margin-left:6px;"></span>
				<?php endif; ?>
			</p>
			<div id="ksef-test-result" style="margin-top:14px;display:none;"></div>
		</form>

		<?php ob_start(); ?>
		(function(){
			const btn = document.getElementById('ksef-test-btn');
			if (!btn) return;
			const spinner = document.getElementById('ksef-test-spinner');
			const result = document.getElementById('ksef-test-result');
			const ajaxUrl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php' ) ); ?>;
			const nonce = <?php echo wp_json_encode( wp_create_nonce( 'ksef_test_connection' ) ); ?>;

			btn.addEventListener('click', async function(){
				btn.disabled = true;
				spinner.classList.add('is-active');
				spinner.style.visibility = 'visible';
				result.style.display = 'none';
				result.innerHTML = '';

				const body = new FormData();
				body.append('action', 'ksef_test_connection');
				body.append('nonce', nonce);
				body.append('credential_id', btn.dataset.credentialId);

				// Twardy timeout — auth flow KSeF może trwać do ~90s (3×timeout + poll),
				// więc dajemy 90s i NIGDY nie zostawiamy wiszącego spinnera.
				const ctrl = new AbortController();
				const timer = setTimeout( () => ctrl.abort(), 90000 );
				try {
					const resp = await fetch(ajaxUrl, { method: 'POST', body, credentials: 'same-origin', signal: ctrl.signal } );
					clearTimeout( timer );
					const json = await resp.json();
					if (json.success) {
						const d = json.data;
						result.innerHTML =
							'<div class="notice notice-success" style="padding:12px;">'
							+ '<p><strong>' + escapeHtml(d.message || 'OK') + '</strong></p>'
							+ '<ul style="margin:8px 0 0 16px;list-style:disc;">'
							+ '<li>Środowisko: <code>' + escapeHtml(d.environment) + '</code></li>'
							+ '<li>NIP: <code>' + escapeHtml(d.nip) + '</code></li>'
							+ '<li>Reference Number: <code>' + escapeHtml(d.reference_number) + '</code></li>'
							+ '</ul>'
							+ '<p style="margin-top:10px;font-size:12px;color:#5a6473;"><strong>Session tokens KSeF</strong> (krótkie z designu — wtyczka auto-odnawia):</p>'
							+ '<ul style="margin:4px 0 0 16px;list-style:disc;font-size:12px;color:#5a6473;">'
							+ '<li>Access token (~20 min): <code>' + escapeHtml(d.access_token_expires_at) + '</code></li>'
							+ '<li>Refresh token (~7 dni): <code>' + escapeHtml(d.refresh_token_expires_at) + '</code></li>'
							+ '</ul>'
							+ expiryNote(d);
					} else {
						const e = json.data || {};
						let detailsHtml = '';
						if (e.details) {
							detailsHtml = '<details style="margin-top:8px;"><summary>Szczegóły techniczne</summary><pre style="white-space:pre-wrap;font-size:11px;">'
								+ escapeHtml(JSON.stringify(e.details, null, 2)) + '</pre></details>';
						}
						let debugHtml = '';
						if (e.debug_tail && e.debug_tail.length) {
							debugHtml = '<details style="margin-top:8px;" open><summary>Log debugowy auth flow (ostatnie ' + e.debug_tail.length + ' wpisów)</summary><pre style="white-space:pre-wrap;font-size:11px;max-height:340px;overflow:auto;background:#f5f5f5;padding:8px;border-radius:6px;">'
								+ escapeHtml(JSON.stringify(e.debug_tail, null, 2)) + '</pre></details>';
						}
						result.innerHTML =
							'<div class="notice notice-error" style="padding:12px;">'
							+ '<p><strong>Błąd: </strong>' + escapeHtml(e.message || 'Nieznany błąd') + '</p>'
							+ (e.error_code ? '<p><code>' + escapeHtml(e.error_code) + '</code>'
								+ (e.file ? ' @ <code>' + escapeHtml(e.file) + '</code>' : '') + '</p>' : '')
							+ detailsHtml
							+ debugHtml
							+ '</div>';
					}
					result.style.display = 'block';
				} catch (err) {
					clearTimeout( timer );
					const msg = ( err && err.name === 'AbortError' )
						? 'Przekroczono czas oczekiwania (90s). Środowisko KSeF mogło chwilowo nie odpowiadać — spróbuj ponownie za chwilę.'
						: ( 'Network error: ' + escapeHtml( String( err ) ) );
					result.innerHTML = '<div class="notice notice-error" style="padding:12px;"><p><strong>' + msg + '</strong></p></div>';
					result.style.display = 'block';
				} finally {
					btn.disabled = false;
					spinner.classList.remove('is-active');
					spinner.style.visibility = 'hidden';
				}
			});

			function escapeHtml(s){
				return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
			}
			function expiryNote(d){
				if ( d.auth_method === 'certificate' ) {
					var vt = d.cert_valid_to ? escapeHtml(String(d.cert_valid_to).substring(0,10)) : '—';
					return '<p style="margin-top:10px;font-size:12px;color:#15803d;">'
						+ '✓ <strong>Wygaśnięcie monitorowane automatycznie.</strong> Certyfikat ważny do <code>' + vt + '</code>'
						+ ' — wtyczka sama wyśle e-mail 14, 7 i 1 dzień przed. Nic nie musisz wpisywać ręcznie.</p>';
				}
				if ( d.token_valid_to ) {
					return '<p style="margin-top:10px;font-size:12px;color:#15803d;">'
						+ '✓ Token ważny do <code>' + escapeHtml(String(d.token_valid_to).substring(0,10)) + '</code> — monitorowany automatycznie.</p>';
				}
				return '<p style="margin-top:10px;font-size:12px;color:#5a6473;">'
					+ '<em>Opcjonalnie:</em> token KSeF (z panelu MF) ma własną datę ważności, której nie da się odczytać z API. '
					+ 'Jeśli chcesz dostać e-mail przed jego wygaśnięciem, wpisz ją w polu „Token ważny do" w formularzu powyżej.</p>';
			}
		})();
		<?php Modern_Shell::inline_script( 'ksef-credentials-test', ob_get_clean() ); ?>
		<?php
	}

	private function handle_save(): void {
		global $wpdb;
		$table = $wpdb->prefix . self::TABLE;

		$id  = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
		$nip = preg_replace( '/\D/', '', (string) sanitize_text_field( wp_unslash( $_POST['nip'] ?? '' ) ) );
		if ( strlen( $nip ) !== 10 ) {
			$this->redirect_with( [ 'action' => $id ? 'edit' : 'add', 'id' => $id, 'error' => __( 'Nieprawidłowy NIP — musi mieć 10 cyfr.', 'prostafaktura-ksef' ) ] );
		}

		$seller_name_raw = sanitize_text_field( wp_unslash( $_POST['seller_name'] ?? '' ) );
		$data = [
			'nip'                  => $nip,
			// 'name' = etykieta wewnętrzna; bierzemy ją z nazwy firmy (seller_name) żeby
			// nie trzymać dwóch tych samych pól. Fallback do NIP gdy nazwa pusta.
			'name'                 => $seller_name_raw !== '' ? $seller_name_raw : ( 'NIP ' . $nip ),
			'ksef_environment'     => in_array( sanitize_text_field( wp_unslash( $_POST['ksef_environment'] ?? '' ) ), [ 'test', 'demo', 'prod' ], true ) ? sanitize_text_field( wp_unslash( $_POST['ksef_environment'] ?? '' ) ) : 'test',
			'is_production'        => ( sanitize_text_field( wp_unslash( $_POST['ksef_environment'] ?? '' ) ) === 'prod' ) ? 1 : 0,
			'auth_method'          => sanitize_text_field( wp_unslash( $_POST['auth_method'] ?? 'token' ) ) === 'certificate' ? 'certificate' : 'token',
			'auto_sync_enabled'    => empty( $_POST['auto_sync_enabled'] ) ? 0 : 1,
			'sync_incoming'        => empty( $_POST['sync_incoming'] ) ? 0 : 1,
			'sync_outgoing'        => empty( $_POST['sync_outgoing'] ) ? 0 : 1,
			'seller_name'          => $seller_name_raw,
			'seller_email'         => sanitize_email( wp_unslash( $_POST['seller_email'] ?? '' ) ),
			'seller_phone'         => sanitize_text_field( wp_unslash( $_POST['seller_phone'] ?? '' ) ),
			'seller_street'        => sanitize_text_field( wp_unslash( $_POST['seller_street'] ?? '' ) ),
			'seller_building'      => sanitize_text_field( wp_unslash( $_POST['seller_building'] ?? '' ) ),
			'seller_postal'        => sanitize_text_field( wp_unslash( $_POST['seller_postal'] ?? '' ) ),
			'seller_city'          => sanitize_text_field( wp_unslash( $_POST['seller_city'] ?? '' ) ),
			'seller_country'       => strtoupper( substr( sanitize_text_field( wp_unslash( $_POST['seller_country'] ?? 'PL' ) ), 0, 2 ) ),
		];

		$token_plain = trim( (string) sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) ) );
		if ( $token_plain !== '' ) {
			$data['token_encrypted'] = Crypto::encrypt_token( $token_plain );
		}

		// Data ważności tokenu KSeF — opcjonalne pole (puste = bezterminowy, brak monitora).
		$token_valid_to_raw = (string) sanitize_text_field( wp_unslash( $_POST['token_valid_to'] ?? '' ) );
		if ( $token_valid_to_raw !== '' && preg_match( '/^\d{4}-\d{2}-\d{2}$/', $token_valid_to_raw ) ) {
			$data['token_valid_to'] = $token_valid_to_raw . ' 23:59:59';
		} elseif ( $token_valid_to_raw === '' ) {
			$data['token_valid_to'] = null;
		}

		// Certyfikat (upload)
		if ( ! empty( $_FILES['certificate_file']['tmp_name'] ) ) {
			$cert_pem = (string) file_get_contents( sanitize_text_field( wp_unslash( $_FILES['certificate_file']['tmp_name'] ) ) );
			$parsed   = openssl_x509_parse( $cert_pem );
			if ( $parsed === false ) {
				$this->redirect_with( [ 'action' => 'edit', 'id' => $id, 'error' => __( 'Nieprawidłowy plik certyfikatu (musi być PEM lub konwertowalny).', 'prostafaktura-ksef' ) ] );
			}
			$data['certificate_pem']        = $cert_pem;
			$data['certificate_subject']    = is_array( $parsed['subject'] ?? null ) ? implode( ', ', array_map( fn ( $k, $v ) => "{$k}={$v}", array_keys( $parsed['subject'] ), array_values( $parsed['subject'] ) ) ) : '';
			$data['certificate_issuer']     = is_array( $parsed['issuer'] ?? null ) ? implode( ', ', array_map( fn ( $k, $v ) => "{$k}={$v}", array_keys( $parsed['issuer'] ), array_values( $parsed['issuer'] ) ) ) : '';
			$data['certificate_serial']     = (string) ( $parsed['serialNumber'] ?? '' );
			$data['certificate_valid_from'] = isset( $parsed['validFrom_time_t'] ) ? gmdate( 'Y-m-d H:i:s', (int) $parsed['validFrom_time_t'] ) : null;
			$data['certificate_valid_to']   = isset( $parsed['validTo_time_t'] ) ? gmdate( 'Y-m-d H:i:s', (int) $parsed['validTo_time_t'] ) : null;
		}

		// Klucz prywatny (upload)
		if ( ! empty( $_FILES['private_key_file']['tmp_name'] ) ) {
			$key_pem = (string) file_get_contents( sanitize_text_field( wp_unslash( $_FILES['private_key_file']['tmp_name'] ) ) );
			$pass    = (string) wp_unslash( $_POST['private_key_password'] ?? '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- haslo do klucza prywatnego: zachowujemy doslownie (bez sanitize).
			$res     = openssl_pkey_get_private( $key_pem, $pass );
			if ( $res === false ) {
				$this->redirect_with( [ 'action' => 'edit', 'id' => $id, 'error' => __( 'Nie udało się odczytać klucza prywatnego (sprawdź hasło i format).', 'prostafaktura-ksef' ) ] );
			}
			// Eksportujemy do PEM (bez passphrase już) i szyfrujemy własnym mechanizmem
			openssl_pkey_export( $res, $exported );
			$data['private_key_pem_encrypted'] = Crypto::encrypt_private_key( $exported );
		}

		if ( $id ) {
			$wpdb->update( $table, $data, [ 'id' => $id ] );
		} else {
			$wpdb->insert( $table, $data );
			$id = (int) $wpdb->insert_id;
		}

		$this->redirect_with( [ 'action' => 'edit', 'id' => $id, 'saved' => 1 ] );
	}

	private function handle_delete( int $id ): void {
		global $wpdb;
		$wpdb->delete( $wpdb->prefix . self::TABLE, [ 'id' => $id ], [ '%d' ] );
		$this->redirect_with( [ 'deleted' => 1 ] );
	}

	private function redirect_with( array $args ): void {
		$base = admin_url( 'admin.php?page=ksef-credentials' );
		wp_safe_redirect( add_query_arg( $args, $base ) );
		exit;
	}
}
