<?php
/**
 * Backend administration UI.
 *
 * @package KGV\VereinManager
 */

namespace KGV\VereinManager\Admin;

use KGV\VereinManager\Roles;
use KGV\VereinManager\Validator;
use KGV\VereinManager\Repositories\AssignmentRepository;
use KGV\VereinManager\Repositories\ChatRepository;
use KGV\VereinManager\Repositories\CostRepository;
use KGV\VereinManager\Repositories\MeterReadingRepository;
use KGV\VereinManager\Repositories\MeterRepository;
use KGV\VereinManager\Repositories\InventoryRepository;
use KGV\VereinManager\Repositories\ParcelRepository;
use KGV\VereinManager\Repositories\SectionRepository;
use KGV\VereinManager\Repositories\TenantRepository;
use KGV\VereinManager\Repositories\WorkRepository;
use KGV\VereinManager\Services\ParcelService;
use KGV\VereinManager\DataTransfer;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Admin {

	/**
	 * Validator instance.
	 *
	 * @var Validator
	 */
	private $validator;

	/**
	 * Repositories and services.
	 */
	private $sections;
	private $parcels;
	private $meters;
	private $readings;
	private $inventory;
	private $tenants;
	private $assignments;
	private $chat;
	private $costs;
	private $work;
	private $parcel_service;
	private $data_transfer;

	/**
	 * Construct admin controller.
	 */
	public function __construct() {
		$this->validator      = new Validator();
		$this->sections       = new SectionRepository();
		$this->parcels        = new ParcelRepository();
		$this->meters         = new MeterRepository();
		$this->readings       = new MeterReadingRepository();
		$this->inventory      = new InventoryRepository();
		$this->tenants        = new TenantRepository();
		$this->assignments    = new AssignmentRepository();
		$this->chat           = new ChatRepository();
		$this->costs          = new CostRepository();
		$this->work           = new WorkRepository();
		$this->parcel_service  = new ParcelService();
		global $wpdb;
		$this->data_transfer   = new DataTransfer( $wpdb );
	}

	/**
	 * Register WP hooks.
	 *
	 * @return void
	 */
	public function register_hooks() {
		add_action( 'admin_menu', array( $this, 'register_menus' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
		add_action( 'admin_init', array( $this, 'handle_requests' ) );
		add_action( 'wp_ajax_kgvvm_fetch_chat_messages', array( $this, 'ajax_fetch_chat_messages' ) );
		add_action( 'wp_ajax_kgvvm_send_chat_message', array( $this, 'ajax_send_chat_message' ) );
	}

	/**
	 * Register admin pages.
	 *
	 * @return void
	 */
	public function register_menus() {
		$cap = 'manage_kleingarten';

		if ( current_user_can( $cap ) ) {
			add_menu_page(
				__( 'Kleingartenverwaltung', KGVVM_TEXT_DOMAIN ),
				__( 'Kleingarten', KGVVM_TEXT_DOMAIN ),
				$cap,
				'kgvvm-dashboard',
				array( $this, 'render_dashboard' ),
				'dashicons-admin-multisite',
				24
			);

			add_submenu_page( 'kgvvm-dashboard', __( 'Dashboard', KGVVM_TEXT_DOMAIN ), __( 'Dashboard', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-dashboard', array( $this, 'render_dashboard' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Meine Parzellen', KGVVM_TEXT_DOMAIN ), __( 'Meine Parzellen', KGVVM_TEXT_DOMAIN ), 'view_assigned_parcels', 'kgvvm-my-parcels', array( $this, 'render_my_parcels_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Vereinschat', KGVVM_TEXT_DOMAIN ), __( 'Vereinschat', KGVVM_TEXT_DOMAIN ), 'view_assigned_parcels', 'kgvvm-chat', array( $this, 'render_chat_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Sparten', KGVVM_TEXT_DOMAIN ), __( 'Sparten', KGVVM_TEXT_DOMAIN ), 'edit_sparten', 'kgvvm-sparten', array( $this, 'render_sections_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Parzellen', KGVVM_TEXT_DOMAIN ), __( 'Parzellen', KGVVM_TEXT_DOMAIN ), 'edit_parzellen', 'kgvvm-parzellen', array( $this, 'render_parcels_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Zähler', KGVVM_TEXT_DOMAIN ), __( 'Zähler', KGVVM_TEXT_DOMAIN ), 'edit_zaehler', 'kgvvm-zaehler', array( $this, 'render_meters_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Inventar', KGVVM_TEXT_DOMAIN ), __( 'Inventar', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-inventory', array( $this, 'render_inventory_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Verbrauch', KGVVM_TEXT_DOMAIN ), __( 'Verbrauch', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-consumption', array( $this, 'render_consumption_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Kosten', KGVVM_TEXT_DOMAIN ), __( 'Kosten', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-costs', array( $this, 'render_costs_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ), __( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ), $cap, 'kgvvm-arbeit', array( $this, 'render_work_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Pächter', KGVVM_TEXT_DOMAIN ), __( 'Pächter', KGVVM_TEXT_DOMAIN ), 'edit_paechter', 'kgvvm-paechter', array( $this, 'render_tenants_page' ) );
			add_submenu_page( 'kgvvm-dashboard', __( 'Einstellungen', KGVVM_TEXT_DOMAIN ), __( 'Einstellungen', KGVVM_TEXT_DOMAIN ), Roles::SETTINGS_CAP, 'kgvvm-settings', array( $this, 'render_settings_page' ) );
		}

		if ( ! current_user_can( $cap ) && current_user_can( 'view_assigned_parcels' ) ) {
			add_menu_page(
				__( 'Meine Parzellen', KGVVM_TEXT_DOMAIN ),
				__( 'Meine Parzellen', KGVVM_TEXT_DOMAIN ),
				'view_assigned_parcels',
				'kgvvm-my-parcels',
				array( $this, 'render_my_parcels_page' ),
				'dashicons-location-alt',
				24
			);

			add_menu_page(
				__( 'Vereinschat', KGVVM_TEXT_DOMAIN ),
				__( 'Vereinschat', KGVVM_TEXT_DOMAIN ),
				'view_assigned_parcels',
				'kgvvm-chat',
				array( $this, 'render_chat_page' ),
				'dashicons-format-chat',
				25
			);
		}
	}

	/**
	 * Load admin stylesheet on plugin pages.
	 *
	 * @param string $hook Current admin hook.
	 * @return void
	 */
	public function enqueue_assets( $hook ) {
		if ( false === strpos( (string) $hook, 'kgvvm' ) ) {
			return;
		}

		wp_enqueue_style( 'kgvvm-admin', KGVVM_PLUGIN_URL . 'assets/css/admin.css', array(), KGVVM_VERSION );

		if ( false !== strpos( (string) $hook, 'kgvvm-chat' ) ) {
			wp_enqueue_script( 'kgvvm-chat', KGVVM_PLUGIN_URL . 'assets/js/chat.js', array(), KGVVM_VERSION, true );
			wp_localize_script(
				'kgvvm-chat',
				'kgvvmChatConfig',
				array(
					'ajaxUrl'         => admin_url( 'admin-ajax.php' ),
					'nonce'           => wp_create_nonce( 'kgvvm_chat_nonce' ),
					'refreshInterval' => 7000,
					'i18n'            => array(
						'empty'      => __( 'Noch keine Nachrichten in diesem Raum.', KGVVM_TEXT_DOMAIN ),
						'loading'    => __( 'Nachrichten werden geladen …', KGVVM_TEXT_DOMAIN ),
						'sending'    => __( 'Nachricht wird gesendet …', KGVVM_TEXT_DOMAIN ),
						'fetchError' => __( 'Der Chat konnte gerade nicht aktualisiert werden.', KGVVM_TEXT_DOMAIN ),
						'sendError'  => __( 'Die Nachricht konnte nicht gesendet werden.', KGVVM_TEXT_DOMAIN ),
					),
				)
			);
		}
	}

	/**
	 * Handle save/delete requests.
	 *
	 * @return void
	 */
	public function handle_requests() {
		if ( ! is_admin() || ( ! current_user_can( 'manage_kleingarten' ) && ! current_user_can( Roles::SETTINGS_CAP ) && ! current_user_can( 'submit_meter_readings' ) ) ) {
			return;
		}

		$page   = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
		$view   = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : '';
		$output = isset( $_GET['output'] ) ? sanitize_key( wp_unslash( $_GET['output'] ) ) : '';

		if ( current_user_can( 'manage_kleingarten' ) && 'kgvvm-costs' === $page && 'statement' === $view && 'pdf' === $output ) {
			$this->render_cost_statement_page();
			return;
		}

		if ( 'POST' === strtoupper( isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '' ) && ! empty( $_POST['kgvvm_action'] ) ) {
			$this->handle_post_action();
		}

		if ( ! empty( $_GET['kgvvm_action'] ) ) {
			$this->handle_get_action();
		}
	}

	/**
	 * Dispatch POST actions.
	 *
	 * @return void
	 */
	private function handle_post_action() {
		$action = sanitize_key( wp_unslash( $_POST['kgvvm_action'] ) );

		switch ( $action ) {
			case 'save_section':
				$this->save_section();
				break;
			case 'save_parcel':
				$this->save_parcel();
				break;
			case 'save_meter':
				$this->save_meter();
				break;
			case 'swap_meter':
				$this->swap_meter();
				break;
			case 'save_tenant':
				$this->save_tenant();
				break;
			case 'save_cost_year':
				$this->save_cost_year();
				break;
			case 'save_cost_prices':
				$this->save_cost_prices();
				break;
			case 'save_cost':
				$this->save_cost();
				break;
			case 'save_meter_reading':
				$this->save_meter_reading();
				break;
			case 'correct_meter_reading':
				$this->correct_meter_reading();
				break;
			case 'save_inventory_item':
				$this->save_inventory_item();
				break;
			case 'borrow_inventory_item':
				$this->borrow_inventory_item();
				break;
			case 'return_inventory_loan':
				$this->return_inventory_loan();
				break;
			case 'save_settings':
				$this->save_settings();
				break;
			case 'save_work_year_config':
				$this->save_work_year_config();
				break;
			case 'save_work_job':
				$this->save_work_job();
				break;
			case 'save_work_log':
				$this->save_work_log();
				break;
			case 'import_plugin_data':
				$this->import_plugin_data();
				break;
			case 'toggle_parcel_cost_assignment':
				$this->toggle_parcel_cost_assignment();
				break;
			case 'set_statement_lock':
				$this->set_statement_lock();
				break;
		}
	}

	/**
	 * Dispatch delete actions.
	 *
	 * @return void
	 */
	private function handle_get_action() {
		$action = sanitize_key( wp_unslash( $_GET['kgvvm_action'] ) );
		$id     = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$nonce  = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';

		switch ( $action ) {
			case 'delete_section':
				$this->require_cap( 'edit_sparten' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_section_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-sparten', 'error', __( 'Der Löschvorgang für die Sparte wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				if ( $this->sections->is_in_use( $id ) ) {
					$this->redirect_with_notice( 'kgvvm-sparten', 'error', __( 'Diese Sparte ist noch mit Parzellen oder Zählern verknüpft und kann nicht gelöscht werden.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->sections->delete( $id );
				$this->redirect_with_notice( 'kgvvm-sparten', 'success', __( 'Sparte wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_parcel':
				$this->require_cap( 'edit_parzellen' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_parcel_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-parzellen', 'error', __( 'Der Löschvorgang für die Parzelle wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->parcel_service->delete( $id );
				$this->redirect_with_notice( 'kgvvm-parzellen', 'success', __( 'Parzelle wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_meter':
				$this->require_cap( 'edit_zaehler' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_meter_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-zaehler', 'error', __( 'Der Löschvorgang für den Zähler wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				if ( $this->meters->is_assigned( $id ) ) {
					$this->redirect_with_notice( 'kgvvm-zaehler', 'error', __( 'Ein zugeordneter Zähler kann nicht gelöscht werden. Bitte zuerst die Parzellenzuordnung entfernen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->meters->delete( $id );
				$this->redirect_with_notice( 'kgvvm-zaehler', 'success', __( 'Zähler wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_meter_reading':
				$this->require_cap( 'manage_kleingarten' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_meter_reading_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-consumption', 'error', __( 'Der Löschvorgang wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->readings->delete( $id );
				$this->redirect_with_notice( 'kgvvm-consumption', 'success', __( 'Die Ablesung wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_inventory_item':
				$this->require_cap( 'manage_kleingarten' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_inventory_item_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Der Löschvorgang für den Inventargegenstand wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				if ( $this->inventory->has_open_loans( $id ) ) {
					$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Der Inventargegenstand hat noch offene Ausleihen und kann nicht gelöscht werden.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->inventory->delete( $id );
				$this->redirect_with_notice( 'kgvvm-inventory', 'success', __( 'Inventargegenstand wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_tenant':
				$this->require_cap( 'edit_paechter' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_tenant_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-paechter', 'error', __( 'Der Löschvorgang für den Pächter wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->assignments->purge_tenant( $id );
				$this->tenants->delete( $id );
				$this->redirect_with_notice( 'kgvvm-paechter', 'success', __( 'Pächter wurde gelöscht.', KGVVM_TEXT_DOMAIN ) );
				break;
			case 'delete_cost':
				$this->require_cap( 'manage_kleingarten' );
				$year = absint( isset( $_GET['year'] ) ? $_GET['year'] : current_time( 'Y' ) );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_cost_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Der Löschvorgang für den Kostenposten wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				}
				$cost = $this->costs->find( $id );
				if ( ! $cost ) {
					$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Der gewünschte Kostenposten wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				}
				$year = (int) $cost->entry_year;
				if ( $this->costs->is_statement_locked( $year ) ) {
					$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				}
				$this->costs->delete_assignments_for_entry( $id );
				$this->costs->delete( $id );
				$this->redirect_with_notice( 'kgvvm-costs', 'success', __( 'Kostenposten wurde gelöscht.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				break;
			case 'export_readings_csv':
				$this->export_readings_csv();
				break;
			case 'export_plugin_data':
				$this->require_cap( Roles::SETTINGS_CAP );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_export_plugin_data' ) ) {
					$this->redirect_with_notice( 'kgvvm-settings', 'error', __( 'Export wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->data_transfer->send_download();
				break;
			case 'delete_work_job':
				$this->require_cap( 'manage_kleingarten' );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_work_job_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Der Löschvorgang wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
				}
				$this->work->delete_job( $id );
				$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitsart wurde gelöscht.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
				break;
			case 'delete_work_log':
				$this->require_cap( 'manage_kleingarten' );
				$year = absint( isset( $_GET['year'] ) ? $_GET['year'] : current_time( 'Y' ) );
				if ( ! wp_verify_nonce( $nonce, 'kgvvm_delete_work_log_' . $id ) ) {
					$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Der Löschvorgang wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				}
				$this->work->delete_log( $id );
				$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitseintrag wurde gelöscht.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
				break;
		}
	}

	/**
	 * Save section.
	 *
	 * @return void
	 */
	private function save_section() {
		$this->require_cap( 'edit_sparten' );
		check_admin_referer( 'kgvvm_save_section' );

		$id     = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$data   = $this->validator->sanitize_section( $_POST );
		$errors = $this->validator->validate_section( $data );

		if ( $this->sections->name_exists( $data['name'], $id ) ) {
			$errors->add( 'duplicate_name', __( 'Es existiert bereits eine Sparte mit diesem Namen.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-sparten', 'error', $errors->get_error_message(), array( 'view' => 'form', 'id' => $id ) );
		}

		$section_id = $this->sections->save( $data, $id );
		$main_ids   = isset( $_POST['main_meter_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['main_meter_ids'] ) ) : array();

		$this->meters->sync_main_meters_for_section( $section_id, $main_ids );
		$this->redirect_with_notice( 'kgvvm-sparten', 'success', __( 'Sparte wurde gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Save parcel.
	 *
	 * @return void
	 */
	private function save_parcel() {
		$this->require_cap( 'edit_parzellen' );
		check_admin_referer( 'kgvvm_save_parcel' );

		$id     = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$result = $this->parcel_service->save( $id, $_POST );

		if ( is_wp_error( $result ) ) {
			$this->redirect_with_notice( 'kgvvm-parzellen', 'error', $result->get_error_message(), array( 'view' => 'form', 'id' => $id ) );
		}

		$this->redirect_with_notice( 'kgvvm-parzellen', 'success', __( 'Parzelle wurde gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Save meter.
	 *
	 * @return void
	 */
	private function save_meter() {
		$this->require_cap( 'edit_zaehler' );
		check_admin_referer( 'kgvvm_save_meter' );

		$id     = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$data   = $this->validator->sanitize_meter( $_POST );
		$errors = $this->validator->validate_meter( $data );

		if ( $this->meters->meter_number_exists( $data['meter_number'], $data['type'], $id ) ) {
			$errors->add( 'duplicate_meter_number', __( 'Diese Zählernummer existiert für den gewählten Typ bereits.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-zaehler', 'error', $errors->get_error_message(), array( 'view' => 'form', 'id' => $id ) );
		}

		$this->meters->save( $data, $id );
		$this->redirect_with_notice( 'kgvvm-zaehler', 'success', __( 'Zähler wurde gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Swap a meter and archive the old one.
	 *
	 * @return void
	 */
	private function swap_meter() {
		$this->require_cap( 'edit_zaehler' );
		check_admin_referer( 'kgvvm_swap_meter' );

		$id        = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$old_meter = $id ? $this->meters->find( $id ) : null;

		if ( ! $old_meter ) {
			$this->redirect_with_notice( 'kgvvm-zaehler', 'error', __( 'Der gewünschte Zähler wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ) );
		}

		$swap_note = sanitize_textarea_field( wp_unslash( isset( $_POST['swap_note'] ) ? $_POST['swap_note'] : '' ) );
		$new_data  = $this->validator->sanitize_meter(
			array(
				'type'             => $old_meter->type,
				'meter_number'     => isset( $_POST['new_meter_number'] ) ? $_POST['new_meter_number'] : '',
				'section_id'       => $old_meter->section_id,
				'installed_at'     => isset( $_POST['swap_date'] ) ? $_POST['swap_date'] : '',
				'calibration_year' => isset( $_POST['new_calibration_year'] ) ? $_POST['new_calibration_year'] : '',
				'is_main_meter'    => ! empty( $old_meter->is_main_meter ) ? 1 : 0,
				'is_active'        => 1,
				'note'             => $swap_note,
			)
		);
		$errors    = $this->validator->validate_meter( $new_data );

		if ( $this->meters->meter_number_exists( $new_data['meter_number'], $new_data['type'] ) ) {
			$errors->add( 'duplicate_meter_number', __( 'Die neue Zählernummer existiert für diesen Typ bereits.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( '' === $new_data['installed_at'] ) {
			$errors->add( 'swap_date_required', __( 'Bitte ein Tauschdatum angeben.', KGVVM_TEXT_DOMAIN ) );
		}

		$parcel_id           = (int) $old_meter->parcel_id;
		$latest              = $this->readings->get_latest_for_meter( $old_meter->id );
		$old_final_raw       = isset( $_POST['old_final_reading'] ) ? str_replace( ',', '.', wp_unslash( $_POST['old_final_reading'] ) ) : '';
		$new_initial_raw     = isset( $_POST['new_initial_reading'] ) ? str_replace( ',', '.', wp_unslash( $_POST['new_initial_reading'] ) ) : '';
		$old_final_reading   = '' === trim( (string) $old_final_raw ) ? '' : (float) $old_final_raw;
		$new_initial_reading = '' === trim( (string) $new_initial_raw ) ? '' : (float) $new_initial_raw;

		if ( '' !== trim( (string) $old_final_raw ) ) {
			if ( ! is_numeric( $old_final_raw ) ) {
				$errors->add( 'old_final_invalid', __( 'Bitte einen gültigen Endstand für den alten Zähler eingeben.', KGVVM_TEXT_DOMAIN ) );
			} elseif ( (float) $old_final_reading < 0 ) {
				$errors->add( 'old_final_negative', __( 'Der Endstand des alten Zählers darf nicht negativ sein.', KGVVM_TEXT_DOMAIN ) );
			}
		}

		if ( $parcel_id > 0 ) {
			if ( '' === trim( (string) $new_initial_raw ) ) {
				$errors->add( 'new_initial_required', __( 'Bitte einen Startstand für den neuen Zähler eingeben.', KGVVM_TEXT_DOMAIN ) );
			} elseif ( ! is_numeric( $new_initial_raw ) ) {
				$errors->add( 'new_initial_invalid', __( 'Bitte einen gültigen Startstand für den neuen Zähler eingeben.', KGVVM_TEXT_DOMAIN ) );
			} elseif ( (float) $new_initial_reading < 0 ) {
				$errors->add( 'new_initial_negative', __( 'Der Startstand des neuen Zählers darf nicht negativ sein.', KGVVM_TEXT_DOMAIN ) );
			}
		}

		if ( $latest && $new_data['installed_at'] && strtotime( $new_data['installed_at'] ) < strtotime( $latest->reading_date ) ) {
			$errors->add( 'swap_date_older', __( 'Das Tauschdatum darf nicht vor der letzten gespeicherten Ablesung liegen.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $latest && '' !== $old_final_reading && (float) $old_final_reading < (float) $latest->reading_value ) {
			$errors->add( 'swap_final_low', __( 'Der Endstand des alten Zählers darf nicht kleiner als die letzte Ablesung sein.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-zaehler', 'error', $errors->get_error_message(), array( 'id' => $id, 'open_swap' => 1 ) );
		}

		$swap_label       = wp_date( 'd.m.Y', strtotime( $new_data['installed_at'] ) );
		$new_data['note'] = trim(
			implode(
				"\n\n",
				array_filter(
					array(
						sprintf( __( 'Ersatzzähler für %1$s seit %2$s.', KGVVM_TEXT_DOMAIN ), $old_meter->meter_number, $swap_label ),
						$swap_note,
					)
				)
			)
		);

		$new_meter_id = $this->meters->save( $new_data, 0 );

		if ( ! $new_meter_id ) {
			$this->redirect_with_notice( 'kgvvm-zaehler', 'error', __( 'Der neue Zähler konnte nicht angelegt werden.', KGVVM_TEXT_DOMAIN ), array( 'id' => $id, 'open_swap' => 1 ) );
		}

		if ( $parcel_id > 0 && '' !== $old_final_reading ) {
			$this->readings->save(
				array(
					'meter_id'      => $old_meter->id,
					'parcel_id'     => $parcel_id,
					'reading_value' => $old_final_reading,
					'reading_date'  => $new_data['installed_at'],
					'note'          => __( 'Abschlussstand beim Zählerwechsel.', KGVVM_TEXT_DOMAIN ),
					'submitted_by'  => get_current_user_id(),
				)
			);
		}

		$this->meters->release_meter( $old_meter->id );
		$this->meters->save(
			array(
				'type'             => $old_meter->type,
				'meter_number'     => $old_meter->meter_number,
				'section_id'       => $old_meter->section_id,
				'installed_at'     => $old_meter->installed_at,
				'calibration_year' => ! empty( $old_meter->calibration_year ) ? (int) $old_meter->calibration_year : null,
				'is_active'        => 0,
				'note'             => trim(
					implode(
						"\n\n",
						array_filter(
							array(
								$old_meter->note,
								sprintf( __( 'Zählerwechsel am %1$s. Neuer Zähler: %2$s.', KGVVM_TEXT_DOMAIN ), $swap_label, $new_data['meter_number'] ),
								$swap_note ? sprintf( __( 'Tauschhinweis: %s', KGVVM_TEXT_DOMAIN ), $swap_note ) : '',
							)
						)
					)
				),
			),
			$old_meter->id
		);

		if ( $parcel_id > 0 ) {
			$this->meters->assign_to_parcel( $new_meter_id, $parcel_id );
			$this->readings->save(
				array(
					'meter_id'      => $new_meter_id,
					'parcel_id'     => $parcel_id,
					'reading_value' => $new_initial_reading,
					'reading_date'  => $new_data['installed_at'],
					'note'          => __( 'Startstand nach Zählerwechsel.', KGVVM_TEXT_DOMAIN ),
					'submitted_by'  => get_current_user_id(),
				)
			);
		}

		$this->redirect_with_notice( 'kgvvm-zaehler', 'success', __( 'Der Zähler wurde erfolgreich getauscht.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Save tenant.
	 *
	 * @return void
	 */
	private function save_tenant() {
		$this->require_cap( 'edit_paechter' );
		check_admin_referer( 'kgvvm_save_tenant' );

		$id     = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$data   = $this->validator->sanitize_tenant( $_POST );
		$errors = $this->validator->validate_tenant( $data );

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-paechter', 'error', $errors->get_error_message(), array( 'view' => 'form', 'id' => $id ) );
		}

		$this->tenants->save( $data, $id );
		$this->redirect_with_notice( 'kgvvm-paechter', 'success', __( 'Pächter wurde gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Save a meter reading from a member or administrator.
	 *
	 * @return void
	 */
	private function save_meter_reading() {
		if ( ! current_user_can( 'submit_meter_readings' ) && ! current_user_can( 'manage_kleingarten' ) ) {
			wp_die( esc_html__( 'Sie haben keine Berechtigung für diese Aktion.', KGVVM_TEXT_DOMAIN ) );
		}

		check_admin_referer( 'kgvvm_save_meter_reading' );

		$return_page = isset( $_POST['return_page'] ) ? sanitize_key( wp_unslash( $_POST['return_page'] ) ) : 'kgvvm-my-parcels';
		$return_view = isset( $_POST['return_view'] ) ? sanitize_key( wp_unslash( $_POST['return_view'] ) ) : '';
		$return_id   = absint( isset( $_POST['return_id'] ) ? $_POST['return_id'] : 0 );
		$return_args = array();

		if ( ! in_array( $return_page, array( 'kgvvm-my-parcels', 'kgvvm-sparten', 'kgvvm-zaehler', 'kgvvm-consumption' ), true ) ) {
			$return_page = 'kgvvm-my-parcels';
		}

		if ( $return_view ) {
			$return_args['view'] = $return_view;
		}

		if ( $return_id > 0 ) {
			$return_args['id'] = $return_id;
		}

		$data   = $this->validator->sanitize_meter_reading( $_POST );
		$errors = $this->validator->validate_meter_reading( $data );
		$meter  = $this->meters->find( $data['meter_id'] );

		if ( ! current_user_can( 'manage_kleingarten' ) && ! $this->assignments->user_has_parcel( get_current_user_id(), $data['parcel_id'] ) ) {
			$errors->add( 'forbidden_parcel', __( 'Sie dürfen nur Ablesungen für Ihre zugewiesenen Parzellen erfassen.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( ! $meter || (int) $meter->parcel_id !== (int) $data['parcel_id'] ) {
			$errors->add( 'invalid_meter_link', __( 'Der ausgewählte Zähler ist dieser Parzelle nicht zugeordnet.', KGVVM_TEXT_DOMAIN ) );
		}

		$latest = $meter ? $this->readings->get_latest_for_meter( $data['meter_id'] ) : null;

		if ( $latest && $data['reading_date'] && strtotime( $data['reading_date'] ) < strtotime( $latest->reading_date ) ) {
			$errors->add( 'reading_date_older', __( 'Das Ablesedatum darf nicht vor der letzten gespeicherten Ablesung liegen.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $latest && (float) $data['reading_value'] < (float) $latest->reading_value ) {
			$errors->add( 'reading_too_low', __( 'Der neue Zählerstand darf nicht kleiner als die letzte erfasste Ablesung sein.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( $return_page, 'error', $errors->get_error_message(), $return_args );
		}

		$data['submitted_by']    = get_current_user_id();
		$data['is_self_reading'] = ( ! current_user_can( 'manage_kleingarten' ) && current_user_can( 'submit_meter_readings' ) ) ? 1 : 0;
		$warning_message         = $this->build_meter_jump_warning_message( $meter, $latest, (float) $data['reading_value'] );
		$this->readings->save( $data );

		if ( $warning_message ) {
			$this->redirect_with_notice( $return_page, 'warning', sprintf( __( 'Die Ablesung wurde gespeichert. %s', KGVVM_TEXT_DOMAIN ), $warning_message ), $return_args );
		}

		$this->redirect_with_notice( $return_page, 'success', __( 'Die Ablesung wurde gespeichert.', KGVVM_TEXT_DOMAIN ), $return_args );
	}

	/**
	 * Correct an existing meter reading (admin only).
	 *
	 * @return void
	 */
	private function correct_meter_reading() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_correct_meter_reading' );

		$id    = absint( isset( $_POST['reading_id'] ) ? $_POST['reading_id'] : 0 );
		$value = isset( $_POST['reading_value'] ) ? (float) wp_unslash( $_POST['reading_value'] ) : null;
		$date  = isset( $_POST['reading_date'] ) ? sanitize_text_field( wp_unslash( $_POST['reading_date'] ) ) : '';
		$note  = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : '';

		$return_args = array_filter(
			array(
				'section_id' => absint( isset( $_POST['section_id'] ) ? $_POST['section_id'] : 0 ),
				'date_from'  => sanitize_text_field( wp_unslash( isset( $_POST['date_from'] ) ? $_POST['date_from'] : '' ) ),
				'date_to'    => sanitize_text_field( wp_unslash( isset( $_POST['date_to'] ) ? $_POST['date_to'] : '' ) ),
				'order'      => sanitize_key( wp_unslash( isset( $_POST['order'] ) ? $_POST['order'] : 'DESC' ) ),
			),
			static function( $v ) { return '' !== $v && 0 !== $v; }
		);

		$reading = $id ? $this->readings->find( $id ) : null;

		if ( ! $reading ) {
			$this->redirect_with_notice( 'kgvvm-consumption', 'error', __( 'Die Ablesung wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ), $return_args );
		}

		if ( null === $value || '' === $date ) {
			$this->redirect_with_notice( 'kgvvm-consumption', 'error', __( 'Bitte Zählerstand und Datum angeben.', KGVVM_TEXT_DOMAIN ), $return_args );
		}

		$this->readings->update_reading(
			$id,
			array(
				'reading_value' => $value,
				'reading_date'  => $date,
				'note'          => $note,
			)
		);

		$this->redirect_with_notice( 'kgvvm-consumption', 'success', __( 'Die Ablesung wurde korrigiert.', KGVVM_TEXT_DOMAIN ), $return_args );
	}

	/**
	 * Save general settings.
	 *
	 * @return void
	 */
	private function save_settings() {
		$this->require_cap( Roles::SETTINGS_CAP );
		check_admin_referer( 'kgvvm_save_settings' );

		$current_settings = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );
		$water_threshold  = isset( $_POST['water_usage_alert_threshold'] ) ? (float) str_replace( ',', '.', wp_unslash( $_POST['water_usage_alert_threshold'] ) ) : 25;
		$power_threshold  = isset( $_POST['power_usage_alert_threshold'] ) ? (float) str_replace( ',', '.', wp_unslash( $_POST['power_usage_alert_threshold'] ) ) : 1000;
		$power_unit       = isset( $_POST['power_unit'] ) ? sanitize_key( wp_unslash( $_POST['power_unit'] ) ) : 'kwh';
		$power_unit       = in_array( $power_unit, array( 'kwh', 'mwh' ), true ) ? $power_unit : 'kwh';
		$pdf_club_name    = isset( $_POST['pdf_club_name'] ) ? sanitize_text_field( wp_unslash( $_POST['pdf_club_name'] ) ) : $current_settings['pdf_club_name'];
		$pdf_logo_url     = isset( $_POST['pdf_logo_url'] ) ? esc_url_raw( trim( wp_unslash( $_POST['pdf_logo_url'] ) ) ) : '';
		$pdf_contact      = isset( $_POST['pdf_contact_block'] ) ? sanitize_textarea_field( wp_unslash( $_POST['pdf_contact_block'] ) ) : '';
		$pdf_intro_text   = isset( $_POST['pdf_intro_text'] ) ? sanitize_textarea_field( wp_unslash( $_POST['pdf_intro_text'] ) ) : '';
		$pdf_footer_text  = isset( $_POST['pdf_footer_text'] ) ? sanitize_textarea_field( wp_unslash( $_POST['pdf_footer_text'] ) ) : '';

		update_option(
			'kgvvm_settings',
			array_merge(
				$current_settings,
				array(
					'allow_multiple_member_parcels' => ! empty( $_POST['allow_multiple_member_parcels'] ) ? 1 : 0,
					'water_usage_alert_threshold'   => max( 0, $water_threshold ),
					'power_usage_alert_threshold'   => max( 0, $power_threshold ),
					'power_unit'                    => $power_unit,
					'pdf_club_name'                 => $pdf_club_name,
					'pdf_logo_url'                  => $pdf_logo_url,
					'pdf_contact_block'             => $pdf_contact,
					'pdf_intro_text'                => $pdf_intro_text,
					'pdf_footer_text'               => $pdf_footer_text,
				)
			),
			false
		);

		$this->redirect_with_notice( 'kgvvm-settings', 'success', __( 'Einstellungen wurden gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Handle plugin data import from an uploaded JSON file.
	 *
	 * @return void
	 */
	private function import_plugin_data() {
		$this->require_cap( Roles::SETTINGS_CAP );
		check_admin_referer( 'kgvvm_import_plugin_data' );

		if ( empty( $_FILES['kgvvm_import_file']['tmp_name'] ) ) {
			$this->redirect_with_notice( 'kgvvm-settings', 'error', __( 'Bitte eine JSON-Datei auswählen.', KGVVM_TEXT_DOMAIN ) );
		}

		$file = $_FILES['kgvvm_import_file'];

		// Validate MIME type.
		$finfo    = new \finfo( FILEINFO_MIME_TYPE );
		$mimetype = $finfo->file( $file['tmp_name'] );
		if ( ! in_array( $mimetype, array( 'application/json', 'text/plain' ), true ) ) {
			$this->redirect_with_notice( 'kgvvm-settings', 'error', __( 'Die hochgeladene Datei ist keine gültige JSON-Datei.', KGVVM_TEXT_DOMAIN ) );
		}

		$raw = file_get_contents( $file['tmp_name'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
		if ( false === $raw ) {
			$this->redirect_with_notice( 'kgvvm-settings', 'error', __( 'Die Datei konnte nicht gelesen werden.', KGVVM_TEXT_DOMAIN ) );
		}

		$payload = json_decode( $raw, true );
		if ( ! is_array( $payload ) ) {
			$this->redirect_with_notice( 'kgvvm-settings', 'error', __( 'Die JSON-Datei konnte nicht verarbeitet werden. Bitte eine gültige Exportdatei hochladen.', KGVVM_TEXT_DOMAIN ) );
		}

		$result = $this->data_transfer->import( $payload );

		if ( ! empty( $result['errors'] ) ) {
			$this->redirect_with_notice( 'kgvvm-settings', 'error', implode( ' | ', $result['errors'] ) );
		}

		$summary = array();
		foreach ( $result['imported'] as $key => $count ) {
			$summary[] = $key . ': ' . $count;
		}
		$this->redirect_with_notice(
			'kgvvm-settings',
			'success',
			sprintf(
				/* translators: %s: summary of imported rows per table */
				__( 'Import erfolgreich. Importierte Datensätze – %s', KGVVM_TEXT_DOMAIN ),
				implode( ', ', $summary )
			)
		);
	}

	/**
	 * Persist one year for the cost dropdown.
	 *
	 * @return void
	 */
	private function save_cost_year() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_cost_year' );

		$entry_year = $this->validator->sanitize_cost_year( $_POST );
		$errors     = $this->validator->validate_cost_year( $entry_year );

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', $errors->get_error_message(), array( 'year' => current_time( 'Y' ) ) );
		}

		if ( ! $this->costs->save_year( $entry_year ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Das Jahr konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $entry_year ) );
		}

		$this->redirect_with_notice( 'kgvvm-costs', 'success', __( 'Das Jahr wurde zur Auswahlliste hinzugefügt.', KGVVM_TEXT_DOMAIN ), array( 'year' => $entry_year ) );
	}

	/**
	 * Save section-specific yearly prices.
	 *
	 * @return void
	 */
	private function save_cost_prices() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_cost_prices' );

		$data   = $this->validator->sanitize_cost_year_settings( $_POST );
		$errors = $this->validator->validate_cost_year_settings( $data );

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', $errors->get_error_message(), array( 'year' => $data['entry_year'] ) );
		}

		if ( $this->costs->is_statement_locked( (int) $data['entry_year'] ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'] ) );
		}

		if ( ! $this->costs->save_section_prices( $data ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Preise für diese Sparte konnten nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'] ) );
		}

		$this->redirect_with_notice( 'kgvvm-costs', 'success', __( 'Die Preise pro Sparte wurden gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'] ) );
	}

	/**
	 * Save one annual cost entry.
	 *
	 * @return void
	 */
	private function save_cost() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_cost' );

		$id     = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$data   = $this->validator->sanitize_cost_entry( $_POST );
		$errors = $this->validator->validate_cost_entry( $data );
		$existing = $id > 0 ? $this->costs->find( $id ) : null;

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', $errors->get_error_message(), array( 'year' => $data['entry_year'], 'id' => $id ) );
		}

		if ( $existing && $this->costs->is_statement_locked( (int) $existing->entry_year ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ), array( 'year' => (int) $existing->entry_year, 'id' => $id ) );
		}

		if ( $this->costs->is_statement_locked( (int) $data['entry_year'] ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'], 'id' => $id ) );
		}

		$active_parcels = array_values(
			array_filter(
				$this->parcels->search(),
				static function( $parcel ) {
					return isset( $parcel->status ) && 'inactive' !== $parcel->status;
				}
			)
		);
		$member_count   = count( $this->assignments->get_member_users() );
		$multiplier     = 'member' === $data['distribution_type'] ? $member_count : count( $active_parcels );
		$data['total_cost'] = (float) $data['unit_amount'] * max( 0, $multiplier );

		$saved = $this->costs->save( $data, $id );

		if ( ! $saved ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Der Kostenposten konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'], 'id' => $id ) );
		}

		$this->redirect_with_notice( 'kgvvm-costs', 'success', __( 'Kostenposten wurde gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $data['entry_year'] ) );
	}

	/**
	 * Add or remove a cost entry assignment for a specific parcel.
	 *
	 * @return void
	 */
	private function toggle_parcel_cost_assignment() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_toggle_parcel_cost' );

		$parcel_id     = absint( isset( $_POST['parcel_id'] ) ? $_POST['parcel_id'] : 0 );
		$cost_entry_id = absint( isset( $_POST['cost_entry_id'] ) ? $_POST['cost_entry_id'] : 0 );
		$mode          = isset( $_POST['assignment_mode'] ) ? sanitize_key( wp_unslash( $_POST['assignment_mode'] ) ) : '';
		$year          = absint( isset( $_POST['year'] ) ? $_POST['year'] : 0 );

		if ( $parcel_id < 1 || $cost_entry_id < 1 || ! in_array( $mode, array( 'add', 'remove' ), true ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Ungültige Anfrage.', KGVVM_TEXT_DOMAIN ), array( 'view' => 'statement', 'statement_type' => 'parcel', 'subject_id' => $parcel_id, 'year' => $year ) );
		}

		if ( $this->costs->is_statement_locked( $year ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ), array( 'view' => 'statement', 'statement_type' => 'parcel', 'subject_id' => $parcel_id, 'year' => $year ) );
		}

		if ( 'add' === $mode ) {
			$this->costs->assign_to_parcel( $parcel_id, $cost_entry_id );
		} else {
			$this->costs->unassign_from_parcel( $parcel_id, $cost_entry_id );
		}

		wp_safe_redirect( $this->admin_url( 'kgvvm-costs', array( 'view' => 'statement', 'statement_type' => 'parcel', 'subject_id' => $parcel_id, 'year' => $year ) ) );
		exit;
	}

	/**
	 * Lock or unlock annual statement changes for one year.
	 *
	 * @return void
	 */
	private function set_statement_lock() {
		$this->require_cap( 'manage_kleingarten' );
		$year           = absint( isset( $_POST['year'] ) ? $_POST['year'] : current_time( 'Y' ) );
		$statement_type = isset( $_POST['statement_type'] ) ? sanitize_key( wp_unslash( $_POST['statement_type'] ) ) : 'parcel';
		$subject_id     = absint( isset( $_POST['subject_id'] ) ? $_POST['subject_id'] : 0 );
		$mode           = isset( $_POST['lock_mode'] ) ? sanitize_key( wp_unslash( $_POST['lock_mode'] ) ) : '';

		check_admin_referer( 'kgvvm_set_statement_lock_' . $year );

		if ( $year < 1 || ! in_array( $mode, array( 'lock', 'unlock' ), true ) ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Ungültige Anfrage.', KGVVM_TEXT_DOMAIN ), array( 'view' => 'statement', 'statement_type' => $statement_type, 'subject_id' => $subject_id, 'year' => $year ) );
		}

		$is_locked = 'lock' === $mode;
		$saved     = $this->costs->set_statement_lock( $year, $is_locked, get_current_user_id() );

		if ( ! $saved ) {
			$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Der Sperrstatus der Jahresabrechnung konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ), array( 'view' => 'statement', 'statement_type' => $statement_type, 'subject_id' => $subject_id, 'year' => $year ) );
		}

		$this->redirect_with_notice(
			'kgvvm-costs',
			'success',
			$is_locked ? __( 'Jahresabrechnung wurde festgeschrieben.', KGVVM_TEXT_DOMAIN ) : __( 'Jahresabrechnung wurde erneut freigegeben.', KGVVM_TEXT_DOMAIN ),
			array( 'view' => 'statement', 'statement_type' => $statement_type, 'subject_id' => $subject_id, 'year' => $year )
		);
	}

	/**
	 * Save one inventory item.
	 *
	 * @return void
	 */
	private function save_inventory_item() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_inventory_item' );

		$id      = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$data    = $this->validator->sanitize_inventory_item( $_POST );
		$errors  = $this->validator->validate_inventory_item( $data );
		$existing = $id > 0 ? $this->inventory->find( $id ) : null;

		if ( $existing ) {
			$borrowed_qty = $this->inventory->get_open_borrowed_quantity( $id );

			if ( (int) $data['total_quantity'] < $borrowed_qty ) {
				$errors->add( 'inventory_total_too_low', __( 'Die Gesamtmenge ist kleiner als aktuell ausgeliehen. Bitte zuerst Rückgaben erfassen.', KGVVM_TEXT_DOMAIN ) );
			}
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', $errors->get_error_message(), array( 'id' => $id ) );
		}

		$saved = $this->inventory->save_item( $data, $id );

		if ( ! $saved ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Der Inventargegenstand konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ), array( 'id' => $id ) );
		}

		$this->redirect_with_notice( 'kgvvm-inventory', 'success', __( 'Inventargegenstand wurde gespeichert.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Borrow inventory item for one member.
	 *
	 * @return void
	 */
	private function borrow_inventory_item() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_borrow_inventory_item' );

		$data   = $this->validator->sanitize_inventory_loan( $_POST );
		$errors = $this->validator->validate_inventory_loan( $data );

		$item = $this->inventory->find( $data['item_id'] );

		if ( ! $item ) {
			$errors->add( 'inventory_item_missing', __( 'Der gewählte Inventargegenstand wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ) );
		} elseif ( ! (bool) $item->is_active ) {
			$errors->add( 'inventory_item_inactive', __( 'Der gewählte Inventargegenstand ist inaktiv.', KGVVM_TEXT_DOMAIN ) );
		} elseif ( (int) $data['quantity'] > (int) $item->available_quantity ) {
			$errors->add( 'inventory_item_not_available', __( 'Die gewünschte Menge ist nicht verfügbar.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( $errors->has_errors() ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', $errors->get_error_message() );
		}

		$saved = $this->inventory->borrow_item( $data['item_id'], $data['user_id'], $data['quantity'], $data['due_date'], $data['note'] );

		if ( ! $saved ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Die Ausleihe konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ) );
		}

		$this->redirect_with_notice( 'kgvvm-inventory', 'success', __( 'Ausleihe wurde erfasst.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Return one inventory loan.
	 *
	 * @return void
	 */
	private function return_inventory_loan() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_return_inventory_loan' );

		$loan_id      = absint( isset( $_POST['loan_id'] ) ? $_POST['loan_id'] : 0 );
		$return_note  = sanitize_textarea_field( wp_unslash( isset( $_POST['return_note'] ) ? $_POST['return_note'] : '' ) );

		if ( $loan_id < 1 ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Ungültige Anfrage.', KGVVM_TEXT_DOMAIN ) );
		}

		$saved = $this->inventory->return_loan( $loan_id, $return_note );

		if ( ! $saved ) {
			$this->redirect_with_notice( 'kgvvm-inventory', 'error', __( 'Die Rückgabe konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ) );
		}

		$this->redirect_with_notice( 'kgvvm-inventory', 'success', __( 'Rückgabe wurde erfasst.', KGVVM_TEXT_DOMAIN ) );
	}

	/**
	 * Render inventory management page.
	 *
	 * @return void
	 */
	public function render_inventory_page() {
		$this->require_cap( 'manage_kleingarten' );

		$search  = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
		$edit_id = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$item    = $edit_id > 0 ? $this->inventory->find( $edit_id ) : null;

		$items       = $this->inventory->search_items( array( 's' => $search, 'orderby' => isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'name', 'order' => isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'ASC' ) );
		$open_loans  = $this->inventory->get_open_loans();
		$recent_loans = $this->inventory->get_recent_loans( 20 );

		$member_query = new \WP_User_Query(
			array(
				'role'    => Roles::MEMBER_ROLE,
				'fields'  => array( 'ID', 'display_name', 'user_email' ),
				'orderby' => 'display_name',
				'order'   => 'ASC',
				'number'  => 2000,
			)
		);

		$members = (array) $member_query->get_results();

		if ( empty( $members ) ) {
			$members = (array) $this->assignments->get_member_users();
		}

		$summary_total_items     = count( $items );
		$summary_available_total = 0;
		$summary_open_total      = count( $open_loans );

		foreach ( $items as $row ) {
			$summary_available_total += max( 0, (int) $row->available_quantity );
		}
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Inventar', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php if ( $item ) : ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-inventory' ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neuer Gegenstand', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php endif; ?>
			<?php $this->render_notice(); ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-inventory' />
				<div class='kgvvm-filters'>
					<input type='search' name='s' value='<?php echo esc_attr( $search ); ?>' placeholder='<?php echo esc_attr__( 'Inventar suchen ...', KGVVM_TEXT_DOMAIN ); ?>' />
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( number_format_i18n( $summary_total_items, 0 ) ); ?></h2>
					<p><?php echo esc_html__( 'Inventargegenstände', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( number_format_i18n( $summary_available_total, 0 ) ); ?></h2>
					<p><?php echo esc_html__( 'Verfügbar gesamt', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( number_format_i18n( $summary_open_total, 0 ) ); ?></h2>
					<p><?php echo esc_html__( 'Offene Ausleihen', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
			</div>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $item ? __( 'Gegenstand bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Gegenstand anlegen', KGVVM_TEXT_DOMAIN ) ); ?></h2>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_save_inventory_item' ); ?>
						<input type='hidden' name='kgvvm_action' value='save_inventory_item' />
						<input type='hidden' name='id' value='<?php echo esc_attr( $item ? $item->id : 0 ); ?>' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-item-name'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='name' id='kgvvm-item-name' type='text' class='regular-text' required value='<?php echo esc_attr( $item ? $item->name : '' ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-item-total'><?php echo esc_html__( 'Gesamtmenge', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='total_quantity' id='kgvvm-item-total' type='number' min='0' required value='<?php echo esc_attr( $item ? (int) $item->total_quantity : 1 ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-item-available'><?php echo esc_html__( 'Verfügbar', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='available_quantity' id='kgvvm-item-available' type='number' min='0' required value='<?php echo esc_attr( $item ? (int) $item->available_quantity : 1 ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-item-location'><?php echo esc_html__( 'Lagerort', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='storage_location' id='kgvvm-item-location' type='text' class='regular-text' value='<?php echo esc_attr( $item ? $item->storage_location : '' ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-item-description'><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><textarea name='description' id='kgvvm-item-description' rows='3' class='large-text'><?php echo esc_textarea( $item ? $item->description : '' ); ?></textarea></td>
							</tr>
							<tr>
								<th scope='row'><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
								<td>
									<label><input type='hidden' name='is_active' value='0' /><input type='checkbox' name='is_active' value='1' <?php checked( $item ? (bool) $item->is_active : true ); ?> /> <?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></label>
								</td>
							</tr>
						</table>
						<?php submit_button( $item ? __( 'Gegenstand aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Gegenstand speichern', KGVVM_TEXT_DOMAIN ) ); ?>
					</form>
				</div>

				<div class='kgvvm-card'>
					<h2><?php echo esc_html__( 'Ausleihe erfassen', KGVVM_TEXT_DOMAIN ); ?></h2>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_borrow_inventory_item' ); ?>
						<input type='hidden' name='kgvvm_action' value='borrow_inventory_item' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-loan-item'><?php echo esc_html__( 'Gegenstand', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='item_id' id='kgvvm-loan-item' required>
										<option value=''><?php echo esc_html__( 'Bitte wählen', KGVVM_TEXT_DOMAIN ); ?></option>
										<?php foreach ( $items as $inventory_item ) : ?>
											<?php if ( ! (bool) $inventory_item->is_active ) : ?>
												<?php continue; ?>
											<?php endif; ?>
											<option value='<?php echo esc_attr( $inventory_item->id ); ?>'><?php echo esc_html( $inventory_item->name . ' (' . (int) $inventory_item->available_quantity . ' verfügbar)' ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-loan-member'><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='user_id' id='kgvvm-loan-member' required>
										<option value=''><?php echo esc_html__( 'Bitte wählen', KGVVM_TEXT_DOMAIN ); ?></option>
										<?php foreach ( $members as $member ) : ?>
											<option value='<?php echo esc_attr( $member->ID ); ?>'><?php echo esc_html( $member->display_name ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-loan-quantity'><?php echo esc_html__( 'Menge', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='quantity' id='kgvvm-loan-quantity' type='number' min='1' required value='1' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-loan-due'><?php echo esc_html__( 'Rückgabe geplant', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='due_date' id='kgvvm-loan-due' type='date' value='' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-loan-note'><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><textarea name='note' id='kgvvm-loan-note' rows='3' class='large-text'></textarea></td>
							</tr>
						</table>
						<?php submit_button( __( 'Ausleihe speichern', KGVVM_TEXT_DOMAIN ), 'secondary' ); ?>
					</form>
				</div>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Inventarliste', KGVVM_TEXT_DOMAIN ); ?></h2>
				<table class='widefat striped'>
					<thead>
						<tr>
							<th><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Lagerort', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Gesamt', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Verfügbar', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php if ( empty( $items ) ) : ?>
							<tr><td colspan='6'><?php echo esc_html__( 'Keine Inventargegenstände gefunden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
						<?php else : ?>
							<?php foreach ( $items as $row ) : ?>
								<tr>
									<td><strong><?php echo esc_html( $row->name ); ?></strong></td>
									<td><?php echo esc_html( ! empty( $row->storage_location ) ? $row->storage_location : '—' ); ?></td>
									<td><?php echo esc_html( number_format_i18n( (int) $row->total_quantity, 0 ) ); ?></td>
									<td><?php echo esc_html( number_format_i18n( (int) $row->available_quantity, 0 ) ); ?></td>
									<td><?php echo esc_html( (bool) $row->is_active ? __( 'Aktiv', KGVVM_TEXT_DOMAIN ) : __( 'Inaktiv', KGVVM_TEXT_DOMAIN ) ); ?></td>
									<td>
										<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-inventory', array( 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
										|
										<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-inventory', array( 'kgvvm_action' => 'delete_inventory_item', 'id' => $row->id ) ), 'kgvvm_delete_inventory_item_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Inventargegenstand wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
									</td>
								</tr>
							<?php endforeach; ?>
						<?php endif; ?>
					</tbody>
				</table>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Offene Ausleihen', KGVVM_TEXT_DOMAIN ); ?></h2>
				<table class='widefat striped'>
					<thead>
						<tr>
							<th><?php echo esc_html__( 'Gegenstand', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Menge', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Ausgeliehen am', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Rückgabe geplant', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Aktion', KGVVM_TEXT_DOMAIN ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php if ( empty( $open_loans ) ) : ?>
							<tr><td colspan='6'><?php echo esc_html__( 'Keine offenen Ausleihen vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
						<?php else : ?>
							<?php foreach ( $open_loans as $loan ) : ?>
								<tr>
									<td><?php echo esc_html( $loan->item_name ? $loan->item_name : '—' ); ?></td>
									<td><?php echo esc_html( $loan->borrower_name ? $loan->borrower_name : '—' ); ?></td>
									<td><?php echo esc_html( number_format_i18n( (int) $loan->borrowed_quantity, 0 ) ); ?></td>
									<td><?php echo esc_html( ! empty( $loan->borrowed_at ) ? wp_date( 'd.m.Y H:i', strtotime( $loan->borrowed_at ) ) : '—' ); ?></td>
									<td><?php echo esc_html( ! empty( $loan->due_date ) ? wp_date( 'd.m.Y', strtotime( $loan->due_date ) ) : '—' ); ?></td>
									<td>
										<form method='post' style='display:flex; gap:6px; align-items:center;'>
											<?php wp_nonce_field( 'kgvvm_return_inventory_loan' ); ?>
											<input type='hidden' name='kgvvm_action' value='return_inventory_loan' />
											<input type='hidden' name='loan_id' value='<?php echo esc_attr( $loan->id ); ?>' />
											<input type='text' name='return_note' value='' placeholder='<?php echo esc_attr__( 'Rückgabe-Notiz', KGVVM_TEXT_DOMAIN ); ?>' />
											<button type='submit' class='button button-small'><?php echo esc_html__( 'Rückgabe', KGVVM_TEXT_DOMAIN ); ?></button>
										</form>
									</td>
								</tr>
							<?php endforeach; ?>
						<?php endif; ?>
					</tbody>
				</table>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Letzte Ausleihvorgänge', KGVVM_TEXT_DOMAIN ); ?></h2>
				<table class='widefat striped'>
					<thead>
						<tr>
							<th><?php echo esc_html__( 'Zeitpunkt', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Gegenstand', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Menge', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php if ( empty( $recent_loans ) ) : ?>
							<tr><td colspan='5'><?php echo esc_html__( 'Noch keine Ausleihen vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
						<?php else : ?>
							<?php foreach ( $recent_loans as $row ) : ?>
								<tr>
									<td><?php echo esc_html( ! empty( $row->borrowed_at ) ? wp_date( 'd.m.Y H:i', strtotime( $row->borrowed_at ) ) : '—' ); ?></td>
									<td><?php echo esc_html( $row->item_name ? $row->item_name : '—' ); ?></td>
									<td><?php echo esc_html( $row->borrower_name ? $row->borrower_name : '—' ); ?></td>
									<td><?php echo esc_html( number_format_i18n( (int) $row->borrowed_quantity, 0 ) ); ?></td>
									<td><?php echo esc_html( 'open' === $row->status ? __( 'Offen', KGVVM_TEXT_DOMAIN ) : __( 'Zurückgegeben', KGVVM_TEXT_DOMAIN ) ); ?></td>
								</tr>
							<?php endforeach; ?>
						<?php endif; ?>
					</tbody>
				</table>
			</div>
		</div>
		<?php
	}

	/**
	 * Render dashboard.
	 *
	 * @return void
	 */
	public function render_dashboard() {
		$this->require_cap( 'manage_kleingarten' );

		$sections_total = count( $this->sections->search() );
		$parcels_total  = count( $this->parcels->search() );
		$meters_free    = count( $this->meters->search( array( 'assignment' => 'free' ) ) );
		$meters_used    = count( $this->meters->search( array( 'assignment' => 'assigned' ) ) );
		$tenants_total  = count( $this->tenants->search() );
		$all_members    = $this->assignments->get_member_users();
		$members_total  = count( $all_members );

		// Arbeitsstunden laufendes Jahr
		$work_year        = (int) current_time( 'Y' );
		$work_config      = $this->work->get_year_config( $work_year );
		$work_summary     = $this->work->get_member_summary( $work_year, $all_members );
		$members_ok       = 0;
		$members_open     = 0;
		$total_missing_h  = 0.0;
		$total_surcharge  = 0.0;
		foreach ( $work_summary as $ws ) {
			if ( $ws->missing_hours > 0 ) {
				$members_open++;
				$total_missing_h += $ws->missing_hours;
				$total_surcharge += $ws->surcharge;
			} else {
				$members_ok++;
			}
		}
		?>
		<div class='wrap'>
			<h1><?php echo esc_html__( 'Kleingartenverwaltung', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $sections_total ); ?></h2>
					<p><?php echo esc_html__( 'Sparten', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $parcels_total ); ?></h2>
					<p><?php echo esc_html__( 'Parzellen', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $meters_free ); ?></h2>
					<p><?php echo esc_html__( 'Freie Zähler', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $meters_used ); ?></h2>
					<p><?php echo esc_html__( 'Zugeordnete Zähler', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $tenants_total ); ?></h2>
					<p><?php echo esc_html__( 'Pächter', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $members_total ); ?></h2>
					<p><?php echo esc_html__( 'WordPress-Mitglieder', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
			</div>

			<?php /* Arbeitsstunden-Widget */ ?>
			<div class='kgvvm-card'>
				<h2><?php echo esc_html( sprintf( __( 'Arbeitsstunden %d', KGVVM_TEXT_DOMAIN ), $work_year ) ); ?></h2>
				<?php if ( ! $work_config ) : ?>
					<p class='kgvvm-help'><?php echo esc_html__( 'Für das laufende Jahr wurden noch keine Jahreseinstellungen für Arbeitsstunden hinterlegt.', KGVVM_TEXT_DOMAIN ); ?>
						<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $work_year ) ) ); ?>'><?php echo esc_html__( 'Jetzt einrichten', KGVVM_TEXT_DOMAIN ); ?></a>
					</p>
				<?php else : ?>
					<div class='kgvvm-grid' style='margin-top:8px;'>
						<div class='kgvvm-card' style='background:#f0faf0;'>
							<h2 style='color:#2e7d32;'><?php echo esc_html( $members_ok ); ?></h2>
							<p><?php echo esc_html__( 'Mitglieder erfüllt', KGVVM_TEXT_DOMAIN ); ?></p>
						</div>
						<div class='kgvvm-card' style='background:#fff4f4;'>
							<h2 style='color:#c00;'><?php echo esc_html( $members_open ); ?></h2>
							<p><?php echo esc_html__( 'Mitglieder mit offenen Stunden', KGVVM_TEXT_DOMAIN ); ?></p>
						</div>
						<div class='kgvvm-card' style='background:#fff4f4;'>
							<h2 style='color:#c00;'><?php echo esc_html( number_format_i18n( $total_missing_h, 2 ) ); ?> Std.</h2>
							<p><?php echo esc_html__( 'Fehlende Stunden gesamt', KGVVM_TEXT_DOMAIN ); ?></p>
						</div>
						<div class='kgvvm-card' style='background:#fff4f4;'>
							<h2 style='color:#c00;'><?php echo esc_html( $this->format_currency( $total_surcharge ) ); ?></h2>
							<p><?php echo esc_html__( 'Aufschlag gesamt (Jahresrechnung)', KGVVM_TEXT_DOMAIN ); ?></p>
						</div>
					</div>
					<?php if ( $members_open > 0 ) : ?>
					<p style='margin-top:8px;'>
						<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $work_year, 'tab' => 'summary' ) ) ); ?>' class='button button-secondary'>
							<?php echo esc_html__( 'Mitgliederübersicht aufrufen', KGVVM_TEXT_DOMAIN ); ?>
						</a>
					</p>
					<?php endif; ?>
				<?php endif; ?>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Fachmodell', KGVVM_TEXT_DOMAIN ); ?></h2>
				<p><?php echo esc_html__( 'Das Plugin verwendet eigene Datenbanktabellen statt Custom Post Types, weil das Fachmodell stark relational ist: Sparten, Parzellen, Zähler, Pächter und Zuordnungen müssen performant gefiltert, eindeutig verknüpft und später um Zählerstände, Verträge oder Umlagen erweitert werden können.', KGVVM_TEXT_DOMAIN ); ?></p>
			</div>
		</div>
		<?php
	}

	/**
	 * Render consumption report page.
	 *
	 * @return void
	 */
	public function render_consumption_page() {
		$this->require_cap( 'manage_kleingarten' );

		$section_id      = isset( $_GET['section_id'] ) ? absint( $_GET['section_id'] ) : 0;
		$date_from       = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash( $_GET['date_from'] ) ) : '';
		$date_to         = isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash( $_GET['date_to'] ) ) : '';
		$order           = isset( $_GET['order'] ) ? strtoupper( sanitize_key( wp_unslash( $_GET['order'] ) ) ) : 'DESC';
		$order           = 'ASC' === $order ? 'ASC' : 'DESC';
		$edit_reading_id = isset( $_GET['edit_reading'] ) ? absint( $_GET['edit_reading'] ) : 0;
		$edit_reading    = $edit_reading_id ? $this->readings->find( $edit_reading_id ) : null;
		$sections        = $this->sections->all_for_options();

		if ( $date_from && $date_to && strtotime( $date_from ) > strtotime( $date_to ) ) {
			$tmp       = $date_from;
			$date_from = $date_to;
			$date_to   = $tmp;
		}

		$rows             = $this->readings->get_consumption_report( $section_id, $date_from, $date_to, $order );
		$rate_lookup      = $this->build_consumption_rate_lookup( $rows );
		$water_total      = 0.0;
		$power_total      = 0.0;
		$water_cost_total = 0.0;
		$power_cost_total = 0.0;
		$consumed_count   = 0;
		$section_totals   = array();

		foreach ( $rows as $row ) {
			$row->unit_price      = $this->get_consumption_unit_price( $row, $rate_lookup );
			$row->calculated_cost = $this->calculate_consumption_cost( $row->consumption, $row->type, $row->unit_price );

			if ( null === $row->consumption ) {
				continue;
			}

			$consumed_count++;

			if ( 'power' === $row->type ) {
				$power_total += (float) $row->consumption;
				if ( null !== $row->calculated_cost ) {
					$power_cost_total += (float) $row->calculated_cost;
				}
			} else {
				$water_total += (float) $row->consumption;
				if ( null !== $row->calculated_cost ) {
					$water_cost_total += (float) $row->calculated_cost;
				}
			}

			$section_key = $row->section_id ? (int) $row->section_id : 0;

			if ( ! isset( $section_totals[ $section_key ] ) ) {
				$section_totals[ $section_key ] = array(
					'name'       => $row->section_name ? $row->section_name : __( 'Ohne Sparte', KGVVM_TEXT_DOMAIN ),
					'water'      => 0.0,
					'power'      => 0.0,
					'water_cost' => 0.0,
					'power_cost' => 0.0,
				);
			}

			$section_totals[ $section_key ][ $row->type ] += (float) $row->consumption;

			if ( null !== $row->calculated_cost ) {
				$section_totals[ $section_key ][ 'power' === $row->type ? 'power_cost' : 'water_cost' ] += (float) $row->calculated_cost;
			}
		}
		?>
		<div class='wrap'>
			<h1><?php echo esc_html__( 'Verbrauchsauswertung', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>

			<?php if ( $edit_reading ) : ?>
			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Ablesung korrigieren', KGVVM_TEXT_DOMAIN ); ?></h2>
				<form method='post'>
					<?php wp_nonce_field( 'kgvvm_correct_meter_reading' ); ?>
					<input type='hidden' name='kgvvm_action' value='correct_meter_reading' />
					<input type='hidden' name='reading_id' value='<?php echo esc_attr( $edit_reading->id ); ?>' />
					<input type='hidden' name='section_id' value='<?php echo esc_attr( $section_id ); ?>' />
					<input type='hidden' name='date_from' value='<?php echo esc_attr( $date_from ); ?>' />
					<input type='hidden' name='date_to' value='<?php echo esc_attr( $date_to ); ?>' />
					<input type='hidden' name='order' value='<?php echo esc_attr( $order ); ?>' />
					<table class='form-table'>
						<tr>
							<th><label for='corr_date'><?php echo esc_html__( 'Ablesedatum', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input type='date' id='corr_date' name='reading_date' value='<?php echo esc_attr( $edit_reading->reading_date ); ?>' required /></td>
						</tr>
						<tr>
							<th><label for='corr_value'><?php echo esc_html__( 'Zählerstand', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input type='number' id='corr_value' name='reading_value' step='0.001' value='<?php echo esc_attr( number_format( (float) $edit_reading->reading_value, 3, '.', '' ) ); ?>' required /></td>
						</tr>
						<tr>
							<th><label for='corr_note'><?php echo esc_html__( 'Korrekturnotiz', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td>
								<textarea id='corr_note' name='note' rows='3' cols='50'><?php echo esc_textarea( $edit_reading->note ); ?></textarea>
								<p class='description'><?php echo esc_html__( 'Bitte den Grund der Korrektur angeben.', KGVVM_TEXT_DOMAIN ); ?></p>
							</td>
						</tr>
					</table>
					<p class='submit'>
						<button type='submit' class='button button-primary'><?php echo esc_html__( 'Korrektur speichern', KGVVM_TEXT_DOMAIN ); ?></button>
						<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-consumption', array_filter( array( 'section_id' => $section_id, 'date_from' => $date_from, 'date_to' => $date_to, 'order' => $order ) ) ) ); ?>' class='button'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
					</p>
				</form>
			</div>
			<?php endif; ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-consumption' />
				<div class='kgvvm-filters'>
					<select name='section_id'>
						<option value=''><?php echo esc_html__( 'Alle Sparten', KGVVM_TEXT_DOMAIN ); ?></option>
						<?php foreach ( $sections as $section ) : ?>
							<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( $section_id, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
						<?php endforeach; ?>
					</select>
					<label>
						<?php echo esc_html__( 'Von', KGVVM_TEXT_DOMAIN ); ?>
						<input type='date' name='date_from' value='<?php echo esc_attr( $date_from ); ?>' />
					</label>
					<label>
						<?php echo esc_html__( 'Bis', KGVVM_TEXT_DOMAIN ); ?>
						<input type='date' name='date_to' value='<?php echo esc_attr( $date_to ); ?>' />
					</label>
					<select name='order'>
						<option value='DESC' <?php selected( $order, 'DESC' ); ?>><?php echo esc_html__( 'Neueste zuerst', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='ASC' <?php selected( $order, 'ASC' ); ?>><?php echo esc_html__( 'Älteste zuerst', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<button class='button button-primary'><?php echo esc_html__( 'Auswerten', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_meter_value_with_unit( $water_total, 'water' ) ); ?></h2>
					<p><?php echo esc_html__( 'Wasserverbrauch im Zeitraum', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_meter_value_with_unit( $power_total, 'power' ) ); ?></h2>
					<p><?php echo esc_html__( 'Stromverbrauch im Zeitraum', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $water_cost_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Wasserkosten im Zeitraum', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $power_cost_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Stromkosten im Zeitraum', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( number_format_i18n( $consumed_count, 0 ) ); ?></h2>
					<p><?php echo esc_html__( 'Ausgewertete Ablesungen', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Gesamtwerte je Sparte', KGVVM_TEXT_DOMAIN ); ?></h2>
				<?php if ( empty( $section_totals ) ) : ?>
					<p><?php echo esc_html__( 'Für den gewählten Zeitraum liegen noch keine auswertbaren Verbrauchsdaten vor.', KGVVM_TEXT_DOMAIN ); ?></p>
				<?php else : ?>
					<table class='widefat striped'>
						<thead>
							<tr>
								<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Wasser', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Wasserkosten', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Strom', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Stromkosten', KGVVM_TEXT_DOMAIN ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $section_totals as $section_total ) : ?>
								<tr>
									<td><strong><?php echo esc_html( $section_total['name'] ); ?></strong></td>
									<td><?php echo esc_html( $this->format_meter_value_with_unit( $section_total['water'], 'water' ) ); ?></td>
									<td><?php echo esc_html( $section_total['water_cost'] > 0 ? $this->format_currency( $section_total['water_cost'] ) : '—' ); ?></td>
									<td><?php echo esc_html( $this->format_meter_value_with_unit( $section_total['power'], 'power' ) ); ?></td>
									<td><?php echo esc_html( $section_total['power_cost'] > 0 ? $this->format_currency( $section_total['power_cost'] ) : '—' ); ?></td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php endif; ?>
			</div>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Zähler', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Vorheriger Stand', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Aktueller Stand', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Verbrauch seit letzter Ablesung', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Preis je Einheit', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Kosten', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='10'><?php echo esc_html__( 'Für die aktuelle Auswahl wurden keine Ablesungen gefunden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<tr>
								<td><?php echo esc_html( wp_date( 'd.m.Y', strtotime( $row->reading_date ) ) ); ?></td>
								<td><?php echo esc_html( $row->section_name ? $row->section_name : '—' ); ?></td>
								<td><?php echo esc_html( $row->parcel_label ? $row->parcel_label : '—' ); ?></td>
								<td>
									<?php echo esc_html( $this->meter_type_label( $row->type ) . ' – ' . $row->meter_number ); ?>
									<?php if ( ! empty( $row->note ) ) : ?>
										<br /><small style='color:#666' title='<?php echo esc_attr( $row->note ); ?>'>&#128204; <?php echo esc_html( mb_strimwidth( $row->note, 0, 60, '…' ) ); ?></small>
									<?php endif; ?>
								</td>
								<td><?php echo esc_html( null !== $row->previous_value ? $this->format_meter_value_with_unit( $row->previous_value, $row->type ) : '—' ); ?></td>
								<td><?php echo esc_html( $this->format_meter_value_with_unit( $row->reading_value, $row->type ) ); ?></td>
								<td><?php echo esc_html( null !== $row->consumption ? $this->format_meter_value_with_unit( $row->consumption, $row->type ) : '—' ); ?></td>
								<td><?php echo esc_html( null !== $row->unit_price ? $this->format_price_per_unit( $row->unit_price, 'power' === $row->type ? 'kWh' : 'm³' ) : '—' ); ?></td>
								<td><?php echo esc_html( null !== $row->calculated_cost ? $this->format_currency( $row->calculated_cost ) : '—' ); ?></td>
								<td>
									<?php
									$filter_args = array_filter(
										array(
											'section_id' => $section_id,
											'date_from'  => $date_from,
											'date_to'    => $date_to,
											'order'      => $order,
										)
									);
									$edit_url    = $this->admin_url( 'kgvvm-consumption', array_merge( $filter_args, array( 'edit_reading' => $row->id ) ) );
									$delete_url  = wp_nonce_url(
										$this->admin_url( 'kgvvm-consumption', array_merge( $filter_args, array( 'kgvvm_action' => 'delete_meter_reading', 'id' => $row->id ) ) ),
										'kgvvm_delete_meter_reading_' . $row->id
									);
									?>
									<a href='<?php echo esc_url( $edit_url ); ?>'><?php echo esc_html__( 'Korrektur', KGVVM_TEXT_DOMAIN ); ?></a>
									|
									<a href='<?php echo esc_url( $delete_url ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Ablesung wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>")' style='color:#b32d2e'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>
		</div>
		<?php
	}

	/**
	 * Render annual cost management.
	 *
	 * @return void
	 */
	public function render_costs_page() {
		$this->require_cap( 'manage_kleingarten' );

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

		if ( 'statement' === $view ) {
			$this->render_cost_statement_page();
			return;
		}

		$selected_year        = isset( $_GET['year'] ) ? absint( $_GET['year'] ) : (int) current_time( 'Y' );
		$selected_year        = $selected_year > 0 ? $selected_year : (int) current_time( 'Y' );
		$search               = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
		$edit_id              = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$edit_rate_section_id = absint( isset( $_GET['edit_rate_section_id'] ) ? $_GET['edit_rate_section_id'] : 0 );
		$cost                 = $edit_id ? $this->costs->find( $edit_id ) : null;

		if ( $cost && ! empty( $cost->entry_year ) ) {
			$selected_year = (int) $cost->entry_year;
		}

		$year_lock_state = $this->costs->get_statement_lock_state( $selected_year );
		$year_is_locked  = ! empty( $year_lock_state['is_locked'] );

		$years          = $this->costs->get_years( $selected_year );
		$sections       = $this->sections->all_for_options( true );
		$section_rates  = $this->costs->get_section_prices( $selected_year );
		$edit_rate      = null;

		foreach ( $section_rates as $section_rate ) {
			if ( (int) $section_rate->section_id === $edit_rate_section_id ) {
				$edit_rate = $section_rate;
				break;
			}
		}

		$rows           = $this->costs->search(
			array(
				'year'    => $selected_year,
				's'       => $search,
				'orderby' => isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'name',
				'order'   => isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'ASC',
			)
		);
		$entry_count    = count( $rows );
		$parcel_rows    = array_values(
			array_filter(
				$this->parcels->search(),
				static function( $parcel ) {
					return isset( $parcel->status ) && 'inactive' !== $parcel->status;
				}
			)
		);
		$tenant_rows    = $this->tenants->search( array( 'status' => 'active' ) );
		$member_rows    = $this->assignments->get_member_users();
		$parcel_count   = count( $parcel_rows );
		$member_count   = count( $member_rows );
		$parcel_share   = 0.0;
		$member_share   = 0.0;
		$year_total     = 0.0;

		foreach ( $rows as $row ) {
			$row->distribution_type    = $this->get_cost_distribution_type( $row );
			$row->unit_amount          = $this->get_cost_unit_amount( $row, $parcel_count, $member_count );
			$row->calculated_total_cost = $this->get_cost_total_amount( $row, $parcel_count, $member_count );
			$year_total               += (float) $row->calculated_total_cost;

			if ( 'member' === $row->distribution_type ) {
				$member_share += (float) $row->unit_amount;
			} else {
				$parcel_share += (float) $row->unit_amount;
			}
		}

		if ( $cost ) {
			$cost->distribution_type     = $this->get_cost_distribution_type( $cost );
			$cost->unit_amount           = $this->get_cost_unit_amount( $cost, $parcel_count, $member_count );
			$cost->calculated_total_cost = $this->get_cost_total_amount( $cost, $parcel_count, $member_count );
		}
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Kosten', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php if ( $cost ) : ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'year' => $selected_year ) ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neuer Eintrag', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php endif; ?>
			<?php $this->render_notice(); ?>
			<?php if ( $year_is_locked ) : ?>
				<div class='notice notice-warning'><p><?php echo esc_html__( 'Diese Jahresabrechnung ist festgeschrieben. Änderungen an Kosten, Preisen und Zuordnungen sind gesperrt, bis sie erneut freigegeben wird.', KGVVM_TEXT_DOMAIN ); ?></p></div>
			<?php endif; ?>

			<form method='get' class='kgvvm-toolbar' id='kgvvm-cost-filter-form'>
				<input type='hidden' name='page' value='kgvvm-costs' />
				<div class='kgvvm-filters'>
					<label for='kgvvm-cost-year-filter'><?php echo esc_html__( 'Jahr', KGVVM_TEXT_DOMAIN ); ?></label>
					<select name='year' id='kgvvm-cost-year-filter'>
						<?php foreach ( $years as $year ) : ?>
							<option value='<?php echo esc_attr( $year ); ?>' <?php selected( $selected_year, $year ); ?>><?php echo esc_html( $year ); ?></option>
						<?php endforeach; ?>
					</select>
					<input type='search' name='s' value='<?php echo esc_attr( $search ); ?>' placeholder='<?php echo esc_attr__( 'Kostenposten suchen …', KGVVM_TEXT_DOMAIN ); ?>' />
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $year_total ) ); ?></h2>
					<p><?php echo esc_html( sprintf( __( 'Gesamtkosten im Jahr %s', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( number_format_i18n( $entry_count, 0 ) ); ?></h2>
					<p><?php echo esc_html( sprintf( __( 'Erfasste Einträge in %s', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $parcel_count > 0 ? $this->format_currency( $parcel_share ) : '—' ); ?></h2>
					<p><?php echo esc_html__( 'Summe je Parzelle', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $member_count > 0 ? $this->format_currency( $member_share ) : '—' ); ?></h2>
					<p><?php echo esc_html__( 'Summe je Mitglied', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
			</div>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $edit_rate ? __( 'Spartenpreise bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Preise je Sparte', KGVVM_TEXT_DOMAIN ) ); ?></h2>
					<p class='kgvvm-help'><?php echo esc_html__( 'Strom- und Wasserpreise können je Jahr und je Sparte unterschiedlich hinterlegt und später wieder bearbeitet werden.', KGVVM_TEXT_DOMAIN ); ?></p>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_save_cost_prices' ); ?>
						<input type='hidden' name='kgvvm_action' value='save_cost_prices' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-price-entry-year'><?php echo esc_html__( 'Jahr', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='entry_year' id='kgvvm-price-entry-year'>
										<?php foreach ( $years as $year ) : ?>
											<option value='<?php echo esc_attr( $year ); ?>' <?php selected( $selected_year, $year ); ?>><?php echo esc_html( $year ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-cost-section-id'><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='section_id' id='kgvvm-cost-section-id' required>
										<option value=''><?php echo esc_html__( 'Bitte wählen', KGVVM_TEXT_DOMAIN ); ?></option>
										<?php foreach ( $sections as $section ) : ?>
											<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( $edit_rate ? (int) $edit_rate->section_id : 0, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-power-price'><?php echo esc_html__( 'Preis pro kWh', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input type='number' min='0' step='0.0001' name='power_price_per_kwh' id='kgvvm-power-price' value='<?php echo esc_attr( $edit_rate && null !== $edit_rate->power_price_per_kwh ? (float) $edit_rate->power_price_per_kwh : '' ); ?>' /> € / kWh</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-water-price'><?php echo esc_html__( 'Preis pro m³', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input type='number' min='0' step='0.0001' name='water_price_per_m3' id='kgvvm-water-price' value='<?php echo esc_attr( $edit_rate && null !== $edit_rate->water_price_per_m3 ? (float) $edit_rate->water_price_per_m3 : '' ); ?>' /> € / m³</td>
							</tr>
						</table>
						<?php submit_button( $edit_rate ? __( 'Spartenpreise aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Spartenpreise speichern', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', true, $year_is_locked ? array( 'disabled' => 'disabled' ) : array() ); ?>
						<?php if ( $edit_rate ) : ?>
							<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'year' => $selected_year ) ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
						<?php endif; ?>
					</form>
				</div>

				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $cost ? __( 'Kostenposten bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Kostenposten anlegen', KGVVM_TEXT_DOMAIN ) ); ?></h2>
					<p class='kgvvm-help'><?php echo esc_html__( 'Pro Jahr können beliebig viele Einträge mit einem Betrag pro Parzelle oder pro Mitglied hinterlegt werden.', KGVVM_TEXT_DOMAIN ); ?></p>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_save_cost' ); ?>
						<input type='hidden' name='kgvvm_action' value='save_cost' />
						<input type='hidden' name='id' value='<?php echo esc_attr( $cost ? $cost->id : 0 ); ?>' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-cost-entry-year'><?php echo esc_html__( 'Jahr', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='entry_year' id='kgvvm-cost-entry-year'>
										<?php foreach ( $years as $year ) : ?>
											<option value='<?php echo esc_attr( $year ); ?>' <?php selected( $cost ? (int) $cost->entry_year : $selected_year, $year ); ?>><?php echo esc_html( $year ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-cost-name'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input name='name' id='kgvvm-cost-name' type='text' class='regular-text' required value='<?php echo esc_attr( $cost ? $cost->name : '' ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-cost-distribution'><?php echo esc_html__( 'Gilt pro', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='distribution_type' id='kgvvm-cost-distribution'>
										<option value='parcel' <?php selected( $cost ? $cost->distribution_type : 'parcel', 'parcel' ); ?>><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></option>
										<option value='member' <?php selected( $cost ? $cost->distribution_type : 'parcel', 'member' ); ?>><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></option>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-cost-unit-amount'><?php echo esc_html__( 'Betrag', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<input name='unit_amount' id='kgvvm-cost-unit-amount' type='number' min='0' step='0.01' required value='<?php echo esc_attr( $cost ? (float) $cost->unit_amount : '' ); ?>' />
									<span>€</span>
									<p class='kgvvm-help'><?php echo esc_html__( 'Betrag je ausgewählter Einheit, also pro Parzelle oder pro Mitglied.', KGVVM_TEXT_DOMAIN ); ?></p>
								</td>
							</tr>
							<tr>
								<th scope='row'><?php echo esc_html__( 'Position', KGVVM_TEXT_DOMAIN ); ?></th>
								<td>
									<label>
										<input type='hidden' name='is_mandatory' value='0' />
										<input type='checkbox' name='is_mandatory' value='1' <?php checked( $cost ? (bool) $cost->is_mandatory : true, true ); ?> />
										<?php echo esc_html__( 'Verpflichtende Position', KGVVM_TEXT_DOMAIN ); ?>
									</label>
									<p class='kgvvm-help'><?php echo esc_html__( 'Wenn aktiviert: Diese Position wird automatisch in allen Abrechnungen berechnet. Wenn deaktiviert: Diese Position wird als manuelle/optionale Position behandelt.', KGVVM_TEXT_DOMAIN ); ?></p>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-cost-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><textarea name='note' id='kgvvm-cost-note' rows='4' class='large-text'><?php echo esc_textarea( $cost ? $cost->note : '' ); ?></textarea></td>
							</tr>
						</table>
						<?php submit_button( $cost ? __( 'Kostenposten aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Kostenposten speichern', KGVVM_TEXT_DOMAIN ), 'primary', 'submit', true, $year_is_locked ? array( 'disabled' => 'disabled' ) : array() ); ?>
					</form>
				</div>

				<div class='kgvvm-card'>
					<h2><?php echo esc_html__( 'Jahresabrechnung drucken', KGVVM_TEXT_DOMAIN ); ?></h2>
					<p class='kgvvm-help'><?php echo esc_html__( 'Die Übersicht kann im Browser direkt gedruckt oder als PDF gespeichert werden.', KGVVM_TEXT_DOMAIN ); ?></p>

					<form method='get' target='_blank' style='margin-bottom:12px;'>
						<input type='hidden' name='page' value='kgvvm-costs' />
						<input type='hidden' name='view' value='statement' />
						<input type='hidden' name='statement_type' value='parcel' />
						<input type='hidden' name='year' value='<?php echo esc_attr( $selected_year ); ?>' />
						<p>
							<label for='kgvvm-statement-parcel'><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></label><br />
							<select name='subject_id' id='kgvvm-statement-parcel' required>
								<option value=''><?php echo esc_html__( 'Parzelle wählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $parcel_rows as $parcel ) : ?>
									<option value='<?php echo esc_attr( $parcel->id ); ?>'><?php echo esc_html( $parcel->label . ( $parcel->section_name ? ' – ' . $parcel->section_name : '' ) ); ?></option>
								<?php endforeach; ?>
							</select>
						</p>
						<?php submit_button( __( 'Parzellenabrechnung öffnen', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', false ); ?>
					</form>

					<form method='get' target='_blank'>
						<input type='hidden' name='page' value='kgvvm-costs' />
						<input type='hidden' name='view' value='statement' />
						<input type='hidden' name='statement_type' value='tenant' />
						<input type='hidden' name='year' value='<?php echo esc_attr( $selected_year ); ?>' />
						<p>
							<label for='kgvvm-statement-tenant'><?php echo esc_html__( 'Pächter', KGVVM_TEXT_DOMAIN ); ?></label><br />
							<select name='subject_id' id='kgvvm-statement-tenant' required>
								<option value=''><?php echo esc_html__( 'Pächter wählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $tenant_rows as $tenant ) : ?>
									<option value='<?php echo esc_attr( $tenant->id ); ?>'><?php echo esc_html( $tenant->last_name . ', ' . $tenant->first_name ); ?></option>
								<?php endforeach; ?>
							</select>
						</p>
						<?php submit_button( __( 'Pächterabrechnung öffnen', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', false ); ?>
					</form>
				</div>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html( sprintf( __( 'Preise je Sparte im Jahr %s', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></h2>
				<?php if ( empty( $section_rates ) ) : ?>
					<p><?php echo esc_html__( 'Für dieses Jahr sind noch keine Spartenpreise hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
				<?php else : ?>
					<table class='widefat striped'>
						<thead>
							<tr>
								<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Preis pro kWh', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Preis pro m³', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Aktualisiert', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $section_rates as $rate ) : ?>
								<tr>
									<td><strong><?php echo esc_html( $rate->section_name ? $rate->section_name : '—' ); ?></strong></td>
									<td><?php echo esc_html( null !== $rate->power_price_per_kwh ? $this->format_price_per_unit( $rate->power_price_per_kwh, 'kWh' ) : '—' ); ?></td>
									<td><?php echo esc_html( null !== $rate->water_price_per_m3 ? $this->format_price_per_unit( $rate->water_price_per_m3, 'm³' ) : '—' ); ?></td>
									<td><?php echo esc_html( ! empty( $rate->updated_at ) ? wp_date( 'd.m.Y H:i', strtotime( $rate->updated_at ) ) : '—' ); ?></td>
									<td>
										<?php if ( $year_is_locked ) : ?>
											<span style='opacity:.65;'><?php echo esc_html__( 'Gesperrt', KGVVM_TEXT_DOMAIN ); ?></span>
										<?php else : ?>
											<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'year' => $selected_year, 'edit_rate_section_id' => $rate->section_id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
										<?php endif; ?>
									</td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php endif; ?>
			</div>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-costs', 'name' ) ); ?>'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Verteilung', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Betrag', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-costs', 'total_cost' ) ); ?>'><?php echo esc_html__( 'Gesamt im Jahr', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-costs', 'updated_at' ) ); ?>'><?php echo esc_html__( 'Aktualisiert', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='8'><?php echo esc_html__( 'Für das gewählte Jahr sind noch keine Kostenposten vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<tr>
								<td><strong><?php echo esc_html( $row->name ); ?></strong></td>
								<td>
									<?php if ( ! isset( $row->is_mandatory ) || (bool) $row->is_mandatory ) : ?>
										<span style='color:#007017;'><?php echo esc_html__( 'Verpflichtend', KGVVM_TEXT_DOMAIN ); ?></span>
									<?php else : ?>
										<span style='color:#9a6700;'><?php echo esc_html__( 'Manuell', KGVVM_TEXT_DOMAIN ); ?></span>
									<?php endif; ?>
								</td>
								<td><?php echo esc_html( $this->get_cost_distribution_label( $row->distribution_type ) ); ?></td>
								<td><?php echo esc_html( $this->format_currency( $row->unit_amount ) ); ?></td>
								<td><?php echo esc_html( $this->format_currency( $row->calculated_total_cost ) ); ?></td>
								<td><?php echo esc_html( $row->note ? $row->note : '—' ); ?></td>
								<td><?php echo esc_html( ! empty( $row->updated_at ) ? wp_date( 'd.m.Y H:i', strtotime( $row->updated_at ) ) : '—' ); ?></td>
								<td>
									<?php if ( $year_is_locked ) : ?>
										<span style='opacity:.65;'><?php echo esc_html__( 'Gesperrt', KGVVM_TEXT_DOMAIN ); ?></span>
									<?php else : ?>
										<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'year' => $selected_year, 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
										|
										<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-costs', array( 'kgvvm_action' => 'delete_cost', 'id' => $row->id, 'year' => $selected_year ) ), 'kgvvm_delete_cost_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Kostenposten wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
									<?php endif; ?>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html__( 'Verteilung pro Parzelle', KGVVM_TEXT_DOMAIN ); ?></h2>
					<?php if ( empty( $parcel_rows ) ) : ?>
						<p><?php echo esc_html__( 'Aktuell sind keine aktiven Parzellen für die Verteilung vorhanden.', KGVVM_TEXT_DOMAIN ); ?></p>
					<?php else : ?>
						<table class='widefat striped'>
							<thead>
								<tr>
									<th><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Anzahl Pächter', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Jahresanteil', KGVVM_TEXT_DOMAIN ); ?></th>
								</tr>
							</thead>
							<tbody>
								<?php foreach ( $parcel_rows as $parcel ) : ?>
									<tr>
										<td><strong><?php echo esc_html( $parcel->label ); ?></strong></td>
										<td><?php echo esc_html( $parcel->section_name ? $parcel->section_name : '—' ); ?></td>
										<td><?php echo esc_html( $this->parcel_status_label( $parcel->status ) ); ?></td>
										<td><?php echo esc_html( ! empty( $parcel->tenant_count ) ? number_format_i18n( (int) $parcel->tenant_count, 0 ) : '—' ); ?></td>
										<td><?php echo esc_html( $this->format_currency( $parcel_share ) ); ?></td>
									</tr>
								<?php endforeach; ?>
							</tbody>
						</table>
					<?php endif; ?>
				</div>

				<div class='kgvvm-card'>
					<h2><?php echo esc_html__( 'Verteilung pro Mitglied', KGVVM_TEXT_DOMAIN ); ?></h2>
					<?php if ( empty( $member_rows ) ) : ?>
						<p><?php echo esc_html__( 'Aktuell sind keine Mitglieder für die Verteilung vorhanden.', KGVVM_TEXT_DOMAIN ); ?></p>
					<?php else : ?>
						<table class='widefat striped'>
							<thead>
								<tr>
									<th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'E-Mail', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Jahresanteil', KGVVM_TEXT_DOMAIN ); ?></th>
								</tr>
							</thead>
							<tbody>
								<?php foreach ( $member_rows as $member ) : ?>
									<tr>
										<td><strong><?php echo esc_html( $member->display_name ); ?></strong></td>
										<td><?php echo esc_html( $member->user_email ? $member->user_email : '—' ); ?></td>
										<td><?php echo esc_html( $member_count > 0 ? $this->format_currency( $member_share ) : '—' ); ?></td>
									</tr>
								<?php endforeach; ?>
							</tbody>
						</table>
					<?php endif; ?>
				</div>
			</div>
		</div>

		<script>
		document.addEventListener('DOMContentLoaded', function () {
			var filterForm = document.getElementById('kgvvm-cost-filter-form');
			var fields = [
				document.getElementById('kgvvm-cost-year-filter'),
				document.getElementById('kgvvm-price-entry-year'),
				document.getElementById('kgvvm-cost-entry-year')
			].filter(Boolean);

			if (!fields.length) {
				return;
			}

			fields.forEach(function (field) {
				field.addEventListener('change', function () {
					fields.forEach(function (otherField) {
						otherField.value = field.value;
					});

					if (field.id === 'kgvvm-cost-year-filter' && filterForm) {
						filterForm.submit();
					}
				});
			});
		});
		</script>
		<?php
	}

	/**
	 * Render one printable annual statement for a parcel or tenant.
	 *
	 * @return void
	 */
	private function render_cost_statement_page() {
		$settings       = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );
		$year           = isset( $_GET['year'] ) ? absint( $_GET['year'] ) : (int) current_time( 'Y' );
		$statement_type = isset( $_GET['statement_type'] ) ? sanitize_key( wp_unslash( $_GET['statement_type'] ) ) : 'parcel';
		$output         = isset( $_GET['output'] ) ? sanitize_key( wp_unslash( $_GET['output'] ) ) : 'html';
		$subject_id     = absint( isset( $_GET['subject_id'] ) ? $_GET['subject_id'] : 0 );
		$lock_state     = $this->costs->get_statement_lock_state( $year );
		$statement_is_locked = ! empty( $lock_state['is_locked'] );
		$date_from      = sprintf( '%04d-01-01', $year );
		$date_to        = sprintf( '%04d-12-31', $year );
		$all_rows       = $this->readings->get_consumption_report( 0, $date_from, $date_to, 'ASC' );
		$active_parcels = array_values(
			array_filter(
				$this->parcels->search(),
				static function( $parcel ) {
					return isset( $parcel->status ) && 'inactive' !== $parcel->status;
				}
			)
		);
		$active_members     = $this->assignments->get_member_users();
		$cost_entries       = $this->costs->search( array( 'year' => $year, 'orderby' => 'name', 'order' => 'ASC' ) );
		$subject_label      = '';
		$subject_meta       = array();
		$parcel_ids         = array();
		$subject_parcel_count = 0;
		$subject_member_count = 0;

		if ( 'tenant' === $statement_type ) {
			$tenant = $this->tenants->find( $subject_id );

			if ( ! $tenant ) {
				$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Der gewünschte Pächter wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
			}

			$tenant_parcels       = $this->assignments->get_parcels_for_tenant( $subject_id );
			$tenant_members       = $this->assignments->get_members_for_tenant( $subject_id );
			$tenant_member_names  = array_values( array_unique( array_filter( wp_list_pluck( $tenant_members, 'display_name' ) ) ) );
			$parcel_ids           = wp_list_pluck( $tenant_parcels, 'id' );
			$subject_parcel_count = count( $tenant_parcels );
			$subject_member_count = count( array_unique( array_filter( array_map( 'intval', wp_list_pluck( $tenant_members, 'ID' ) ) ) ) );
			$subject_label        = sprintf( __( 'Jahresabrechnung Pächter %1$s – %2$s', KGVVM_TEXT_DOMAIN ), $tenant->last_name . ', ' . $tenant->first_name, $year );
			$subject_meta[] = array(
				'label' => __( 'Mitglied/Pächter', KGVVM_TEXT_DOMAIN ),
				'value' => empty( $tenant_member_names ) ? '—' : implode( ', ', $tenant_member_names ),
			);
			$subject_meta[] = array(
				'label' => __( 'Pächter', KGVVM_TEXT_DOMAIN ),
				'value' => $tenant->last_name . ', ' . $tenant->first_name,
			);
			$subject_meta[] = array(
				'label' => __( 'Parzellen', KGVVM_TEXT_DOMAIN ),
				'value' => empty( $tenant_parcels ) ? '—' : implode( ', ', wp_list_pluck( $tenant_parcels, 'label' ) ),
			);
		} else {
			$parcel = $this->parcels->find( $subject_id );

			if ( ! $parcel ) {
				$this->redirect_with_notice( 'kgvvm-costs', 'error', __( 'Die gewünschte Parzelle wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
			}

			$section              = $parcel->section_id ? $this->sections->find( $parcel->section_id ) : null;
			$parcel_tenants       = $this->assignments->get_tenants_for_parcel( $subject_id );
			$parcel_members       = $this->assignments->get_members_for_parcel( $subject_id );
			$parcel_member_names  = array_values( array_unique( array_filter( wp_list_pluck( $parcel_members, 'display_name' ) ) ) );
			$parcel_ids           = array( $subject_id );
			$subject_parcel_count = 1;
			$subject_member_count = count( $parcel_members );
			$subject_label        = sprintf( __( 'Jahresabrechnung Parzelle %1$s – %2$s', KGVVM_TEXT_DOMAIN ), $parcel->label, $year );
			$subject_meta[] = array(
				'label' => __( 'Parzelle', KGVVM_TEXT_DOMAIN ),
				'value' => $parcel->label,
			);
			$subject_meta[] = array(
				'label' => __( 'Sparte', KGVVM_TEXT_DOMAIN ),
				'value' => $section ? $section->name : '—',
			);
			$subject_meta[] = array(
				'label' => __( 'Mitglied/Pächter', KGVVM_TEXT_DOMAIN ),
				'value' => empty( $parcel_member_names ) ? '—' : implode( ', ', $parcel_member_names ),
			);
			$subject_meta[] = array(
				'label' => __( 'Pächter', KGVVM_TEXT_DOMAIN ),
				'value' => empty( $parcel_tenants ) ? '—' : implode( ', ', array_map( array( $this, 'format_tenant_full_name' ), $parcel_tenants ) ),
			);
		}

		$statement_rows = array();

		foreach ( $all_rows as $row ) {
			if ( in_array( (int) $row->parcel_id, array_map( 'intval', $parcel_ids ), true ) ) {
				$statement_rows[] = $row;
			}
		}

		$rate_lookup       = $this->build_consumption_rate_lookup( $statement_rows );
		$water_total       = 0.0;
		$power_total       = 0.0;
		$water_cost_total  = 0.0;
		$power_cost_total  = 0.0;
		$utility_rows      = array();

		foreach ( $statement_rows as $row ) {
			$row->unit_price      = $this->get_consumption_unit_price( $row, $rate_lookup );
			$row->calculated_cost = $this->calculate_consumption_cost( $row->consumption, $row->type, $row->unit_price );

			if ( null === $row->consumption ) {
				continue;
			}

			$utility_rows[] = $row;

			if ( 'power' === $row->type ) {
				$power_total += (float) $row->consumption;
				if ( null !== $row->calculated_cost ) {
					$power_cost_total += (float) $row->calculated_cost;
				}
			} else {
				$water_total += (float) $row->consumption;
				if ( null !== $row->calculated_cost ) {
					$water_cost_total += (float) $row->calculated_cost;
				}
			}
		}

		$fixed_items = array();
		$fixed_total = 0.0;

		// Load parcel-specific cost assignments so entries can either apply to
		// all parcels (mandatory) or only to explicitly assigned parcels (manual).
		$entries_with_assignments = $this->costs->get_entry_ids_with_assignments( $year );
		$subject_assigned_ids     = array();

		foreach ( array_map( 'intval', $parcel_ids ) as $parcel_id ) {
			$subject_assigned_ids = array_merge( $subject_assigned_ids, $this->costs->get_assigned_entry_ids( $parcel_id ) );
		}

		$subject_assigned_ids = array_values( array_unique( array_map( 'intval', $subject_assigned_ids ) ) );

		foreach ( $cost_entries as $entry ) {
			$entry_id        = (int) $entry->id;
			$has_assignments = in_array( $entry_id, $entries_with_assignments, true );
			$is_assigned     = in_array( $entry_id, $subject_assigned_ids, true );
			$is_mandatory    = ! isset( $entry->is_mandatory ) || (bool) $entry->is_mandatory;

			if ( ! $is_mandatory && ! $is_assigned ) {
				continue;
			}

			if ( $is_mandatory && $has_assignments && ! $is_assigned ) {
				continue;
			}

			$distribution_type = $this->get_cost_distribution_type( $entry );
			$unit_amount       = $this->get_cost_unit_amount( $entry, count( $active_parcels ), count( $active_members ) );
			$subject_units     = 'member' === $distribution_type ? $subject_member_count : $subject_parcel_count;
			$share             = $unit_amount * max( 0, $subject_units );
			$total_amount      = $this->get_cost_total_amount( $entry, count( $active_parcels ), count( $active_members ) );
			$fixed_total      += $share;
			$fixed_items[]     = array(
				'name'               => $entry->name,
				'distribution_label' => $this->get_cost_distribution_label( $distribution_type ),
				'unit_amount'        => $unit_amount,
				'units'              => $subject_units,
				'total'              => $total_amount,
				'share'              => $share,
				'note'               => $entry->note,
			);
		}

		$utility_total = $water_cost_total + $power_cost_total;
		$grand_total   = $fixed_total + $utility_total;

		if ( 'pdf' === $output ) {
			$this->render_cost_statement_pdf(
				array(
					'year'             => $year,
					'statement_type'   => $statement_type,
					'subject_id'       => $subject_id,
					'subject_label'    => $subject_label,
					'subject_meta'     => $subject_meta,
					'fixed_items'      => $fixed_items,
					'utility_rows'     => $utility_rows,
					'fixed_total'      => $fixed_total,
					'water_cost_total' => $water_cost_total,
					'power_cost_total' => $power_cost_total,
					'grand_total'      => $grand_total,
					'settings'         => $settings,
				)
			);
			return;
		}
		?>
		<style>
		@media screen {
			.kgvvm-statement-layout { display: flex; gap: 24px; align-items: flex-start; }
			.kgvvm-statement-main   { flex: 1 1 0; min-width: 0; }
			.kgvvm-statement-sidebar { flex: 0 0 300px; width: 300px; }
		}
		@media print { .kgvvm-statement-sidebar { display: none !important; } }
		.kgvvm-cost-assignment-list { list-style: none; margin: 0; padding: 0; }
		.kgvvm-cost-assignment-list li { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #f0f0f0; gap: 8px; }
		.kgvvm-cost-assignment-list li:last-child { border-bottom: none; }
		.kgvvm-cost-assignment-list .kgvvm-entry-name { flex: 1; }
		</style>
		<div class='wrap kgvvm-print-page'>
			<h1><?php echo esc_html( $subject_label ); ?></h1>
			<?php if ( $statement_is_locked ) : ?>
				<div class='notice notice-warning'><p><?php echo esc_html__( 'Diese Jahresabrechnung ist festgeschrieben. Änderungen sind erst nach erneuter Freigabe möglich.', KGVVM_TEXT_DOMAIN ); ?></p></div>
			<?php endif; ?>
			<div class='kgvvm-print-actions'>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'view' => 'statement', 'statement_type' => $statement_type, 'subject_id' => $subject_id, 'year' => $year, 'output' => 'pdf' ) ) ); ?>' class='button button-primary' target='_blank'><?php echo esc_html__( 'PDF erzeugen', KGVVM_TEXT_DOMAIN ); ?></a>
				<a href='#' class='button button-secondary' onclick='window.print(); return false;'><?php echo esc_html__( 'Drucken / Als PDF speichern', KGVVM_TEXT_DOMAIN ); ?></a>
				<form method='post' action='<?php echo esc_url( admin_url( 'admin.php' ) ); ?>' style='display:inline-block;'>
					<?php wp_nonce_field( 'kgvvm_set_statement_lock_' . $year ); ?>
					<input type='hidden' name='kgvvm_action' value='set_statement_lock' />
					<input type='hidden' name='year' value='<?php echo esc_attr( $year ); ?>' />
					<input type='hidden' name='statement_type' value='<?php echo esc_attr( $statement_type ); ?>' />
					<input type='hidden' name='subject_id' value='<?php echo esc_attr( $subject_id ); ?>' />
					<input type='hidden' name='lock_mode' value='<?php echo esc_attr( $statement_is_locked ? 'unlock' : 'lock' ); ?>' />
					<input type='hidden' name='page' value='kgvvm-costs' />
					<button type='submit' class='button <?php echo esc_attr( $statement_is_locked ? 'button-secondary' : 'button-primary' ); ?>'>
						<?php echo esc_html( $statement_is_locked ? __( 'Erneut freigeben', KGVVM_TEXT_DOMAIN ) : __( 'Jahresabrechnung festschreiben', KGVVM_TEXT_DOMAIN ) ); ?>
					</button>
				</form>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-costs', array( 'year' => $year ) ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Zurück', KGVVM_TEXT_DOMAIN ); ?></a>
			</div>

			<div class='kgvvm-statement-layout'>
			<div class='kgvvm-statement-main'>

			<?php if ( ! empty( $settings['pdf_club_name'] ) || ! empty( $settings['pdf_logo_url'] ) || ! empty( $settings['pdf_contact_block'] ) || ! empty( $settings['pdf_intro_text'] ) ) : ?>
				<div class='kgvvm-card'>
					<table style='width:100%; border-collapse:collapse;'>
						<tr>
							<?php if ( ! empty( $settings['pdf_logo_url'] ) ) : ?>
								<td style='width:140px; vertical-align:top;'>
									<img src='<?php echo esc_url( $settings['pdf_logo_url'] ); ?>' alt='' style='max-width:120px; height:auto;' />
								</td>
							<?php endif; ?>
							<td style='vertical-align:top;'>
								<?php if ( ! empty( $settings['pdf_club_name'] ) ) : ?>
									<h2 style='margin-top:0;'><?php echo esc_html( $settings['pdf_club_name'] ); ?></h2>
								<?php endif; ?>
								<?php if ( ! empty( $settings['pdf_contact_block'] ) ) : ?>
									<p><?php echo wp_kses_post( nl2br( esc_html( $settings['pdf_contact_block'] ) ) ); ?></p>
								<?php endif; ?>
							</td>
						</tr>
					</table>
					<?php if ( ! empty( $settings['pdf_intro_text'] ) ) : ?>
						<p><?php echo wp_kses_post( nl2br( esc_html( $settings['pdf_intro_text'] ) ) ); ?></p>
					<?php endif; ?>
				</div>
			<?php endif; ?>

			<div class='kgvvm-card'>
				<ul>
					<?php foreach ( $subject_meta as $meta ) : ?>
						<li><strong><?php echo esc_html( $meta['label'] ); ?>:</strong> <?php echo esc_html( $meta['value'] ); ?></li>
					<?php endforeach; ?>
				</ul>
			</div>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $fixed_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Anteilige Grundkosten', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $water_cost_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Wasserkosten', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $power_cost_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Stromkosten', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
				<div class='kgvvm-card'>
					<h2><?php echo esc_html( $this->format_currency( $grand_total ) ); ?></h2>
					<p><?php echo esc_html__( 'Gesamtbetrag', KGVVM_TEXT_DOMAIN ); ?></p>
				</div>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Grundkosten anteilig', KGVVM_TEXT_DOMAIN ); ?></h2>
				<?php if ( empty( $fixed_items ) ) : ?>
					<p><?php echo esc_html__( 'Für dieses Jahr sind keine allgemeinen Kostenposten hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
				<?php else : ?>
					<table class='widefat striped'>
						<thead>
							<tr>
								<th><?php echo esc_html__( 'Kostenposten', KGVVM_TEXT_DOMAIN ); ?></th>
								<th style='width:140px; white-space:nowrap;'><?php echo esc_html__( 'Anteil', KGVVM_TEXT_DOMAIN ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ( $fixed_items as $item ) : ?>
								<tr>
									<td>
										<strong><?php echo esc_html( $item['name'] ); ?></strong>
										<br /><span class="kgvvm-help"><?php echo esc_html( sprintf( __( '%1$s × %2$s zu je %3$s', KGVVM_TEXT_DOMAIN ), $item['distribution_label'], number_format_i18n( (int) $item['units'], 0 ), $this->format_currency( $item['unit_amount'] ) ) ); ?></span>
										<?php echo $item['note'] ? '<br /><span class="kgvvm-help">' . esc_html( $item['note'] ) . '</span>' : ''; ?>
									</td>
									<td style='width:140px; white-space:nowrap; text-align:right;'><?php echo esc_html( $this->format_currency( $item['share'] ) ); ?></td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>
				<?php endif; ?>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Wasserzähler', KGVVM_TEXT_DOMAIN ); ?></h2>
				<?php echo $this->build_statement_meter_detail_table( $this->get_statement_rows_by_meter_type( $utility_rows, 'water' ), 'water' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
			</div>

			<div class='kgvvm-card'>
				<h2><?php echo esc_html__( 'Stromzähler', KGVVM_TEXT_DOMAIN ); ?></h2>
				<?php echo $this->build_statement_meter_detail_table( $this->get_statement_rows_by_meter_type( $utility_rows, 'power' ), 'power' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
			</div>

			<?php if ( ! empty( $settings['pdf_footer_text'] ) ) : ?>
				<div class='kgvvm-card'>
					<p><?php echo wp_kses_post( nl2br( esc_html( $settings['pdf_footer_text'] ) ) ); ?></p>
				</div>
			<?php endif; ?>

			</div><!-- .kgvvm-statement-main -->

			<?php if ( 'parcel' === $statement_type ) : ?>
			<div class='kgvvm-statement-sidebar'>
				<div class='kgvvm-card'>
					<h3 style='margin-top:0;'><?php echo esc_html__( 'Freigabe-Status', KGVVM_TEXT_DOMAIN ); ?></h3>
					<p>
						<?php if ( $statement_is_locked ) : ?>
							<strong style='color:#b32d2e;'><?php echo esc_html__( 'Festgeschrieben', KGVVM_TEXT_DOMAIN ); ?></strong>
						<?php else : ?>
							<strong style='color:#007017;'><?php echo esc_html__( 'Freigegeben', KGVVM_TEXT_DOMAIN ); ?></strong>
						<?php endif; ?>
					</p>
					<form method='post' action='<?php echo esc_url( admin_url( 'admin.php' ) ); ?>'>
						<?php wp_nonce_field( 'kgvvm_set_statement_lock_' . $year ); ?>
						<input type='hidden' name='kgvvm_action' value='set_statement_lock' />
						<input type='hidden' name='year' value='<?php echo esc_attr( $year ); ?>' />
						<input type='hidden' name='statement_type' value='<?php echo esc_attr( $statement_type ); ?>' />
						<input type='hidden' name='subject_id' value='<?php echo esc_attr( $subject_id ); ?>' />
						<input type='hidden' name='lock_mode' value='<?php echo esc_attr( $statement_is_locked ? 'unlock' : 'lock' ); ?>' />
						<input type='hidden' name='page' value='kgvvm-costs' />
						<button type='submit' class='button <?php echo esc_attr( $statement_is_locked ? 'button-secondary' : 'button-primary' ); ?>'>
							<?php echo esc_html( $statement_is_locked ? __( 'Erneut freigeben', KGVVM_TEXT_DOMAIN ) : __( 'Jahresabrechnung festschreiben', KGVVM_TEXT_DOMAIN ) ); ?>
						</button>
					</form>
				</div>

				<div class='kgvvm-card'>
					<h3 style='margin-top:0;'><?php echo esc_html__( 'Kostenpositionen', KGVVM_TEXT_DOMAIN ); ?></h3>
					<p class='description'><?php echo esc_html__( 'Hier legen Sie fest, welche Positionen dieser Parzelle berechnet werden. Positionen ohne Einschränkung gelten für alle Parzellen.', KGVVM_TEXT_DOMAIN ); ?></p>

					<?php if ( empty( $cost_entries ) ) : ?>
						<p><?php echo esc_html__( 'Für dieses Jahr sind keine Kostenposten hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
					<?php else : ?>
						<ul class='kgvvm-cost-assignment-list'>
							<?php foreach ( $cost_entries as $entry ) :
								$entry_id     = (int) $entry->id;
								$is_assigned  = in_array( $entry_id, $subject_assigned_ids, true );
								$has_any      = in_array( $entry_id, $entries_with_assignments, true );
								$is_mandatory = ! isset( $entry->is_mandatory ) || (bool) $entry->is_mandatory;
							?>
							<li>
								<span class='kgvvm-entry-name'>
									<?php if ( $is_assigned ) : ?>
										<span style='color:#007017;' title='<?php esc_attr_e( 'Diese Parzelle ist zugeordnet', KGVVM_TEXT_DOMAIN ); ?>'>&#10003;</span>
									<?php elseif ( ! $is_mandatory || $has_any ) : ?>
										<span style='color:#b32d2e;' title='<?php esc_attr_e( 'Diese Parzelle ist nicht zugeordnet', KGVVM_TEXT_DOMAIN ); ?>'>&#10007;</span>
									<?php else : ?>
										<span style='color:#999;' title='<?php esc_attr_e( 'Gilt für alle Parzellen', KGVVM_TEXT_DOMAIN ); ?>'>&#8212;</span>
									<?php endif; ?>
									<?php echo esc_html( $entry->name ); ?>
								</span>
								<?php if ( $statement_is_locked ) : ?>
									<span class='button button-small' style='pointer-events:none; opacity:.65;'><?php echo esc_html__( 'Gesperrt', KGVVM_TEXT_DOMAIN ); ?></span>
								<?php elseif ( $is_assigned ) : ?>
									<form method='post' action='<?php echo esc_url( admin_url( 'admin.php' ) ); ?>' style='display:inline;'>
										<?php wp_nonce_field( 'kgvvm_toggle_parcel_cost' ); ?>
										<input type='hidden' name='kgvvm_action' value='toggle_parcel_cost_assignment' />
										<input type='hidden' name='parcel_id' value='<?php echo esc_attr( $subject_id ); ?>' />
										<input type='hidden' name='cost_entry_id' value='<?php echo esc_attr( $entry_id ); ?>' />
										<input type='hidden' name='assignment_mode' value='remove' />
										<input type='hidden' name='year' value='<?php echo esc_attr( $year ); ?>' />
										<input type='hidden' name='page' value='kgvvm-costs' />
										<button type='submit' class='button button-small'><?php echo esc_html__( 'Entfernen', KGVVM_TEXT_DOMAIN ); ?></button>
									</form>
								<?php elseif ( $is_mandatory && ! $has_any ) : ?>
									<span class='button button-small' style='pointer-events:none; opacity:.65;'><?php echo esc_html__( 'Automatisch', KGVVM_TEXT_DOMAIN ); ?></span>
								<?php else : ?>
									<form method='post' action='<?php echo esc_url( admin_url( 'admin.php' ) ); ?>' style='display:inline;'>
										<?php wp_nonce_field( 'kgvvm_toggle_parcel_cost' ); ?>
										<input type='hidden' name='kgvvm_action' value='toggle_parcel_cost_assignment' />
										<input type='hidden' name='parcel_id' value='<?php echo esc_attr( $subject_id ); ?>' />
										<input type='hidden' name='cost_entry_id' value='<?php echo esc_attr( $entry_id ); ?>' />
										<input type='hidden' name='assignment_mode' value='add' />
										<input type='hidden' name='year' value='<?php echo esc_attr( $year ); ?>' />
										<input type='hidden' name='page' value='kgvvm-costs' />
										<button type='submit' class='button button-small button-primary'><?php echo esc_html__( 'Hinzufügen', KGVVM_TEXT_DOMAIN ); ?></button>
									</form>
								<?php endif; ?>
							</li>
							<?php endforeach; ?>
						</ul>
						<p class='description' style='margin-top:12px; font-size:11px;'>
							<span style='color:#007017;'>&#10003;</span> <?php echo esc_html__( 'zugeordnet', KGVVM_TEXT_DOMAIN ); ?> &nbsp;
							<span style='color:#b32d2e;'>&#10007;</span> <?php echo esc_html__( 'manuell oder ausgeschlossen', KGVVM_TEXT_DOMAIN ); ?> &nbsp;
							<span style='color:#999;'>&#8212;</span> <?php echo esc_html__( 'Pflichtposition für alle Parzellen', KGVVM_TEXT_DOMAIN ); ?>
						</p>
					<?php endif; ?>
				</div>
			</div>
			<?php endif; ?>

			</div><!-- .kgvvm-statement-layout -->
		</div>
		<?php
	}

	/**
	 * Output one annual statement as a real PDF using TCPDF.
	 *
	 * @param array $statement Statement payload.
	 * @return void
	 */
	private function render_cost_statement_pdf( $statement ) {
		$tcpdf_path      = KGVVM_PLUGIN_DIR . 'lib/tcpdf/tcpdf.php';
		$settings        = isset( $statement['settings'] ) && is_array( $statement['settings'] ) ? wp_parse_args( $statement['settings'], $this->get_settings_defaults() ) : $this->get_settings_defaults();
		$document_author = ! empty( $settings['pdf_club_name'] ) ? $settings['pdf_club_name'] : 'KGV Vereinsverwaltung';

		if ( function_exists( 'nocache_headers' ) ) {
			nocache_headers();
		}

		while ( ob_get_level() > 0 ) {
			ob_end_clean();
		}

		if ( ! class_exists( '\\TCPDF' ) && file_exists( $tcpdf_path ) ) {
			require_once $tcpdf_path;
		}

		if ( ! class_exists( '\\TCPDF' ) ) {
			wp_die( esc_html__( 'TCPDF konnte nicht geladen werden. Bitte die Bibliothek prüfen.', KGVVM_TEXT_DOMAIN ) );
		}

		$pdf = new \TCPDF( PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false );
		$pdf->SetCreator( $document_author );
		$pdf->SetAuthor( $document_author );
		$pdf->SetTitle( $statement['subject_label'] );
		$pdf->SetMargins( 12, 12, 12 );
		$pdf->SetAutoPageBreak( true, 12 );
		$pdf->setPrintHeader( false );
		$pdf->setPrintFooter( false );
		$pdf->AddPage();
		$pdf->SetFont( 'dejavusans', '', 10 );
		$pdf->writeHTML( $this->build_cost_statement_pdf_html( $statement ), true, false, true, false, '' );

		$filename = sanitize_file_name( sprintf( 'jahresabrechnung-%s-%s-%s.pdf', $statement['year'], $statement['statement_type'], $statement['subject_id'] ) );
		$pdf->Output( $filename, 'I' );
		exit;
	}

	/**
	 * Build the HTML markup for the PDF statement.
	 *
	 * @param array $statement Statement payload.
	 * @return string
	 */
	private function build_cost_statement_pdf_html( $statement ) {
		$settings = isset( $statement['settings'] ) && is_array( $statement['settings'] ) ? wp_parse_args( $statement['settings'], $this->get_settings_defaults() ) : $this->get_settings_defaults();
		ob_start();
		?>
		<?php if ( ! empty( $settings['pdf_club_name'] ) || ! empty( $settings['pdf_logo_url'] ) || ! empty( $settings['pdf_contact_block'] ) || ! empty( $settings['pdf_intro_text'] ) ) : ?>
			<table cellpadding="4" cellspacing="0" border="0">
				<tr>
					<?php if ( ! empty( $settings['pdf_logo_url'] ) ) : ?>
						<td width="120">
							<img src="<?php echo esc_url( $settings['pdf_logo_url'] ); ?>" style="max-width:100px; height:auto;" alt="" />
						</td>
					<?php endif; ?>
					<td>
						<?php if ( ! empty( $settings['pdf_club_name'] ) ) : ?>
							<h2><?php echo esc_html( $settings['pdf_club_name'] ); ?></h2>
						<?php endif; ?>
						<?php if ( ! empty( $settings['pdf_contact_block'] ) ) : ?>
							<p><?php echo nl2br( esc_html( $settings['pdf_contact_block'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
						<?php endif; ?>
					</td>
				</tr>
			</table>
			<?php if ( ! empty( $settings['pdf_intro_text'] ) ) : ?>
				<p><?php echo nl2br( esc_html( $settings['pdf_intro_text'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
			<?php endif; ?>
		<?php endif; ?>

		<h1><?php echo esc_html( $statement['subject_label'] ); ?></h1>
		<table cellpadding="4" cellspacing="0" border="0">
			<?php foreach ( $statement['subject_meta'] as $meta ) : ?>
				<tr>
					<td><strong><?php echo esc_html( $meta['label'] ); ?>:</strong></td>
					<td><?php echo esc_html( $meta['value'] ); ?></td>
				</tr>
			<?php endforeach; ?>
		</table>

		<h2><?php echo esc_html__( 'Zusammenfassung', KGVVM_TEXT_DOMAIN ); ?></h2>
		<table cellpadding="5" cellspacing="0" border="1">
			<tr>
				<th style="width:76%;"><strong><?php echo esc_html__( 'Position', KGVVM_TEXT_DOMAIN ); ?></strong></th>
				<th style="width:24%; text-align:right; white-space:nowrap;"><strong><?php echo esc_html__( 'Betrag', KGVVM_TEXT_DOMAIN ); ?></strong></th>
			</tr>
			<tr>
				<td><?php echo esc_html__( 'Anteilige Grundkosten', KGVVM_TEXT_DOMAIN ); ?></td>
				<td style="text-align:right; white-space:nowrap;"><?php echo esc_html( $this->format_currency( $statement['fixed_total'] ) ); ?></td>
			</tr>
			<tr>
				<td><?php echo esc_html__( 'Wasserkosten', KGVVM_TEXT_DOMAIN ); ?></td>
				<td style="text-align:right; white-space:nowrap;"><?php echo esc_html( $this->format_currency( $statement['water_cost_total'] ) ); ?></td>
			</tr>
			<tr>
				<td><?php echo esc_html__( 'Stromkosten', KGVVM_TEXT_DOMAIN ); ?></td>
				<td style="text-align:right; white-space:nowrap;"><?php echo esc_html( $this->format_currency( $statement['power_cost_total'] ) ); ?></td>
			</tr>
			<tr>
				<td><strong><?php echo esc_html__( 'Gesamtbetrag', KGVVM_TEXT_DOMAIN ); ?></strong></td>
				<td style="text-align:right; white-space:nowrap;"><strong><?php echo esc_html( $this->format_currency( $statement['grand_total'] ) ); ?></strong></td>
			</tr>
		</table>

		<h2><?php echo esc_html__( 'Grundkosten anteilig', KGVVM_TEXT_DOMAIN ); ?></h2>
		<?php if ( empty( $statement['fixed_items'] ) ) : ?>
			<p><?php echo esc_html__( 'Keine allgemeinen Kostenposten vorhanden.', KGVVM_TEXT_DOMAIN ); ?></p>
		<?php else : ?>
			<table cellpadding="5" cellspacing="0" border="1">
				<tr>
					<th style="width:76%;"><strong><?php echo esc_html__( 'Kostenposten', KGVVM_TEXT_DOMAIN ); ?></strong></th>
					<th style="width:24%; text-align:right; white-space:nowrap;"><strong><?php echo esc_html__( 'Anteil', KGVVM_TEXT_DOMAIN ); ?></strong></th>
				</tr>
				<?php foreach ( $statement['fixed_items'] as $item ) : ?>
					<tr>
						<td style="width:76%;"><?php echo esc_html( $item['name'] . ' – ' . sprintf( __( '%1$s × %2$s zu je %3$s', KGVVM_TEXT_DOMAIN ), $item['distribution_label'], number_format_i18n( (int) $item['units'], 0 ), $this->format_currency( $item['unit_amount'] ) ) ); ?></td>
						<td style="width:24%; text-align:right; white-space:nowrap;"><?php echo esc_html( $this->format_currency( $item['share'] ) ); ?></td>
					</tr>
				<?php endforeach; ?>
			</table>
		<?php endif; ?>

		<h2><?php echo esc_html__( 'Wasserzähler', KGVVM_TEXT_DOMAIN ); ?></h2>
		<?php echo $this->build_statement_meter_detail_table( $this->get_statement_rows_by_meter_type( $statement['utility_rows'], 'water' ), 'water', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

		<h2><?php echo esc_html__( 'Stromzähler', KGVVM_TEXT_DOMAIN ); ?></h2>
		<?php echo $this->build_statement_meter_detail_table( $this->get_statement_rows_by_meter_type( $statement['utility_rows'], 'power' ), 'power', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

		<?php if ( ! empty( $settings['pdf_footer_text'] ) ) : ?>
			<p style="margin-top:16px;"><?php echo nl2br( esc_html( $settings['pdf_footer_text'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
		<?php endif; ?>
		<?php
		return (string) ob_get_clean();
	}

	/**
	 * Render section admin page.
	 *
	 * @return void
	 */
	public function render_sections_page() {
		$this->require_cap( 'edit_sparten' );
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'list';

		if ( 'form' === $view ) {
			$this->render_section_form();
			return;
		}

		$rows = $this->sections->search( $_GET );
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Sparten', KGVVM_TEXT_DOMAIN ); ?></h1>
			<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-sparten', array( 'view' => 'form' ) ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neue Sparte', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php $this->render_notice(); ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-sparten' />
				<div class='kgvvm-filters'>
					<input type='search' name='s' value='<?php echo esc_attr( isset( $_GET['s'] ) ? wp_unslash( $_GET['s'] ) : '' ); ?>' placeholder='<?php echo esc_attr__( 'Sparte suchen …', KGVVM_TEXT_DOMAIN ); ?>' />
					<select name='status'>
						<option value=''><?php echo esc_html__( 'Alle Status', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='active' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'active' ); ?>><?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='inactive' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'inactive' ); ?>><?php echo esc_html__( 'Inaktiv', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-sparten', 'name' ) ); ?>'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Hauptzähler', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-sparten', 'status' ) ); ?>'><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='5'><?php echo esc_html__( 'Noch keine Sparten vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<?php $main_meters = $this->meters->get_main_for_section( $row->id ); ?>
							<tr>
								<td><strong><?php echo esc_html( $row->name ); ?></strong></td>
								<td><?php echo esc_html( $row->description ? $row->description : '—' ); ?></td>
								<td>
									<?php if ( empty( $main_meters ) ) : ?>
										—
									<?php else : ?>
										<?php echo esc_html( implode( ', ', array_map( array( $this, 'format_meter_short_label' ), $main_meters ) ) ); ?>
									<?php endif; ?>
								</td>
								<td><?php echo $this->status_badge( 'active' === $row->status ? __( 'Aktiv', KGVVM_TEXT_DOMAIN ) : __( 'Inaktiv', KGVVM_TEXT_DOMAIN ), 'active' === $row->status ? 'green' : 'gray' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
								<td>
									<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-sparten', array( 'view' => 'form', 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
									|
									<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-sparten', array( 'kgvvm_action' => 'delete_section', 'id' => $row->id ) ), 'kgvvm_delete_section_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Sparte wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>
		</div>
		<?php
	}

	/**
	 * Render section form.
	 *
	 * @return void
	 */
	private function render_section_form() {
		$id                      = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$section                 = $id ? $this->sections->find( $id ) : null;
		$selected_main_meters    = $id ? $this->meters->get_main_for_section( $id ) : array();
		$main_meter_options      = $id ? $this->meters->get_available_main_for_section( $id ) : array();
		$selected_main_meter_ids = $id ? wp_list_pluck( $selected_main_meters, 'id' ) : array();
		$today                   = current_time( 'Y-m-d' );
		?>
		<div class='wrap'>
			<h1><?php echo esc_html( $id ? __( 'Sparte bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neue Sparte', KGVVM_TEXT_DOMAIN ) ); ?></h1>
			<?php $this->render_notice(); ?>
			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_save_section' ); ?>
				<input type='hidden' name='kgvvm_action' value='save_section' />
				<input type='hidden' name='id' value='<?php echo esc_attr( $id ); ?>' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><label for='kgvvm-section-name'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='name' id='kgvvm-section-name' type='text' class='regular-text' required value='<?php echo esc_attr( $section ? $section->name : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-section-description'><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><textarea name='description' id='kgvvm-section-description' rows='5' class='large-text'><?php echo esc_textarea( $section ? $section->description : '' ); ?></textarea></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-section-main-meters'><?php echo esc_html__( 'Hauptzähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<?php if ( ! $id ) : ?>
								<p class='kgvvm-help'><?php echo esc_html__( 'Bitte die Sparte zuerst speichern. Danach können mehrere Hauptzähler zugeordnet werden.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php elseif ( empty( $main_meter_options ) ) : ?>
								<p class='kgvvm-help'><?php echo esc_html__( 'Für diese Sparte sind aktuell keine freien Zähler verfügbar. Bitte zuerst unter „Zähler“ passende Zähler anlegen.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php else : ?>
								<select name='main_meter_ids[]' id='kgvvm-section-main-meters' multiple class='kgvvm-multiselect'>
									<?php foreach ( $main_meter_options as $meter ) : ?>
										<option value='<?php echo esc_attr( $meter->id ); ?>' <?php selected( in_array( (int) $meter->id, $selected_main_meter_ids, true ), true ); ?>><?php echo esc_html( $this->format_meter_short_label( $meter ) ); ?></option>
									<?php endforeach; ?>
								</select>
								<p class='kgvvm-help'><?php echo esc_html__( 'Hier können mehrere freie Zähler dieser Sparte als Hauptzähler markiert werden.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php endif; ?>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-section-status'><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='status' id='kgvvm-section-status'>
								<option value='active' <?php selected( $section ? $section->status : 'active', 'active' ); ?>><?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></option>
								<option value='inactive' <?php selected( $section ? $section->status : 'active', 'inactive' ); ?>><?php echo esc_html__( 'Inaktiv', KGVVM_TEXT_DOMAIN ); ?></option>
							</select>
						</td>
					</tr>
				</table>
				<?php submit_button( $id ? __( 'Sparte aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Sparte anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-sparten' ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Zurück', KGVVM_TEXT_DOMAIN ); ?></a>
			</form>

			<?php if ( $id && ! empty( $selected_main_meters ) ) : ?>
				<div class='kgvvm-card' style='margin-top:24px;'>
					<h2><?php echo esc_html__( 'Ablesungen der Hauptzähler', KGVVM_TEXT_DOMAIN ); ?></h2>
					<p class='kgvvm-help'><?php echo esc_html__( 'Hier können für die Hauptzähler dieser Sparte direkt neue Ablesungen erfasst werden.', KGVVM_TEXT_DOMAIN ); ?></p>
					<div class='kgvvm-grid'>
						<?php foreach ( $selected_main_meters as $main_meter ) : ?>
							<?php
							$latest_main    = $this->readings->get_latest_for_meter( $main_meter->id );
							$recent_main    = $this->readings->get_all_for_meter( $main_meter->id, 5 );
							$monthly_summary = $this->readings->get_monthly_summary_for_meter( $main_meter->id );
							$step           = 'water' === $main_meter->type ? '0.01' : '1';
							?>
							<div class='kgvvm-card'>
								<h3><?php echo esc_html( $this->format_meter_short_label( $main_meter ) ); ?></h3>
								<p>
									<strong><?php echo esc_html__( 'Letzte Ablesung:', KGVVM_TEXT_DOMAIN ); ?></strong>
									<?php if ( $latest_main ) : ?>
										<?php echo esc_html( $this->format_meter_value_with_unit( $latest_main->reading_value, $main_meter->type ) . ' am ' . wp_date( 'd.m.Y', strtotime( $latest_main->reading_date ) ) ); ?>
									<?php else : ?>
										<?php echo esc_html__( 'Noch keine Ablesung vorhanden', KGVVM_TEXT_DOMAIN ); ?>
									<?php endif; ?>
								</p>

								<form method='post'>
									<?php wp_nonce_field( 'kgvvm_save_meter_reading' ); ?>
									<input type='hidden' name='kgvvm_action' value='save_meter_reading' />
									<input type='hidden' name='return_page' value='kgvvm-sparten' />
									<input type='hidden' name='return_view' value='form' />
									<input type='hidden' name='return_id' value='<?php echo esc_attr( $id ); ?>' />
									<input type='hidden' name='parcel_id' value='0' />
									<input type='hidden' name='meter_id' value='<?php echo esc_attr( $main_meter->id ); ?>' />
									<p>
										<label><?php echo esc_html__( 'Ablesedatum', KGVVM_TEXT_DOMAIN ); ?><br />
											<input type='date' name='reading_date' required value='<?php echo esc_attr( $today ); ?>' />
										</label>
									</p>
									<p>
										<label><?php echo esc_html__( 'Zählerstand', KGVVM_TEXT_DOMAIN ); ?> (<?php echo esc_html( $this->meter_unit_label( $main_meter->type ) ); ?>)<br />
											<input type='number' min='0' step='<?php echo esc_attr( $step ); ?>' name='reading_value' required />
										</label>
									</p>
									<p>
										<label><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?><br />
											<textarea name='note' rows='3' class='large-text'></textarea>
										</label>
									</p>
									<?php submit_button( __( 'Ablesung speichern', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', false ); ?>
								</form>

								<?php if ( ! empty( $recent_main ) ) : ?>
									<table class='widefat striped' style='margin-top:12px;'>
										<thead>
											<tr>
												<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Stand', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Flag', KGVVM_TEXT_DOMAIN ); ?></th>
											</tr>
										</thead>
										<tbody>
											<?php foreach ( $recent_main as $reading ) : ?>
												<tr>
													<td><?php echo esc_html( wp_date( 'd.m.Y', strtotime( $reading->reading_date ) ) ); ?></td>
													<td><?php echo esc_html( $this->format_meter_value_with_unit( $reading->reading_value, $main_meter->type ) ); ?></td>
													<td><?php echo wp_kses_post( $this->reading_flag_badge( $reading ) ); ?></td>
												</tr>
											<?php endforeach; ?>
										</tbody>
									</table>
								<?php endif; ?>

								<?php if ( ! empty( $monthly_summary ) ) : ?>
									<h4 style='margin-top:16px;'><?php echo esc_html__( 'Monatsübersicht', KGVVM_TEXT_DOMAIN ); ?></h4>
									<table class='widefat striped'>
										<thead>
											<tr>
												<th><?php echo esc_html__( 'Monat', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Von', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Bis', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Verbrauch', KGVVM_TEXT_DOMAIN ); ?></th>
												<th><?php echo esc_html__( 'Ablesungen', KGVVM_TEXT_DOMAIN ); ?></th>
											</tr>
										</thead>
										<tbody>
											<?php foreach ( $monthly_summary as $month_row ) : ?>
												<tr>
													<td><?php echo esc_html( wp_date( 'm.Y', strtotime( $month_row['month'] . '-01' ) ) ); ?></td>
													<td><?php echo esc_html( null !== $month_row['from_value'] ? $this->format_meter_value_with_unit( $month_row['from_value'], $main_meter->type ) : '—' ); ?></td>
													<td><?php echo esc_html( $this->format_meter_value_with_unit( $month_row['to_value'], $main_meter->type ) ); ?></td>
													<td><?php echo esc_html( $this->format_meter_value_with_unit( $month_row['consumption'], $main_meter->type ) ); ?></td>
													<td><?php echo esc_html( (string) $month_row['readings'] ); ?></td>
												</tr>
											<?php endforeach; ?>
										</tbody>
									</table>
								<?php endif; ?>
							</div>
						<?php endforeach; ?>
					</div>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render parcel page.
	 *
	 * @return void
	 */
	public function render_parcels_page() {
		$this->require_cap( 'edit_parzellen' );
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'list';

		if ( 'form' === $view ) {
			$this->render_parcel_form();
			return;
		}

		$paged       = max( 1, absint( isset( $_GET['paged'] ) ? $_GET['paged'] : 1 ) );
		$per_page    = 50;
		$total_items = $this->parcels->count_filtered( $_GET );
		$total_pages = max( 1, (int) ceil( $total_items / $per_page ) );
		$rows        = $this->parcels->search( array_merge( $_GET, array( 'limit' => $per_page, 'paged' => $paged ) ) );
		$sections    = $this->sections->all_for_options();
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Parzellen', KGVVM_TEXT_DOMAIN ); ?></h1>
			<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-parzellen', array( 'view' => 'form' ) ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neue Parzelle', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php $this->render_notice(); ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-parzellen' />
				<div class='kgvvm-filters'>
					<input type='search' name='s' value='<?php echo esc_attr( isset( $_GET['s'] ) ? wp_unslash( $_GET['s'] ) : '' ); ?>' placeholder='<?php echo esc_attr__( 'Parzelle suchen …', KGVVM_TEXT_DOMAIN ); ?>' />
					<select name='section_id'>
						<option value=''><?php echo esc_html__( 'Alle Sparten', KGVVM_TEXT_DOMAIN ); ?></option>
						<?php foreach ( $sections as $section ) : ?>
							<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( isset( $_GET['section_id'] ) ? absint( $_GET['section_id'] ) : 0, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
						<?php endforeach; ?>
					</select>
					<select name='status'>
						<option value=''><?php echo esc_html__( 'Alle Status', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='free' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'free' ); ?>><?php echo esc_html__( 'Frei', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='assigned' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'assigned' ); ?>><?php echo esc_html__( 'Vergeben', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='reserved' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'reserved' ); ?>><?php echo esc_html__( 'Reserviert', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='inactive' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'inactive' ); ?>><?php echo esc_html__( 'Inaktiv', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<div class='kgvvm-toolbar'>
				<p class='kgvvm-help'>
					<?php echo esc_html( sprintf( __( '%1$d Parzellen gesamt · Seite %2$d von %3$d', KGVVM_TEXT_DOMAIN ), (int) $total_items, (int) $paged, (int) $total_pages ) ); ?>
				</p>
				<?php if ( $total_items > $per_page ) : ?>
					<div class='tablenav-pages'>
						<?php
						echo wp_kses_post(
							paginate_links(
								array(
									'base'      => add_query_arg( 'paged', '%#%' ),
									'format'    => '',
									'current'   => $paged,
									'total'     => $total_pages,
									'prev_text' => '«',
									'next_text' => '»',
								)
							)
						);
						?>
					</div>
				<?php endif; ?>
			</div>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-parzellen', 'label' ) ); ?>'><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-parzellen', 'area' ) ); ?>'><?php echo esc_html__( 'Fläche', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-parzellen', 'annual_rent' ) ); ?>'><?php echo esc_html__( 'Pacht / Jahr', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Wasserzähler', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Stromzähler', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Mitglieder', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Pächter', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-parzellen', 'status' ) ); ?>'><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='10'><?php echo esc_html__( 'Noch keine Parzellen vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<tr>
								<td><strong><?php echo esc_html( $row->label ); ?></strong></td>
								<td><?php echo esc_html( $row->section_name ); ?></td>
								<td><?php echo null !== $row->area ? esc_html( number_format_i18n( (float) $row->area, 2 ) . ' m²' ) : '—'; ?></td>
								<td><?php echo isset( $row->annual_rent ) && null !== $row->annual_rent ? esc_html( $this->format_currency( $row->annual_rent ) ) : '—'; ?></td>
								<td><?php echo esc_html( $row->water_meter_number ? $row->water_meter_number : '—' ); ?></td>
								<td><?php echo esc_html( $row->power_meter_number ? $row->power_meter_number : '—' ); ?></td>
								<td><?php echo esc_html( (string) $row->member_count ); ?></td>
								<td><?php echo esc_html( (string) $row->tenant_count ); ?></td>
								<td><?php echo $this->status_badge( $this->parcel_status_label( $row->status ), 'inactive' === $row->status ? 'gray' : ( 'reserved' === $row->status ? 'orange' : 'green' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
								<td>
									<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-parzellen', array( 'view' => 'form', 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
									|
									<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-parzellen', array( 'kgvvm_action' => 'delete_parcel', 'id' => $row->id ) ), 'kgvvm_delete_parcel_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Parzelle wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>

			<?php if ( $total_items > $per_page ) : ?>
				<div class='kgvvm-toolbar'>
					<div class='tablenav-pages'>
						<?php
						echo wp_kses_post(
							paginate_links(
								array(
									'base'      => add_query_arg( 'paged', '%#%' ),
									'format'    => '',
									'current'   => $paged,
									'total'     => $total_pages,
									'prev_text' => '«',
									'next_text' => '»',
								)
							)
						);
						?>
					</div>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render parcel form.
	 *
	 * @return void
	 */
	private function render_parcel_form() {
		$id               = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$parcel           = $id ? $this->parcels->find( $id ) : null;
		$sections         = $this->sections->all_for_options( true );
		$selected_section = $parcel ? absint( $parcel->section_id ) : 0;
		$water_selected   = $parcel ? $this->meters->get_assigned_to_parcel( $id, 'water' ) : null;
		$power_selected   = $parcel ? $this->meters->get_assigned_to_parcel( $id, 'power' ) : null;
		$water_meters     = $this->meters->get_free_by_type( 'water', $selected_section, $id );
		$power_meters     = $this->meters->get_free_by_type( 'power', $selected_section, $id );
		$member_users     = $this->assignments->get_member_users();
		$member_ids       = $id ? $this->assignments->get_member_ids_for_parcel( $id ) : array();
		$tenant_ids       = $id ? $this->assignments->get_tenant_ids_for_parcel( $id ) : array();
		$tenants          = $this->tenants->all_active();
		?>
		<div class='wrap'>
			<h1><?php echo esc_html( $id ? __( 'Parzelle bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neue Parzelle', KGVVM_TEXT_DOMAIN ) ); ?></h1>
			<?php $this->render_notice(); ?>

			<?php if ( empty( $sections ) ) : ?>
				<div class='notice notice-warning'><p><?php echo esc_html__( 'Bitte zuerst mindestens eine aktive Sparte anlegen.', KGVVM_TEXT_DOMAIN ); ?></p></div>
			<?php endif; ?>

			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_save_parcel' ); ?>
				<input type='hidden' name='kgvvm_action' value='save_parcel' />
				<input type='hidden' name='id' value='<?php echo esc_attr( $id ); ?>' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-label'><?php echo esc_html__( 'Parzellennummer / Bezeichnung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='label' id='kgvvm-parcel-label' type='text' class='regular-text' required value='<?php echo esc_attr( $parcel ? $parcel->label : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-section'><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='section_id' id='kgvvm-parcel-section' required>
								<option value=''><?php echo esc_html__( 'Bitte auswählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $sections as $section ) : ?>
									<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( $selected_section, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Freie Wasser- und Stromzähler müssen zur gleichen Sparte gehören.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-area'><?php echo esc_html__( 'Fläche', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='area' id='kgvvm-parcel-area' type='number' min='0' step='0.01' value='<?php echo esc_attr( $parcel && null !== $parcel->area ? $parcel->area : '' ); ?>' /> <span class='kgvvm-help'>m²</span></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-rent'><?php echo esc_html__( 'Pacht pro Jahr', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<input name='annual_rent' id='kgvvm-parcel-rent' type='number' min='0' step='0.01' value='<?php echo esc_attr( $parcel && isset( $parcel->annual_rent ) && null !== $parcel->annual_rent ? $parcel->annual_rent : '' ); ?>' />
							<span class='kgvvm-help'>€</span>
							<p class='kgvvm-help'><?php echo esc_html__( 'Optionaler jährlicher Pachtbetrag für diese Parzelle.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-status'><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='status' id='kgvvm-parcel-status'>
								<option value='free' <?php selected( $parcel ? $parcel->status : 'free', 'free' ); ?>><?php echo esc_html__( 'Frei', KGVVM_TEXT_DOMAIN ); ?></option>
								<option value='assigned' <?php selected( $parcel ? $parcel->status : 'free', 'assigned' ); ?>><?php echo esc_html__( 'Vergeben', KGVVM_TEXT_DOMAIN ); ?></option>
								<option value='reserved' <?php selected( $parcel ? $parcel->status : 'free', 'reserved' ); ?>><?php echo esc_html__( 'Reserviert', KGVVM_TEXT_DOMAIN ); ?></option>
								<option value='inactive' <?php selected( $parcel ? $parcel->status : 'free', 'inactive' ); ?>><?php echo esc_html__( 'Inaktiv', KGVVM_TEXT_DOMAIN ); ?></option>
							</select>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-water-meter'><?php echo esc_html__( 'Wasserzähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='water_meter_id' id='kgvvm-water-meter' required>
								<option value=''><?php echo esc_html__( 'Bitte auswählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $water_meters as $meter ) : ?>
									<option value='<?php echo esc_attr( $meter->id ); ?>' <?php selected( $water_selected ? $water_selected->id : 0, $meter->id ); ?>><?php echo esc_html( $meter->meter_number . ' (Sparte #' . $meter->section_id . ')' ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Es werden nur freie oder bereits dieser Parzelle zugeordnete Wasserzähler angeboten.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-power-meter'><?php echo esc_html__( 'Stromzähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='power_meter_id' id='kgvvm-power-meter' required>
								<option value=''><?php echo esc_html__( 'Bitte auswählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $power_meters as $meter ) : ?>
									<option value='<?php echo esc_attr( $meter->id ); ?>' <?php selected( $power_selected ? $power_selected->id : 0, $meter->id ); ?>><?php echo esc_html( $meter->meter_number . ' (Sparte #' . $meter->section_id . ')' ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Es werden nur freie oder bereits dieser Parzelle zugeordnete Stromzähler angeboten.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-members'><?php echo esc_html__( 'Mitglieder (WordPress-User)', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='member_ids[]' id='kgvvm-members' multiple class='kgvvm-multiselect'>
								<?php foreach ( $member_users as $member ) : ?>
									<option value='<?php echo esc_attr( $member->ID ); ?>' <?php selected( in_array( (int) $member->ID, $member_ids, true ), true ); ?>><?php echo esc_html( $member->display_name . ' (' . $member->user_email . ')' ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Rolle "Mitglied" wird bei der Plugin-Aktivierung automatisch angelegt.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenants'><?php echo esc_html__( 'Pächter', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='tenant_ids[]' id='kgvvm-tenants' multiple class='kgvvm-multiselect'>
								<?php foreach ( $tenants as $tenant ) : ?>
									<option value='<?php echo esc_attr( $tenant->id ); ?>' <?php selected( in_array( (int) $tenant->id, $tenant_ids, true ), true ); ?>><?php echo esc_html( $tenant->last_name . ', ' . $tenant->first_name ); ?></option>
								<?php endforeach; ?>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Mehrfachauswahl möglich, um mehrere Pächter einer Parzelle zuzuordnen.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-parcel-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><textarea name='note' id='kgvvm-parcel-note' rows='5' class='large-text'><?php echo esc_textarea( $parcel ? $parcel->note : '' ); ?></textarea></td>
					</tr>
				</table>
				<?php submit_button( $id ? __( 'Parzelle aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Parzelle anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-parzellen' ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Zurück', KGVVM_TEXT_DOMAIN ); ?></a>
			</form>
		</div>
		<?php
	}

	/**
	 * Render meter page.
	 *
	 * @return void
	 */
	public function render_meters_page() {
		$this->require_cap( 'edit_zaehler' );
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'list';

		if ( 'form' === $view ) {
			$this->render_meter_form();
			return;
		}

		if ( 'swap' === $view ) {
			$this->render_meter_swap_form();
			return;
		}

		$rows     = $this->meters->search( $_GET );
		$sections = $this->sections->all_for_options();
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Zähler', KGVVM_TEXT_DOMAIN ); ?></h1>
			<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-zaehler', array( 'view' => 'form' ) ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neuer Zähler', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php $this->render_notice(); ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-zaehler' />
				<div class='kgvvm-filters'>
					<input type='search' name='s' value='<?php echo esc_attr( isset( $_GET['s'] ) ? wp_unslash( $_GET['s'] ) : '' ); ?>' placeholder='<?php echo esc_attr__( 'Zähler suchen …', KGVVM_TEXT_DOMAIN ); ?>' />
					<select name='type'>
						<option value=''><?php echo esc_html__( 'Alle Typen', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='water' <?php selected( isset( $_GET['type'] ) ? wp_unslash( $_GET['type'] ) : '', 'water' ); ?>><?php echo esc_html__( 'Wasser', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='power' <?php selected( isset( $_GET['type'] ) ? wp_unslash( $_GET['type'] ) : '', 'power' ); ?>><?php echo esc_html__( 'Strom', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<select name='section_id'>
						<option value=''><?php echo esc_html__( 'Alle Sparten', KGVVM_TEXT_DOMAIN ); ?></option>
						<?php foreach ( $sections as $section ) : ?>
							<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( isset( $_GET['section_id'] ) ? absint( $_GET['section_id'] ) : 0, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
						<?php endforeach; ?>
					</select>
					<select name='assignment'>
						<option value=''><?php echo esc_html__( 'Alle Zuordnungen', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='free' <?php selected( isset( $_GET['assignment'] ) ? wp_unslash( $_GET['assignment'] ) : '', 'free' ); ?>><?php echo esc_html__( 'Frei', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='assigned' <?php selected( isset( $_GET['assignment'] ) ? wp_unslash( $_GET['assignment'] ) : '', 'assigned' ); ?>><?php echo esc_html__( 'Zugeordnet', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='main' <?php selected( isset( $_GET['assignment'] ) ? wp_unslash( $_GET['assignment'] ) : '', 'main' ); ?>><?php echo esc_html__( 'Hauptzähler', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-zaehler', 'meter_number' ) ); ?>'><?php echo esc_html__( 'Zählernummer', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-zaehler', 'type' ) ); ?>'><?php echo esc_html__( 'Typ', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-zaehler', 'installed_at' ) ); ?>'><?php echo esc_html__( 'Einbaudatum', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-zaehler', 'calibration_year' ) ); ?>'><?php echo esc_html__( 'Eichjahr', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='8'><?php echo esc_html__( 'Noch keine Zähler vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<?php $latest_swap_reading = $this->readings->get_latest_for_meter( $row->id ); ?>
							<tr>
								<td><strong><?php echo esc_html( $row->meter_number ); ?></strong></td>
								<td><?php echo esc_html( $this->meter_type_label( $row->type ) ); ?></td>
								<td><?php echo esc_html( $row->section_name ? $row->section_name : '—' ); ?></td>
								<td><?php echo esc_html( $row->parcel_label ? $row->parcel_label : ( ! empty( $row->is_main_meter ) ? __( 'Hauptzähler der Sparte', KGVVM_TEXT_DOMAIN ) : __( 'frei', KGVVM_TEXT_DOMAIN ) ) ); ?></td>
								<td><?php echo esc_html( $row->installed_at ? wp_date( 'd.m.Y', strtotime( $row->installed_at ) ) : '—' ); ?></td>
								<td><?php echo esc_html( ! empty( $row->calibration_year ) ? (string) $row->calibration_year : '—' ); ?></td>
								<td><?php echo $this->status_badge( (int) $row->is_active === 1 ? ( ! empty( $row->parcel_id ) ? __( 'Zugeordnet', KGVVM_TEXT_DOMAIN ) : ( ! empty( $row->is_main_meter ) ? __( 'Hauptzähler', KGVVM_TEXT_DOMAIN ) : __( 'Frei', KGVVM_TEXT_DOMAIN ) ) ) : __( 'Inaktiv', KGVVM_TEXT_DOMAIN ), (int) $row->is_active === 1 ? ( ! empty( $row->parcel_id ) ? 'orange' : ( ! empty( $row->is_main_meter ) ? 'blue' : 'green' ) ) : 'gray' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
								<td>
									<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-zaehler', array( 'view' => 'form', 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
									<?php if ( (int) $row->is_active === 1 ) : ?>
										|
										<button
											type='button'
											class='button-link kgvvm-open-swap-modal'
											data-meter-id='<?php echo esc_attr( $row->id ); ?>'
											data-meter-number='<?php echo esc_attr( $row->meter_number ); ?>'
											data-meter-type='<?php echo esc_attr( $this->meter_type_label( $row->type ) ); ?>'
											data-parcel-label='<?php echo esc_attr( $row->parcel_label ? $row->parcel_label : ( ! empty( $row->is_main_meter ) ? __( 'Hauptzähler der Sparte', KGVVM_TEXT_DOMAIN ) : __( 'frei', KGVVM_TEXT_DOMAIN ) ) ); ?>'
											data-is-assigned='<?php echo esc_attr( ! empty( $row->parcel_id ) ? '1' : '0' ); ?>'
											data-last-reading='<?php echo esc_attr( $latest_swap_reading ? (string) $latest_swap_reading->reading_value : '' ); ?>'
											data-last-reading-display='<?php echo esc_attr( $latest_swap_reading ? $this->format_meter_value( $latest_swap_reading->reading_value, $row->type ) : '' ); ?>'
											data-last-date='<?php echo esc_attr( $latest_swap_reading ? wp_date( 'd.m.Y', strtotime( $latest_swap_reading->reading_date ) ) : '' ); ?>'
											data-unit='<?php echo esc_attr( $this->meter_unit_label( $row->type ) ); ?>'
										><?php echo esc_html__( 'Tauschen', KGVVM_TEXT_DOMAIN ); ?></button>
									<?php endif; ?>
									|
									<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-zaehler', array( 'kgvvm_action' => 'delete_meter', 'id' => $row->id ) ), 'kgvvm_delete_meter_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Zähler wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>

			<?php $this->render_meter_swap_modal(); ?>
		</div>
		<?php
	}

	/**
	 * Render the swap modal directly inside the meter overview.
	 *
	 * @return void
	 */
	private function render_meter_swap_modal() {
		$today = current_time( 'Y-m-d' );
		?>
		<div id='kgvvm-swap-modal' class='kgvvm-modal' hidden>
			<div class='kgvvm-modal__backdrop' data-kgvvm-close-modal></div>
			<div class='kgvvm-modal__dialog' role='dialog' aria-modal='true' aria-labelledby='kgvvm-swap-modal-title'>
				<button type='button' class='kgvvm-modal__close' data-kgvvm-close-modal aria-label='<?php echo esc_attr__( 'Schließen', KGVVM_TEXT_DOMAIN ); ?>'>×</button>
				<h2 id='kgvvm-swap-modal-title'><?php echo esc_html__( 'Zähler tauschen', KGVVM_TEXT_DOMAIN ); ?></h2>
				<p class='kgvvm-help'><?php echo esc_html__( 'Der Altzähler wird archiviert, die Historie bleibt erhalten und der neue Zähler übernimmt bei Bedarf direkt die Parzellenzuordnung.', KGVVM_TEXT_DOMAIN ); ?></p>

				<div class='kgvvm-card kgvvm-modal__summary'>
					<p><strong id='kgvvm-swap-current-meter'>—</strong></p>
					<p id='kgvvm-swap-current-assignment'>—</p>
					<p id='kgvvm-swap-current-reading'>—</p>
				</div>

				<form method='post'>
					<?php wp_nonce_field( 'kgvvm_swap_meter' ); ?>
					<input type='hidden' name='kgvvm_action' value='swap_meter' />
					<input type='hidden' name='id' id='kgvvm-swap-meter-id' value='' />
					<table class='form-table kgvvm-form-table'>
						<tr>
							<th scope='row'><label for='kgvvm-new-meter-number'><?php echo esc_html__( 'Neue Zählernummer', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input name='new_meter_number' id='kgvvm-new-meter-number' type='text' class='regular-text' required value='' /></td>
						</tr>
						<tr>
							<th scope='row'><label for='kgvvm-swap-date'><?php echo esc_html__( 'Tauschdatum', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input name='swap_date' id='kgvvm-swap-date' type='date' required value='<?php echo esc_attr( $today ); ?>' /></td>
						</tr>
						<tr>
							<th scope='row'><label for='kgvvm-new-calibration-year'><?php echo esc_html__( 'Eichjahr neuer Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td>
								<input name='new_calibration_year' id='kgvvm-new-calibration-year' type='number' min='2000' max='2100' step='1' value='' />
								<p class='kgvvm-help'><?php echo esc_html__( 'Optional: Jahr der Eichung des neuen Ersatz-Zählers.', KGVVM_TEXT_DOMAIN ); ?></p>
							</td>
						</tr>
						<tbody id='kgvvm-swap-assigned-fields'>
							<tr>
								<th scope='row'><label for='kgvvm-old-final-reading'><?php echo esc_html__( 'Endstand alter Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<input name='old_final_reading' id='kgvvm-old-final-reading' type='number' min='0' step='0.001' value='' />
									<p class='kgvvm-help'><?php echo esc_html__( 'Optionaler Abschlussstand am Tauschdatum.', KGVVM_TEXT_DOMAIN ); ?> <span id='kgvvm-swap-final-unit'></span></p>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-new-initial-reading'><?php echo esc_html__( 'Startstand neuer Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<input name='new_initial_reading' id='kgvvm-new-initial-reading' type='number' min='0' step='0.001' value='0' />
									<p class='kgvvm-help'><?php echo esc_html__( 'Dieser Wert wird direkt als erste Ablesung gespeichert.', KGVVM_TEXT_DOMAIN ); ?> <span id='kgvvm-swap-start-unit'></span></p>
								</td>
							</tr>
						</tbody>
						<tr>
							<th scope='row'><label for='kgvvm-swap-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td>
								<textarea name='swap_note' id='kgvvm-swap-note' rows='4' class='large-text'></textarea>
							</td>
						</tr>
					</table>
					<div class='kgvvm-modal__actions'>
						<?php submit_button( __( 'Zähler tauschen', KGVVM_TEXT_DOMAIN ), 'primary', 'submit', false ); ?>
						<button type='button' class='button-secondary' data-kgvvm-close-modal><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></button>
					</div>
				</form>
			</div>
		</div>

		<script>
		document.addEventListener('DOMContentLoaded', function () {
			const modal = document.getElementById('kgvvm-swap-modal');
			if (!modal) {
				return;
			}

			const form = modal.querySelector('form');
			const meterIdInput = document.getElementById('kgvvm-swap-meter-id');
			const meterTitle = document.getElementById('kgvvm-swap-current-meter');
			const assignment = document.getElementById('kgvvm-swap-current-assignment');
			const readingInfo = document.getElementById('kgvvm-swap-current-reading');
			const assignedFields = document.getElementById('kgvvm-swap-assigned-fields');
			const oldFinalInput = document.getElementById('kgvvm-old-final-reading');
			const newInitialInput = document.getElementById('kgvvm-new-initial-reading');
			const swapDateInput = document.getElementById('kgvvm-swap-date');
			const finalUnit = document.getElementById('kgvvm-swap-final-unit');
			const startUnit = document.getElementById('kgvvm-swap-start-unit');
			const defaultSwapDate = <?php echo wp_json_encode( $today ); ?>;
			const autoOpenMeterId = <?php echo wp_json_encode( isset( $_GET['open_swap'] ) ? absint( $_GET['id'] ) : 0 ); ?>;

			function closeModal() {
				modal.hidden = true;
				modal.classList.remove('is-open');
				form.reset();
				meterIdInput.value = '';
				swapDateInput.value = defaultSwapDate;
				assignedFields.hidden = false;
				newInitialInput.required = false;
			}

			document.querySelectorAll('.kgvvm-open-swap-modal').forEach(function (button) {
				button.addEventListener('click', function () {
					const isAssigned = button.dataset.isAssigned === '1';
					meterIdInput.value = button.dataset.meterId || '';
					meterTitle.textContent = (button.dataset.meterNumber || '—') + ' (' + (button.dataset.meterType || '') + ')';
					assignment.textContent = isAssigned
						? 'Parzelle: ' + (button.dataset.parcelLabel || '—')
						: <?php echo wp_json_encode( __( 'Der Zähler ist aktuell frei und wird als freier Ersatzzähler angelegt.', KGVVM_TEXT_DOMAIN ) ); ?>;

					if (button.dataset.lastReading) {
						readingInfo.textContent = 'Letzte Ablesung: ' + (button.dataset.lastReadingDisplay || button.dataset.lastReading) + ' ' + (button.dataset.unit || '') + (button.dataset.lastDate ? ' am ' + button.dataset.lastDate : '');
					} else {
						readingInfo.textContent = <?php echo wp_json_encode( __( 'Für diesen Zähler liegt noch keine gespeicherte Ablesung vor.', KGVVM_TEXT_DOMAIN ) ); ?>;
					}

					finalUnit.textContent = button.dataset.unit ? '(' + button.dataset.unit + ')' : '';
					startUnit.textContent = button.dataset.unit ? '(' + button.dataset.unit + ')' : '';
					oldFinalInput.value = button.dataset.lastReading || '';
					newInitialInput.value = '0';
					newInitialInput.required = isAssigned;
					assignedFields.hidden = !isAssigned;
					swapDateInput.value = defaultSwapDate;
					modal.hidden = false;
					modal.classList.add('is-open');
				});
			});

			modal.querySelectorAll('[data-kgvvm-close-modal]').forEach(function (closeButton) {
				closeButton.addEventListener('click', closeModal);
			});

			document.addEventListener('keydown', function (event) {
				if (event.key === 'Escape' && modal.classList.contains('is-open')) {
					closeModal();
				}
			});

			if (autoOpenMeterId) {
				const autoButton = document.querySelector('.kgvvm-open-swap-modal[data-meter-id="' + autoOpenMeterId + '"]');
				if (autoButton) {
					autoButton.click();
				}
			}
		});
		</script>
		<?php
	}

	/**
	 * Render meter form.
	 *
	 * @return void
	 */
	private function render_meter_form() {
		$id       = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$meter    = $id ? $this->meters->find( $id ) : null;
		$sections = $this->sections->all_for_options();
		$history  = $id ? $this->readings->get_all_for_meter( $id, 50 ) : array();
		?>
		<div class='wrap'>
			<h1><?php echo esc_html( $id ? __( 'Zähler bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neuer Zähler', KGVVM_TEXT_DOMAIN ) ); ?></h1>
			<?php $this->render_notice(); ?>
			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_save_meter' ); ?>
				<input type='hidden' name='kgvvm_action' value='save_meter' />
				<input type='hidden' name='id' value='<?php echo esc_attr( $id ); ?>' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><label for='kgvvm-meter-type'><?php echo esc_html__( 'Typ', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='type' id='kgvvm-meter-type'>
								<option value='water' <?php selected( $meter ? $meter->type : 'water', 'water' ); ?>><?php echo esc_html__( 'Wasser', KGVVM_TEXT_DOMAIN ); ?></option>
								<option value='power' <?php selected( $meter ? $meter->type : 'water', 'power' ); ?>><?php echo esc_html__( 'Strom', KGVVM_TEXT_DOMAIN ); ?></option>
							</select>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-meter-number'><?php echo esc_html__( 'Zählernummer', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='meter_number' id='kgvvm-meter-number' type='text' class='regular-text' required value='<?php echo esc_attr( $meter ? $meter->meter_number : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-meter-section'><?php echo esc_html__( 'Sparte', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<select name='section_id' id='kgvvm-meter-section' required>
								<option value=''><?php echo esc_html__( 'Bitte auswählen', KGVVM_TEXT_DOMAIN ); ?></option>
								<?php foreach ( $sections as $section ) : ?>
									<option value='<?php echo esc_attr( $section->id ); ?>' <?php selected( $meter ? $meter->section_id : 0, $section->id ); ?>><?php echo esc_html( $section->name ); ?></option>
								<?php endforeach; ?>
							</select>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-meter-installed'><?php echo esc_html__( 'Einbaudatum', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='installed_at' id='kgvvm-meter-installed' type='date' value='<?php echo esc_attr( $meter ? $meter->installed_at : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-meter-calibration-year'><?php echo esc_html__( 'Eichjahr', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<input name='calibration_year' id='kgvvm-meter-calibration-year' type='number' min='2000' max='2100' step='1' value='<?php echo esc_attr( $meter && ! empty( $meter->calibration_year ) ? $meter->calibration_year : '' ); ?>' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Für getauschte Wasser- und Stromzähler kann hier das Eichjahr hinterlegt werden. Das Jahr reicht aus.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></th>
						<td><label><input type='checkbox' name='is_active' value='1' <?php checked( $meter ? (int) $meter->is_active : 1, 1 ); ?> /> <?php echo esc_html__( 'Zähler ist aktiv', KGVVM_TEXT_DOMAIN ); ?></label></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-meter-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<textarea name='note' id='kgvvm-meter-note' rows='5' class='large-text'><?php echo esc_textarea( $meter ? $meter->note : '' ); ?></textarea>
							<p class='kgvvm-help'><?php echo esc_html__( 'Bei einem Zählerwechsel empfiehlt es sich, den alten Zähler inaktiv zu setzen und den neuen Zähler als eigenen Datensatz anzulegen. So bleibt die Historie nachvollziehbar.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
				</table>
				<?php submit_button( $id ? __( 'Zähler aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Zähler anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-zaehler' ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Zurück', KGVVM_TEXT_DOMAIN ); ?></a>
			</form>

			<?php if ( $id ) : ?>
				<div class='kgvvm-card' style='margin-top:24px;'>
					<h2><?php echo esc_html__( 'Historie dieses Zählers', KGVVM_TEXT_DOMAIN ); ?></h2>
					<?php if ( empty( $history ) ) : ?>
						<p><?php echo esc_html__( 'Für diesen Zähler wurden noch keine Ablesungen erfasst.', KGVVM_TEXT_DOMAIN ); ?></p>
					<?php else : ?>
						<table class='widefat striped'>
							<thead>
								<tr>
									<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Parzelle', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Stand', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Flag', KGVVM_TEXT_DOMAIN ); ?></th>
									<th><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></th>
								</tr>
							</thead>
							<tbody>
								<?php foreach ( $history as $entry ) : ?>
									<tr>
										<td><?php echo esc_html( wp_date( 'd.m.Y', strtotime( $entry->reading_date ) ) ); ?></td>
										<td><?php echo esc_html( $entry->parcel_label ? $entry->parcel_label : '—' ); ?></td>
										<td><?php echo esc_html( $this->format_meter_value_with_unit( $entry->reading_value, $meter->type ) ); ?></td>
										<td><?php echo wp_kses_post( $this->reading_flag_badge( $entry ) ); ?></td>
										<td><?php echo esc_html( $entry->note ? $entry->note : '—' ); ?></td>
									</tr>
								<?php endforeach; ?>
							</tbody>
						</table>
					<?php endif; ?>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render meter swap form.
	 *
	 * @return void
	 */
	private function render_meter_swap_form() {
		$id      = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$meter   = $id ? $this->meters->find( $id ) : null;
		$parcel  = $meter && ! empty( $meter->parcel_id ) ? $this->parcels->find( (int) $meter->parcel_id ) : null;
		$latest  = $meter ? $this->readings->get_latest_for_meter( $meter->id ) : null;
		$today   = current_time( 'Y-m-d' );
		$unit    = $meter ? $this->meter_unit_label( $meter->type ) : '';

		if ( ! $meter ) {
			$this->redirect_with_notice( 'kgvvm-zaehler', 'error', __( 'Der gewünschte Zähler wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ) );
		}
		?>
		<div class='wrap'>
			<h1><?php echo esc_html__( 'Zähler tauschen', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>

			<div class='notice notice-info inline'>
				<p>
					<strong><?php echo esc_html( $meter->meter_number ); ?></strong>
					(<?php echo esc_html( $this->meter_type_label( $meter->type ) ); ?>)
					<?php echo esc_html( $parcel ? ' – ' . $parcel->label : ' – ' . __( 'aktuell keiner Parzelle zugeordnet', KGVVM_TEXT_DOMAIN ) ); ?>
					<?php if ( $latest ) : ?>
						<br /><?php echo esc_html( sprintf( __( 'Letzte Ablesung: %1$s %2$s am %3$s', KGVVM_TEXT_DOMAIN ), $this->format_meter_value( $latest->reading_value, $meter->type ), $unit, wp_date( 'd.m.Y', strtotime( $latest->reading_date ) ) ) ); ?>
					<?php endif; ?>
				</p>
			</div>

			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_swap_meter' ); ?>
				<input type='hidden' name='kgvvm_action' value='swap_meter' />
				<input type='hidden' name='id' value='<?php echo esc_attr( $id ); ?>' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><label for='kgvvm-new-meter-number'><?php echo esc_html__( 'Neue Zählernummer', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='new_meter_number' id='kgvvm-new-meter-number' type='text' class='regular-text' required value='' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-swap-date'><?php echo esc_html__( 'Tauschdatum', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='swap_date' id='kgvvm-swap-date' type='date' required value='<?php echo esc_attr( $today ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-new-calibration-year'><?php echo esc_html__( 'Eichjahr neuer Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<input name='new_calibration_year' id='kgvvm-new-calibration-year' type='number' min='2000' max='2100' step='1' value='' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Optional: Jahr der Eichung des neuen Ersatz-Zählers.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<?php if ( $parcel ) : ?>
						<tr>
							<th scope='row'><label for='kgvvm-old-final-reading'><?php echo esc_html__( 'Endstand alter Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td>
								<input name='old_final_reading' id='kgvvm-old-final-reading' type='number' min='0' step='0.001' value='<?php echo esc_attr( $latest ? $latest->reading_value : '' ); ?>' />
								<p class='kgvvm-help'><?php echo esc_html( sprintf( __( 'Optionaler Abschlussstand in %s am Tauschdatum.', KGVVM_TEXT_DOMAIN ), $unit ) ); ?></p>
							</td>
						</tr>
						<tr>
							<th scope='row'><label for='kgvvm-new-initial-reading'><?php echo esc_html__( 'Startstand neuer Zähler', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td>
								<input name='new_initial_reading' id='kgvvm-new-initial-reading' type='number' min='0' step='0.001' required value='0' />
								<p class='kgvvm-help'><?php echo esc_html( sprintf( __( 'Dieser Stand wird direkt für die Parzelle mit %s gespeichert.', KGVVM_TEXT_DOMAIN ), $unit ) ); ?></p>
							</td>
						</tr>
					<?php else : ?>
						<tr>
							<th scope='row'><?php echo esc_html__( 'Hinweis', KGVVM_TEXT_DOMAIN ); ?></th>
							<td><?php echo esc_html__( 'Der alte Zähler ist aktuell keiner Parzelle zugeordnet. Der neue Zähler wird daher als freier Zähler angelegt.', KGVVM_TEXT_DOMAIN ); ?></td>
						</tr>
					<?php endif; ?>
					<tr>
						<th scope='row'><label for='kgvvm-swap-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td>
							<textarea name='swap_note' id='kgvvm-swap-note' rows='5' class='large-text'></textarea>
							<p class='kgvvm-help'><?php echo esc_html__( 'Der alte Zähler wird inaktiv gesetzt, die Historie bleibt erhalten und der neue Zähler übernimmt automatisch die Zuordnung.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
				</table>
				<?php submit_button( __( 'Zähler tauschen', KGVVM_TEXT_DOMAIN ), 'primary', 'submit', false ); ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-zaehler' ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
			</form>
		</div>
		<?php
	}

	/**
	 * Render tenant page.
	 *
	 * @return void
	 */
	public function render_tenants_page() {
		$this->require_cap( 'edit_paechter' );
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'list';

		if ( 'form' === $view ) {
			$this->render_tenant_form();
			return;
		}

		$rows = $this->tenants->search( $_GET );
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Pächter', KGVVM_TEXT_DOMAIN ); ?></h1>
			<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-paechter', array( 'view' => 'form' ) ) ); ?>' class='page-title-action'><?php echo esc_html__( 'Neuer Pächter', KGVVM_TEXT_DOMAIN ); ?></a>
			<?php $this->render_notice(); ?>

			<form method='get' class='kgvvm-toolbar'>
				<input type='hidden' name='page' value='kgvvm-paechter' />
				<div class='kgvvm-filters'>
					<input type='search' name='s' value='<?php echo esc_attr( isset( $_GET['s'] ) ? wp_unslash( $_GET['s'] ) : '' ); ?>' placeholder='<?php echo esc_attr__( 'Pächter suchen …', KGVVM_TEXT_DOMAIN ); ?>' />
					<select name='status'>
						<option value=''><?php echo esc_html__( 'Alle Status', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='active' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'active' ); ?>><?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></option>
						<option value='inactive' <?php selected( isset( $_GET['status'] ) ? wp_unslash( $_GET['status'] ) : '', 'inactive' ); ?>><?php echo esc_html__( 'Inaktiv', KGVVM_TEXT_DOMAIN ); ?></option>
					</select>
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<table class='widefat striped'>
				<thead>
					<tr>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-paechter', 'last_name' ) ); ?>'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Telefon', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'E-Mail', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><a href='<?php echo esc_url( $this->sort_url( 'kgvvm-paechter', 'contract_start' ) ); ?>'><?php echo esc_html__( 'Vertragsbeginn', KGVVM_TEXT_DOMAIN ); ?></a></th>
						<th><?php echo esc_html__( 'Parzellen', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Mitglieder', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Status', KGVVM_TEXT_DOMAIN ); ?></th>
						<th><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr><td colspan='8'><?php echo esc_html__( 'Noch keine Pächter vorhanden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<?php $tenant_members = $this->assignments->get_members_for_tenant( $row->id ); ?>
							<tr>
								<td><strong><?php echo esc_html( $row->last_name . ', ' . $row->first_name ); ?></strong></td>
								<td><?php echo esc_html( $row->phone ? $row->phone : '—' ); ?></td>
								<td><?php echo esc_html( $row->email ? $row->email : '—' ); ?></td>
								<td><?php echo esc_html( $row->contract_start ? wp_date( 'd.m.Y', strtotime( $row->contract_start ) ) : '—' ); ?></td>
								<td><?php echo esc_html( (string) $row->parcel_count ); ?></td>
								<td>
									<?php if ( empty( $tenant_members ) ) : ?>
										—
									<?php else : ?>
										<?php echo esc_html( implode( ', ', array_map( array( $this, 'format_tenant_member_label' ), $tenant_members ) ) ); ?>
									<?php endif; ?>
								</td>
								<td><?php echo $this->status_badge( (int) $row->is_active === 1 ? __( 'Aktiv', KGVVM_TEXT_DOMAIN ) : __( 'Inaktiv', KGVVM_TEXT_DOMAIN ), (int) $row->is_active === 1 ? 'green' : 'gray' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
								<td>
									<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-paechter', array( 'view' => 'form', 'id' => $row->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
									|
									<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-paechter', array( 'kgvvm_action' => 'delete_tenant', 'id' => $row->id ) ), 'kgvvm_delete_tenant_' . $row->id ) ); ?>' onclick='return confirm("<?php echo esc_js( __( 'Pächter wirklich löschen?', KGVVM_TEXT_DOMAIN ) ); ?>");'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>
		</div>
		<?php
	}

	/**
	 * Render tenant form.
	 *
	 * @return void
	 */
	private function render_tenant_form() {
		$id             = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$tenant         = $id ? $this->tenants->find( $id ) : null;
		$tenant_members = $id ? $this->assignments->get_members_for_tenant( $id ) : array();
		?>
		<div class='wrap'>
			<h1><?php echo esc_html( $id ? __( 'Pächter bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neuer Pächter', KGVVM_TEXT_DOMAIN ) ); ?></h1>
			<?php $this->render_notice(); ?>
			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_save_tenant' ); ?>
				<input type='hidden' name='kgvvm_action' value='save_tenant' />
				<input type='hidden' name='id' value='<?php echo esc_attr( $id ); ?>' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-first-name'><?php echo esc_html__( 'Vorname', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='first_name' id='kgvvm-tenant-first-name' type='text' class='regular-text' required value='<?php echo esc_attr( $tenant ? $tenant->first_name : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-last-name'><?php echo esc_html__( 'Nachname', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='last_name' id='kgvvm-tenant-last-name' type='text' class='regular-text' required value='<?php echo esc_attr( $tenant ? $tenant->last_name : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-address'><?php echo esc_html__( 'Adresse', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><textarea name='address' id='kgvvm-tenant-address' rows='4' class='large-text'><?php echo esc_textarea( $tenant ? $tenant->address : '' ); ?></textarea></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-phone'><?php echo esc_html__( 'Telefon', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='phone' id='kgvvm-tenant-phone' type='text' class='regular-text' value='<?php echo esc_attr( $tenant ? $tenant->phone : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-email'><?php echo esc_html__( 'E-Mail', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='email' id='kgvvm-tenant-email' type='email' class='regular-text' value='<?php echo esc_attr( $tenant ? $tenant->email : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-contract-start'><?php echo esc_html__( 'Vertragsbeginn', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='contract_start' id='kgvvm-tenant-contract-start' type='date' required value='<?php echo esc_attr( $tenant ? $tenant->contract_start : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-contract-end'><?php echo esc_html__( 'Vertragsende', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><input name='contract_end' id='kgvvm-tenant-contract-end' type='date' value='<?php echo esc_attr( $tenant ? $tenant->contract_end : '' ); ?>' /></td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Aktiv', KGVVM_TEXT_DOMAIN ); ?></th>
						<td><label><input type='checkbox' name='is_active' value='1' <?php checked( $tenant ? (int) $tenant->is_active : 1, 1 ); ?> /> <?php echo esc_html__( 'Pächter ist aktiv', KGVVM_TEXT_DOMAIN ); ?></label></td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Zugeordnete Mitglieder', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<?php if ( empty( $tenant_members ) ) : ?>
								<p class='kgvvm-help'><?php echo esc_html__( 'Aktuell sind über die zugeordneten Parzellen keine Mitglieder verknüpft.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php else : ?>
								<ul>
									<?php foreach ( $tenant_members as $member ) : ?>
										<li><?php echo esc_html( $this->format_tenant_member_label( $member ) ); ?></li>
									<?php endforeach; ?>
								</ul>
								<p class='kgvvm-help'><?php echo esc_html__( 'Nur Anzeige: Die Zuordnung der Mitglieder erfolgt bei den Parzellen und ist hier nicht bearbeitbar.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php endif; ?>
						</td>
					</tr>
					<tr>
						<th scope='row'><label for='kgvvm-tenant-note'><?php echo esc_html__( 'Bemerkung', KGVVM_TEXT_DOMAIN ); ?></label></th>
						<td><textarea name='note' id='kgvvm-tenant-note' rows='5' class='large-text'><?php echo esc_textarea( $tenant ? $tenant->note : '' ); ?></textarea></td>
					</tr>
				</table>
				<?php submit_button( $id ? __( 'Pächter aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Pächter anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-paechter' ) ); ?>' class='button-secondary'><?php echo esc_html__( 'Zurück', KGVVM_TEXT_DOMAIN ); ?></a>
			</form>
		</div>
		<?php
	}

	/**
	 * Render the member page with assigned parcels and meter reading forms.
	 *
	 * @return void
	 */
	public function render_my_parcels_page() {
		$this->require_cap( 'view_assigned_parcels' );

		$user_id = get_current_user_id();
		$parcels = $this->assignments->get_parcels_for_user( $user_id );
		?>
		<div class='wrap'>
			<div class='kgvvm-toolbar'>
				<h1><?php echo esc_html__( 'Meine Parzellen', KGVVM_TEXT_DOMAIN ); ?></h1>
			</div>
			<?php $this->render_notice(); ?>

			<?php if ( empty( $parcels ) ) : ?>
				<div class='notice notice-info'><p><?php echo esc_html__( 'Ihnen ist aktuell keine Parzelle zugewiesen.', KGVVM_TEXT_DOMAIN ); ?></p></div>
			<?php else : ?>
				<?php foreach ( $parcels as $parcel ) : ?>
					<?php
					$water_meter  = $this->meters->get_assigned_to_parcel( $parcel->id, 'water' );
					$power_meter  = $this->meters->get_assigned_to_parcel( $parcel->id, 'power' );
					$water_latest = $water_meter ? $this->readings->get_latest_for_meter( $water_meter->id ) : null;
					$power_latest = $power_meter ? $this->readings->get_latest_for_meter( $power_meter->id ) : null;
					$recent       = $this->readings->get_recent_for_parcel( $parcel->id, 8 );
					?>
					<div class='kgvvm-card' style='margin-bottom:20px;'>
						<div class='kgvvm-toolbar'>
							<h2><?php echo esc_html( $parcel->label ); ?></h2>
							<a class='button button-secondary' href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-my-parcels', array( 'kgvvm_action' => 'export_readings_csv', 'parcel_id' => $parcel->id ) ), 'kgvvm_export_readings_' . $parcel->id ) ); ?>'><?php echo esc_html__( 'CSV-Export Ablesungen', KGVVM_TEXT_DOMAIN ); ?></a>
						</div>
						<p>
							<strong><?php echo esc_html__( 'Sparte:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo esc_html( $parcel->section_name ? $parcel->section_name : '—' ); ?><br />
							<strong><?php echo esc_html__( 'Fläche:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo null !== $parcel->area ? esc_html( number_format_i18n( (float) $parcel->area, 2 ) . ' m²' ) : '—'; ?><br />
							<strong><?php echo esc_html__( 'Pacht:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo isset( $parcel->annual_rent ) && null !== $parcel->annual_rent ? esc_html( $this->format_currency( $parcel->annual_rent ) ) : '—'; ?><br />
							<strong><?php echo esc_html__( 'Status:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo esc_html( $this->parcel_status_label( $parcel->status ) ); ?>
						</p>
						<p class='kgvvm-help'><?php echo esc_html__( 'Die Parzellendaten sind schreibgeschützt. Sie können hier nur Ihre Zählerstände erfassen und exportieren.', KGVVM_TEXT_DOMAIN ); ?></p>
						<?php if ( ! empty( $parcel->note ) ) : ?>
							<p><strong><?php echo esc_html__( 'Bemerkung:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo esc_html( $parcel->note ); ?></p>
						<?php endif; ?>

						<div class='kgvvm-grid'>
							<?php foreach ( array(
								'water' => array( 'meter' => $water_meter, 'latest' => $water_latest, 'label' => __( 'Wasserzähler', KGVVM_TEXT_DOMAIN ) ),
								'power' => array( 'meter' => $power_meter, 'latest' => $power_latest, 'label' => __( 'Stromzähler', KGVVM_TEXT_DOMAIN ) ),
							) as $meter_config ) : ?>
								<div class='kgvvm-card'>
									<h3><?php echo esc_html( $meter_config['label'] ); ?></h3>
									<?php if ( empty( $meter_config['meter'] ) ) : ?>
										<p><?php echo esc_html__( 'Für diese Parzelle ist aktuell kein Zähler hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
									<?php else : ?>
										<p>
											<strong><?php echo esc_html__( 'Zählernummer:', KGVVM_TEXT_DOMAIN ); ?></strong> <?php echo esc_html( $meter_config['meter']->meter_number ); ?><br />
											<strong><?php echo esc_html__( 'Letzte Ablesung:', KGVVM_TEXT_DOMAIN ); ?></strong>
											<?php if ( $meter_config['latest'] ) : ?>
												<?php echo esc_html( $this->format_meter_value_with_unit( $meter_config['latest']->reading_value, $meter_config['meter']->type ) . ' am ' . wp_date( 'd.m.Y', strtotime( $meter_config['latest']->reading_date ) ) ); ?>
											<?php else : ?>
												<?php echo esc_html__( 'Noch keine Ablesung vorhanden', KGVVM_TEXT_DOMAIN ); ?>
											<?php endif; ?>
										</p>
										<form method='post'>
											<?php wp_nonce_field( 'kgvvm_save_meter_reading' ); ?>
											<input type='hidden' name='kgvvm_action' value='save_meter_reading' />
											<input type='hidden' name='parcel_id' value='<?php echo esc_attr( $parcel->id ); ?>' />
											<input type='hidden' name='meter_id' value='<?php echo esc_attr( $meter_config['meter']->id ); ?>' />
											<p>
												<label><?php echo esc_html__( 'Ablesedatum', KGVVM_TEXT_DOMAIN ); ?><br />
													<input type='date' name='reading_date' required value='<?php echo esc_attr( wp_date( 'Y-m-d' ) ); ?>' />
												</label>
											</p>
											<p>
												<label><?php echo esc_html__( 'Zählerstand', KGVVM_TEXT_DOMAIN ); ?> (<?php echo esc_html( $this->meter_unit_label( $meter_config['meter']->type ) ); ?>)<br />
													<input type='number' min='0' step='0.001' name='reading_value' required />
												</label>
											</p>
											<p>
												<label><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?><br />
													<textarea name='note' rows='3' class='large-text'></textarea>
												</label>
											</p>
											<?php submit_button( __( 'Ablesung speichern', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', false ); ?>
										</form>
									<?php endif; ?>
								</div>
							<?php endforeach; ?>
						</div>

						<div class='kgvvm-card'>
							<h3><?php echo esc_html__( 'Letzte Einträge', KGVVM_TEXT_DOMAIN ); ?></h3>
							<?php if ( empty( $recent ) ) : ?>
								<p><?php echo esc_html__( 'Noch keine Ablesungen für diese Parzelle erfasst.', KGVVM_TEXT_DOMAIN ); ?></p>
							<?php else : ?>
								<table class='widefat striped'>
									<thead>
										<tr>
											<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
											<th><?php echo esc_html__( 'Typ', KGVVM_TEXT_DOMAIN ); ?></th>
											<th><?php echo esc_html__( 'Zähler', KGVVM_TEXT_DOMAIN ); ?></th>
											<th><?php echo esc_html__( 'Stand', KGVVM_TEXT_DOMAIN ); ?></th>
											<th><?php echo esc_html__( 'Flag', KGVVM_TEXT_DOMAIN ); ?></th>
										</tr>
									</thead>
									<tbody>
										<?php foreach ( $recent as $reading ) : ?>
											<tr>
												<td><?php echo esc_html( wp_date( 'd.m.Y', strtotime( $reading->reading_date ) ) ); ?></td>
												<td><?php echo esc_html( $this->meter_type_label( $reading->type ) ); ?></td>
												<td><?php echo esc_html( $reading->meter_number ); ?></td>
												<td><?php echo esc_html( $this->format_meter_value_with_unit( $reading->reading_value, $reading->type ) ); ?></td>
												<td><?php echo wp_kses_post( $this->reading_flag_badge( $reading ) ); ?></td>
											</tr>
										<?php endforeach; ?>
									</tbody>
								</table>
							<?php endif; ?>
						</div>
					</div>
				<?php endforeach; ?>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render the shared club chat for members and board.
	 *
	 * @return void
	 */
	public function render_chat_page() {
		$this->require_chat_access();

		$rooms        = $this->get_chat_rooms();
		$requested    = isset( $_GET['room'] ) ? sanitize_key( wp_unslash( $_GET['room'] ) ) : '';
		$room_keys    = array_keys( $rooms );
		$current_room = ( $requested && isset( $rooms[ $requested ] ) ) ? $requested : reset( $room_keys );
		$messages     = $this->normalize_chat_messages( $this->chat->get_recent_messages( $current_room, 60 ) );
		$last_message = ! empty( $messages ) ? end( $messages ) : null;

		if ( ! empty( $messages ) ) {
			reset( $messages );
		}
		?>
		<div class='wrap'>
			<h1><?php echo esc_html__( 'Vereinschat', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>

			<div class='kgvvm-card'>
				<p><?php echo esc_html__( 'Hier können Mitglieder, Vorstand und Verwaltung direkt miteinander schreiben. Mitglieder sehen nur den Vereinschat sowie die Sparte ihrer zugewiesenen Parzelle. Der Raum „Vorstand“ ist nur für berechtigte Rollen sichtbar.', KGVVM_TEXT_DOMAIN ); ?></p>
			</div>

			<div class='kgvvm-chat-app' data-room='<?php echo esc_attr( $current_room ); ?>' data-current-user-id='<?php echo esc_attr( get_current_user_id() ); ?>'>
				<aside class='kgvvm-chat-sidebar kgvvm-card'>
					<h2><?php echo esc_html__( 'Räume', KGVVM_TEXT_DOMAIN ); ?></h2>
					<div class='kgvvm-chat-room-list'>
						<?php foreach ( $rooms as $room_key => $room ) : ?>
							<button
								type='button'
								class='button button-secondary kgvvm-chat-room<?php echo $current_room === $room_key ? ' is-active' : ''; ?>'
								data-chat-room='<?php echo esc_attr( $room_key ); ?>'
								data-chat-label='<?php echo esc_attr( $room['label'] ); ?>'
								data-chat-description='<?php echo esc_attr( $room['description'] ); ?>'
							>
								<?php echo esc_html( $room['label'] ); ?>
							</button>
						<?php endforeach; ?>
					</div>
				</aside>

				<section class='kgvvm-chat-panel kgvvm-card'>
					<div class='kgvvm-chat-panel__header'>
						<div>
							<h2 data-chat-room-title><?php echo esc_html( $rooms[ $current_room ]['label'] ); ?></h2>
							<p class='kgvvm-help' data-chat-room-description><?php echo esc_html( $rooms[ $current_room ]['description'] ); ?></p>
						</div>
						<span class='kgvvm-status kgvvm-status--blue'><?php echo esc_html__( 'nur intern', KGVVM_TEXT_DOMAIN ); ?></span>
					</div>

					<div class='kgvvm-chat-messages' data-chat-messages data-last-id='<?php echo esc_attr( $last_message ? $last_message['id'] : 0 ); ?>'>
						<?php if ( empty( $messages ) ) : ?>
							<p class='kgvvm-chat-empty'><?php echo esc_html__( 'Noch keine Nachrichten in diesem Raum.', KGVVM_TEXT_DOMAIN ); ?></p>
						<?php else : ?>
							<?php foreach ( $messages as $message ) : ?>
								<article class='kgvvm-chat-message<?php echo ! empty( $message['is_own'] ) ? ' kgvvm-chat-message--own' : ''; ?>' data-id='<?php echo esc_attr( $message['id'] ); ?>'>
									<div class='kgvvm-chat-message__meta'>
										<strong><?php echo esc_html( $message['user'] ); ?></strong>
										<?php if ( ! empty( $message['role'] ) ) : ?>
											 · <?php echo esc_html( $message['role'] ); ?>
										<?php endif; ?>
										 · <?php echo esc_html( $message['time'] ); ?>
									</div>
									<div class='kgvvm-chat-message__body'><?php echo nl2br( esc_html( $message['message'] ) ); ?></div>
								</article>
							<?php endforeach; ?>
						<?php endif; ?>
					</div>

					<form class='kgvvm-chat-form' data-chat-form>
						<label for='kgvvm-chat-input' class='screen-reader-text'><?php echo esc_html__( 'Nachricht', KGVVM_TEXT_DOMAIN ); ?></label>
						<textarea id='kgvvm-chat-input' rows='3' maxlength='1000' data-chat-input placeholder='<?php echo esc_attr__( 'Nachricht an den Verein schreiben …', KGVVM_TEXT_DOMAIN ); ?>'></textarea>
						<div class='kgvvm-chat-form__actions'>
							<span class='kgvvm-help' data-chat-status></span>
							<button type='submit' class='button button-primary'><?php echo esc_html__( 'Senden', KGVVM_TEXT_DOMAIN ); ?></button>
						</div>
					</form>
				</section>
			</div>
		</div>
		<?php
	}

	/**
	 * Render settings page.
	 *
	 * @return void
	 */
	public function render_settings_page() {
		$this->require_cap( Roles::SETTINGS_CAP );
		$settings = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );
		?>
		<div class='wrap'>
			<h1><?php echo esc_html__( 'Einstellungen', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>
			<form method='post'>
				<?php wp_nonce_field( 'kgvvm_save_settings' ); ?>
				<input type='hidden' name='kgvvm_action' value='save_settings' />
				<table class='form-table kgvvm-form-table'>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Mitgliederregel', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<label>
								<input type='checkbox' name='allow_multiple_member_parcels' value='1' <?php checked( ! empty( $settings['allow_multiple_member_parcels'] ), true ); ?> />
								<?php echo esc_html__( 'Ein Mitglied darf mehreren Parzellen zugeordnet werden.', KGVVM_TEXT_DOMAIN ); ?>
							</label>
							<p class='kgvvm-help'><?php echo esc_html__( 'Wenn diese Option deaktiviert ist, wird eine Mehrfachzuordnung bei der Parzellenpflege validiert und blockiert.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Plausibilitätsgrenze Wasser', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<input type='number' min='0' step='0.001' name='water_usage_alert_threshold' value='<?php echo esc_attr( $settings['water_usage_alert_threshold'] ); ?>' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Wenn der neue Wasserzählerstand den letzten Eintrag um mehr als diesen Wert in m³ erhöht, wird nach dem Speichern eine Warnung angezeigt.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Strom-Einheit', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<select name='power_unit'>
								<option value='kwh' <?php selected( $settings['power_unit'], 'kwh' ); ?>>kWh</option>
								<option value='mwh' <?php selected( $settings['power_unit'], 'mwh' ); ?>>MWh</option>
							</select>
							<p class='kgvvm-help'><?php echo esc_html__( 'Wasser wird immer in m³ geführt. Für Strom kann hier die gewünschte Einheit gewählt werden.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Plausibilitätsgrenze Strom', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<input type='number' min='0' step='0.001' name='power_usage_alert_threshold' value='<?php echo esc_attr( $settings['power_usage_alert_threshold'] ); ?>' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Wenn der neue Stromzählerstand den letzten Eintrag um mehr als diesen Wert in der gewählten Strom-Einheit erhöht, wird nach dem Speichern eine Warnung angezeigt.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'PDF / Briefkopf', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<p class='kgvvm-help'><?php echo esc_html__( 'Diese Angaben erscheinen in der Druckansicht und im TCPDF-Export der Jahresabrechnung.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Vereinsname', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<input type='text' name='pdf_club_name' class='regular-text' value='<?php echo esc_attr( $settings['pdf_club_name'] ); ?>' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Wird als Überschrift im PDF- und Druckkopf verwendet.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Logo-URL', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<input type='url' name='pdf_logo_url' class='regular-text code' value='<?php echo esc_attr( $settings['pdf_logo_url'] ); ?>' placeholder='https://example.de/logo.png' />
							<p class='kgvvm-help'><?php echo esc_html__( 'Optional: URL zu einem Vereinslogo aus der Mediathek oder einer öffentlich erreichbaren Bilddatei.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Adress- / Kontaktblock', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<textarea name='pdf_contact_block' rows='4' class='large-text'><?php echo esc_textarea( $settings['pdf_contact_block'] ); ?></textarea>
							<p class='kgvvm-help'><?php echo esc_html__( 'Mehrzeilig möglich, z. B. Adresse, Telefon, E-Mail oder Bankverbindung.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Einleitungstext', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<textarea name='pdf_intro_text' rows='3' class='large-text'><?php echo esc_textarea( $settings['pdf_intro_text'] ); ?></textarea>
							<p class='kgvvm-help'><?php echo esc_html__( 'Optionaler Hinweistext oberhalb der Abrechnungsdetails.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
					<tr>
						<th scope='row'><?php echo esc_html__( 'Fußtext / Hinweis', KGVVM_TEXT_DOMAIN ); ?></th>
						<td>
							<textarea name='pdf_footer_text' rows='3' class='large-text'><?php echo esc_textarea( $settings['pdf_footer_text'] ); ?></textarea>
							<p class='kgvvm-help'><?php echo esc_html__( 'Erscheint unterhalb der Jahresabrechnung, z. B. als Zahlungs- oder Rückfragehinweis.', KGVVM_TEXT_DOMAIN ); ?></p>
						</td>
					</tr>
				</table>
				<?php submit_button( __( 'Einstellungen speichern', KGVVM_TEXT_DOMAIN ) ); ?>
			</form>

			<hr />
			<h2><?php echo esc_html__( 'Datensicherung', KGVVM_TEXT_DOMAIN ); ?></h2>

			<div class='kgvvm-grid'>
				<div class='kgvvm-card'>
					<h3><?php echo esc_html__( 'Export', KGVVM_TEXT_DOMAIN ); ?></h3>
					<p><?php echo esc_html__( 'Exportiert alle Plugin-Tabellen (Sparten, Parzellen, Zähler, Verbrauch, Kosten, Arbeitsstunden usw.) sowie die zugehörigen WordPress-Mitgliederkonten als JSON-Datei.', KGVVM_TEXT_DOMAIN ); ?></p>
					<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-settings', array( 'kgvvm_action' => 'export_plugin_data' ) ), 'kgvvm_export_plugin_data' ) ); ?>'
					   class='button button-secondary'>
						<?php echo esc_html__( 'JSON-Export herunterladen', KGVVM_TEXT_DOMAIN ); ?>
					</a>
				</div>

				<div class='kgvvm-card'>
					<h3><?php echo esc_html__( 'Import', KGVVM_TEXT_DOMAIN ); ?></h3>
					<p class='notice notice-warning inline' style='margin:0 0 10px;padding:6px 10px;'>
						<?php echo esc_html__( 'Achtung: Der Import überschreibt alle vorhandenen Plugin-Daten vollständig. Bitte vorher einen Export erstellen.', KGVVM_TEXT_DOMAIN ); ?>
					</p>
					<form method='post' enctype='multipart/form-data'>
						<?php wp_nonce_field( 'kgvvm_import_plugin_data' ); ?>
						<input type='hidden' name='kgvvm_action' value='import_plugin_data' />
						<input type='file' name='kgvvm_import_file' accept='.json,application/json' required style='margin-bottom:8px;display:block;' />
						<?php submit_button( __( 'Import starten', KGVVM_TEXT_DOMAIN ), 'secondary', 'submit', false ); ?>
					</form>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Handle AJAX polling for chat messages.
	 *
	 * @return void
	 */
	public function ajax_fetch_chat_messages() {
		$this->require_chat_access();
		check_ajax_referer( 'kgvvm_chat_nonce', 'nonce' );

		$room_key = isset( $_POST['room'] ) ? sanitize_key( wp_unslash( $_POST['room'] ) ) : 'general';
		$after_id = absint( isset( $_POST['after_id'] ) ? wp_unslash( $_POST['after_id'] ) : 0 );

		if ( ! $this->is_chat_room_allowed( $room_key ) ) {
			wp_send_json_error( array( 'message' => __( 'Sie dürfen diesen Chatraum nicht öffnen.', KGVVM_TEXT_DOMAIN ) ), 403 );
		}

		wp_send_json_success(
			array(
				'messages' => $this->normalize_chat_messages( $this->chat->get_recent_messages( $room_key, $after_id > 0 ? 100 : 60, $after_id ) ),
			)
		);
	}

	/**
	 * Handle AJAX submission for one new chat message.
	 *
	 * @return void
	 */
	public function ajax_send_chat_message() {
		$this->require_chat_access();
		check_ajax_referer( 'kgvvm_chat_nonce', 'nonce' );

		$room_key = isset( $_POST['room'] ) ? sanitize_key( wp_unslash( $_POST['room'] ) ) : 'general';
		$message  = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : '';
		$message  = trim( substr( $message, 0, 1000 ) );

		if ( ! $this->is_chat_room_allowed( $room_key ) ) {
			wp_send_json_error( array( 'message' => __( 'Sie dürfen in diesem Chatraum nicht schreiben.', KGVVM_TEXT_DOMAIN ) ), 403 );
		}

		if ( '' === $message ) {
			wp_send_json_error( array( 'message' => __( 'Bitte zuerst eine Nachricht eingeben.', KGVVM_TEXT_DOMAIN ) ), 400 );
		}

		$created = $this->chat->save_message( $room_key, get_current_user_id(), $message );

		if ( ! $created ) {
			wp_send_json_error( array( 'message' => __( 'Die Nachricht konnte nicht gespeichert werden.', KGVVM_TEXT_DOMAIN ) ), 500 );
		}

		wp_send_json_success(
			array(
				'messages' => $this->normalize_chat_messages( array( $created ) ),
			)
		);
	}

	/**
	 * Return plugin settings defaults.
	 *
	 * @return array
	 */
	private function get_settings_defaults() {
		return array(
			'allow_multiple_member_parcels' => 1,
			'water_usage_alert_threshold'   => 25,
			'power_usage_alert_threshold'   => 1000,
			'power_unit'                    => 'kwh',
			'pdf_club_name'                 => get_bloginfo( 'name' ),
			'pdf_logo_url'                  => '',
			'pdf_contact_block'             => '',
			'pdf_intro_text'                => __( 'Diese Jahresabrechnung wurde automatisch mit der KGV Vereinsverwaltung erstellt.', KGVVM_TEXT_DOMAIN ),
			'pdf_footer_text'               => __( 'Bitte prüfen Sie die Angaben und melden Sie Rückfragen an den Vorstand.', KGVVM_TEXT_DOMAIN ),
		);
	}

	/**
	 * Export one parcel's reading history as CSV.
	 *
	 * @return void
	 */
	private function export_readings_csv() {
		$this->require_cap( 'view_assigned_parcels' );

		$parcel_id = absint( isset( $_GET['parcel_id'] ) ? $_GET['parcel_id'] : 0 );
		$nonce     = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';

		if ( ! $parcel_id || ! wp_verify_nonce( $nonce, 'kgvvm_export_readings_' . $parcel_id ) ) {
			$this->redirect_with_notice( 'kgvvm-my-parcels', 'error', __( 'Der CSV-Export wurde aus Sicherheitsgründen abgebrochen.', KGVVM_TEXT_DOMAIN ) );
		}

		if ( ! current_user_can( 'manage_kleingarten' ) && ! $this->assignments->user_has_parcel( get_current_user_id(), $parcel_id ) ) {
			wp_die( esc_html__( 'Sie dürfen nur die Ablesungen Ihrer eigenen Parzellen exportieren.', KGVVM_TEXT_DOMAIN ) );
		}

		$parcel = $this->parcels->find( $parcel_id );

		if ( ! $parcel ) {
			$this->redirect_with_notice( 'kgvvm-my-parcels', 'error', __( 'Die gewünschte Parzelle wurde nicht gefunden.', KGVVM_TEXT_DOMAIN ) );
		}

		$rows = $this->readings->get_all_for_parcel( $parcel_id );

		nocache_headers();
		header( 'Content-Type: text/csv; charset=utf-8' );
		header( 'Content-Disposition: attachment; filename=ablesungen-' . sanitize_file_name( $parcel->label ) . '-' . gmdate( 'Ymd-His' ) . '.csv' );

		$output = fopen( 'php://output', 'w' );

		if ( false === $output ) {
			exit;
		}

		fwrite( $output, "\xEF\xBB\xBF" );
		fputcsv( $output, array( 'Parzelle', 'Datum', 'Typ', 'Zählernummer', 'Zählerstand', 'Einheit', 'Flag', 'Notiz' ), ';' );

		foreach ( $rows as $row ) {
			fputcsv(
				$output,
				array(
					$parcel->label,
					wp_date( 'd.m.Y', strtotime( $row->reading_date ) ),
					$this->meter_type_label( $row->type ),
					$row->meter_number,
					$this->format_meter_value( $row->reading_value, $row->type ),
					$this->meter_unit_label( $row->type ),
					! empty( $row->is_self_reading ) ? __( 'Selbstablesung', KGVVM_TEXT_DOMAIN ) : '',
					$row->note,
				),
				';'
			);
		}

		fclose( $output );
		exit;
	}

	/**
	 * Ensure the current user may access the chat.
	 *
	 * @return void
	 */
	private function require_chat_access() {
		if ( ! current_user_can( 'view_assigned_parcels' ) && ! current_user_can( 'manage_kleingarten' ) ) {
			wp_die( esc_html__( 'Sie haben keine Berechtigung für den Vereinschat.', KGVVM_TEXT_DOMAIN ) );
		}
	}

	/**
	 * List all chat rooms visible for the current user.
	 *
	 * @return array
	 */
	private function get_chat_rooms() {
		$rooms = array(
			'general' => array(
				'label'       => __( 'Vereinschat', KGVVM_TEXT_DOMAIN ),
				'description' => __( 'Offener Austausch für alle Mitglieder, Vorstand und Verwaltung.', KGVVM_TEXT_DOMAIN ),
			),
		);

		$is_management = current_user_can( Roles::SETTINGS_CAP ) || current_user_can( 'manage_kleingarten' );

		if ( $is_management ) {
			$rooms['board'] = array(
				'label'       => __( 'Vorstand', KGVVM_TEXT_DOMAIN ),
				'description' => __( 'Interner Raum für Vorstand und Verwaltung.', KGVVM_TEXT_DOMAIN ),
			);
		}

		$sections = array();

		if ( $is_management ) {
			$sections = $this->sections->all_for_options();
		} else {
			$user_parcels   = $this->assignments->get_parcels_for_user( get_current_user_id() );
			$seen_sections  = array();

			foreach ( (array) $user_parcels as $parcel ) {
				$section_id = ! empty( $parcel->section_id ) ? (int) $parcel->section_id : 0;

				if ( $section_id > 0 && ! isset( $seen_sections[ $section_id ] ) ) {
					$section = $this->sections->find( $section_id );

					if ( $section ) {
						$sections[] = $section;
					}

					$seen_sections[ $section_id ] = true;
				}
			}
		}

		foreach ( (array) $sections as $section ) {
			$section_id   = ! empty( $section->id ) ? (int) $section->id : 0;
			$section_name = ! empty( $section->name ) ? $section->name : '';

			if ( $section_id < 1 ) {
				continue;
			}

			$section_room_key           = 'section-' . $section_id;
			$rooms[ $section_room_key ] = array(
				'label'       => sprintf( __( 'Sparte: %s', KGVVM_TEXT_DOMAIN ), $section_name ? $section_name : $section_id ),
				'description' => sprintf( __( 'Austausch für Mitglieder der Sparte %s.', KGVVM_TEXT_DOMAIN ), $section_name ? $section_name : $section_id ),
			);
		}

		return $rooms;
	}

	/**
	 * Check whether a chat room is available for the current user.
	 *
	 * @param string $room_key Room key.
	 * @return bool
	 */
	private function is_chat_room_allowed( $room_key ) {
		$rooms = $this->get_chat_rooms();
		return isset( $rooms[ sanitize_key( $room_key ) ] );
	}

	/**
	 * Normalize chat messages for HTML and AJAX responses.
	 *
	 * @param array $messages Raw database rows.
	 * @return array
	 */
	private function normalize_chat_messages( $messages ) {
		$current_user_id = get_current_user_id();
		$normalized      = array();

		foreach ( (array) $messages as $message ) {
			$normalized[] = array(
				'id'      => isset( $message->id ) ? (int) $message->id : 0,
				'user_id' => isset( $message->user_id ) ? (int) $message->user_id : 0,
				'user'    => ! empty( $message->display_name ) ? (string) $message->display_name : __( 'Mitglied', KGVVM_TEXT_DOMAIN ),
				'role'    => $this->get_chat_role_label( isset( $message->user_id ) ? (int) $message->user_id : 0 ),
				'message' => isset( $message->message ) ? (string) $message->message : '',
				'time'    => ! empty( $message->created_at ) ? wp_date( 'd.m.Y H:i', strtotime( $message->created_at ) ) : '',
				'is_own'  => isset( $message->user_id ) && (int) $message->user_id === (int) $current_user_id,
			);
		}

		return $normalized;
	}

	/**
	 * Resolve a readable sender label.
	 *
	 * @param int $user_id Sender ID.
	 * @return string
	 */
	private function get_chat_role_label( $user_id ) {
		$user_id = absint( $user_id );

		if ( $user_id > 0 && ( user_can( $user_id, Roles::SETTINGS_CAP ) || user_can( $user_id, 'manage_kleingarten' ) ) ) {
			return __( 'Vorstand / Verwaltung', KGVVM_TEXT_DOMAIN );
		}

		return __( 'Mitglied', KGVVM_TEXT_DOMAIN );
	}

	/**
	 * Return a visible badge for self-readings.
	 *
	 * @param object $reading Reading row.
	 * @return string
	 */
	private function reading_flag_badge( $reading ) {
		if ( empty( $reading ) || empty( $reading->is_self_reading ) ) {
			return '—';
		}

		return $this->status_badge( __( 'Selbstablesung', KGVVM_TEXT_DOMAIN ), 'blue' );
	}

	/**
	 * Build a plausibility warning for unusual jumps.
	 *
	 * @param object      $meter       Meter object.
	 * @param object|null $latest      Latest previous reading.
	 * @param float       $new_reading New reading value.
	 * @return string
	 */
	private function build_meter_jump_warning_message( $meter, $latest, $new_reading ) {
		if ( ! $meter || ! $latest ) {
			return '';
		}

		$settings  = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );
		$threshold = 'water' === $meter->type ? (float) $settings['water_usage_alert_threshold'] : (float) $settings['power_usage_alert_threshold'];

		if ( $threshold <= 0 ) {
			return '';
		}

		$delta = (float) $new_reading - (float) $latest->reading_value;

		if ( $delta <= $threshold ) {
			return '';
		}

		return sprintf(
			/* translators: 1: meter number, 2: consumption increase */
			__( 'Auffälliger Verbrauchssprung beim Zähler %1$s: +%2$s seit der letzten Ablesung. Bitte prüfen.', KGVVM_TEXT_DOMAIN ),
			$meter->meter_number,
			$this->format_meter_value_with_unit( $delta, $meter->type )
		);
	}

	/**
	 * Format one meter value for display.
	 *
	 * @param float|int|string $value Numeric value.
	 * @param string           $type  Meter type.
	 * @return string
	 */
	private function format_meter_value( $value, $type ) {
		$number = (float) $value;

		if ( 'water' === $type ) {
			return number_format( $number, 2, ',', '' );
		}

		return number_format( $number, 0, '', '' );
	}

	/**
	 * Format one meter value together with its unit.
	 *
	 * @param float|int|string $value Numeric value.
	 * @param string           $type  Meter type.
	 * @return string
	 */
	private function format_meter_value_with_unit( $value, $type ) {
		return $this->format_meter_value( $value, $type ) . ' ' . $this->meter_unit_label( $type );
	}

	/**
	 * Format a currency value for display.
	 *
	 * @param float|int|string $value Numeric value.
	 * @return string
	 */
	private function format_currency( $value ) {
		return number_format_i18n( (float) $value, 2 ) . ' €';
	}

	/**
	 * Format a yearly unit price for display.
	 *
	 * @param float|int|string $value Numeric value.
	 * @param string           $unit  Unit label.
	 * @return string
	 */
	private function format_price_per_unit( $value, $unit ) {
		return number_format_i18n( (float) $value, 4 ) . ' € / ' . $unit;
	}

	/**
	 * Normalize the saved distribution type for one cost entry.
	 *
	 * @param object|array $entry Cost entry.
	 * @return string
	 */
	private function get_cost_distribution_type( $entry ) {
		$type = 'parcel';

		if ( is_object( $entry ) && isset( $entry->distribution_type ) ) {
			$type = sanitize_key( (string) $entry->distribution_type );
		} elseif ( is_array( $entry ) && isset( $entry['distribution_type'] ) ) {
			$type = sanitize_key( (string) $entry['distribution_type'] );
		}

		return in_array( $type, array( 'parcel', 'member' ), true ) ? $type : 'parcel';
	}

	/**
	 * Return the UI label for one cost distribution type.
	 *
	 * @param string $distribution_type Distribution type.
	 * @return string
	 */
	private function get_cost_distribution_label( $distribution_type ) {
		return 'member' === $distribution_type ? __( 'Mitglied', KGVVM_TEXT_DOMAIN ) : __( 'Parzelle', KGVVM_TEXT_DOMAIN );
	}

	/**
	 * Resolve the amount per parcel/member for one cost entry.
	 *
	 * @param object|array $entry        Cost entry.
	 * @param int          $parcel_count Active parcel count.
	 * @param int          $member_count Active member count.
	 * @return float
	 */
	private function get_cost_unit_amount( $entry, $parcel_count = 0, $member_count = 0 ) {
		if ( is_object( $entry ) && isset( $entry->unit_amount ) && '' !== trim( (string) $entry->unit_amount ) ) {
			return (float) $entry->unit_amount;
		}

		if ( is_array( $entry ) && isset( $entry['unit_amount'] ) && '' !== trim( (string) $entry['unit_amount'] ) ) {
			return (float) $entry['unit_amount'];
		}

		$distribution_type = $this->get_cost_distribution_type( $entry );
		$total_cost        = 0.0;

		if ( is_object( $entry ) && isset( $entry->total_cost ) ) {
			$total_cost = (float) $entry->total_cost;
		} elseif ( is_array( $entry ) && isset( $entry['total_cost'] ) ) {
			$total_cost = (float) $entry['total_cost'];
		}

		$divisor = 'member' === $distribution_type ? max( 1, (int) $member_count ) : max( 1, (int) $parcel_count );

		return $divisor > 0 ? $total_cost / $divisor : 0.0;
	}

	/**
	 * Calculate the overall yearly total for one cost entry.
	 *
	 * @param object|array $entry        Cost entry.
	 * @param int          $parcel_count Active parcel count.
	 * @param int          $member_count Active member count.
	 * @return float
	 */
	private function get_cost_total_amount( $entry, $parcel_count = 0, $member_count = 0 ) {
		$distribution_type = $this->get_cost_distribution_type( $entry );
		$unit_amount       = $this->get_cost_unit_amount( $entry, $parcel_count, $member_count );
		$multiplier        = 'member' === $distribution_type ? max( 0, (int) $member_count ) : max( 0, (int) $parcel_count );

		if ( $multiplier < 1 ) {
			if ( is_object( $entry ) && isset( $entry->total_cost ) ) {
				return (float) $entry->total_cost;
			}

			if ( is_array( $entry ) && isset( $entry['total_cost'] ) ) {
				return (float) $entry['total_cost'];
			}
		}

		return round( $unit_amount * $multiplier, 2 );
	}

	/**
	 * Filter statement utility rows by meter type.
	 *
	 * @param array  $rows Utility rows.
	 * @param string $type Meter type.
	 * @return array
	 */
	private function get_statement_rows_by_meter_type( $rows, $type ) {
		return array_values(
			array_filter(
				(array) $rows,
				static function( $row ) use ( $type ) {
					return isset( $row->type ) && $type === $row->type;
				}
			)
		);
	}

	/**
	 * Build the detailed meter table for HTML/PDF annual statements.
	 *
	 * @param array  $rows   Statement rows.
	 * @param string $type   Meter type.
	 * @param bool   $is_pdf Whether PDF markup is requested.
	 * @return string
	 */
	private function build_statement_meter_detail_table( $rows, $type, $is_pdf = false ) {
		$rows      = array_values( (array) $rows );
		$unit      = 'power' === $type ? 'kWh' : 'm³';
		$empty_msg = 'power' === $type ? __( 'Keine auswertbaren Stromdaten vorhanden.', KGVVM_TEXT_DOMAIN ) : __( 'Keine auswertbaren Wasserdaten vorhanden.', KGVVM_TEXT_DOMAIN );

		ob_start();

		if ( empty( $rows ) ) {
			echo '<p>' . esc_html( $empty_msg ) . '</p>';
			return (string) ob_get_clean();
		}
		?>
		<?php if ( $is_pdf ) : ?>
		<table cellpadding="5" cellspacing="0" border="1">
		<?php else : ?>
		<table class='widefat striped'>
		<?php endif; ?>
			<?php if ( ! $is_pdf ) : ?>
			<thead>
			<?php endif; ?>
				<tr>
					<th><?php echo esc_html__( 'Seriennummer', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Stand alt', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Stand neu', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Verbrauch', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Einzelpreis', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Gesamtpreis', KGVVM_TEXT_DOMAIN ); ?></th>
					<th><?php echo esc_html__( 'Schwund', KGVVM_TEXT_DOMAIN ); ?></th>
				</tr>
			<?php if ( ! $is_pdf ) : ?>
			</thead>
			<tbody>
			<?php endif; ?>
			<?php foreach ( $rows as $row ) : ?>
				<tr>
					<td><?php echo esc_html( ! empty( $row->meter_number ) ? $row->meter_number : '—' ); ?></td>
					<td><?php echo esc_html( null !== $row->previous_value ? $this->format_meter_value_with_unit( $row->previous_value, $type ) : '—' ); ?></td>
					<td><?php echo esc_html( null !== $row->reading_value ? $this->format_meter_value_with_unit( $row->reading_value, $type ) : '—' ); ?></td>
					<td><?php echo esc_html( null !== $row->consumption ? $this->format_meter_value_with_unit( $row->consumption, $type ) : '—' ); ?></td>
					<td><?php echo esc_html( null !== $row->unit_price ? $this->format_price_per_unit( $row->unit_price, $unit ) : '—' ); ?></td>
					<td><?php echo esc_html( null !== $row->calculated_cost ? $this->format_currency( $row->calculated_cost ) : '—' ); ?></td>
					<td><?php echo esc_html( isset( $row->loss_value ) && null !== $row->loss_value ? $this->format_meter_value_with_unit( $row->loss_value, $type ) : '—' ); ?></td>
				</tr>
			<?php endforeach; ?>
			<?php if ( ! $is_pdf ) : ?>
			</tbody>
			<?php endif; ?>
		</table>
		<?php

		return (string) ob_get_clean();
	}

	/**
	 * Build a year/section lookup for saved consumption prices.
	 *
	 * @param array $rows Consumption rows.
	 * @return array
	 */
	private function build_consumption_rate_lookup( $rows ) {
		$lookup = array();
		$years  = array();

		foreach ( (array) $rows as $row ) {
			if ( empty( $row->section_id ) || empty( $row->reading_date ) ) {
				continue;
			}

			$years[] = (int) gmdate( 'Y', strtotime( $row->reading_date ) );
		}

		$years = array_values( array_unique( array_filter( $years ) ) );

		foreach ( $years as $year ) {
			$rates = $this->costs->get_section_prices( $year );

			foreach ( $rates as $rate ) {
				$lookup[ $year ][ (int) $rate->section_id ] = $rate;
			}
		}

		return $lookup;
	}

	/**
	 * Resolve the saved unit price for one consumption row.
	 *
	 * @param object $row         Consumption row.
	 * @param array  $rate_lookup Saved rate map.
	 * @return float|null
	 */
	private function get_consumption_unit_price( $row, $rate_lookup ) {
		$year       = ! empty( $row->reading_date ) ? (int) gmdate( 'Y', strtotime( $row->reading_date ) ) : 0;
		$section_id = ! empty( $row->section_id ) ? (int) $row->section_id : 0;

		if ( ! $year || ! $section_id || empty( $rate_lookup[ $year ][ $section_id ] ) ) {
			return null;
		}

		$rate = $rate_lookup[ $year ][ $section_id ];

		if ( 'power' === $row->type ) {
			return null !== $rate->power_price_per_kwh ? (float) $rate->power_price_per_kwh : null;
		}

		return null !== $rate->water_price_per_m3 ? (float) $rate->water_price_per_m3 : null;
	}

	/**
	 * Calculate the cost for a consumption delta.
	 *
	 * @param float|int|null $consumption Consumption amount.
	 * @param string         $type        Meter type.
	 * @param float|null     $unit_price  Saved price per unit.
	 * @return float|null
	 */
	private function calculate_consumption_cost( $consumption, $type, $unit_price ) {
		if ( null === $consumption || null === $unit_price ) {
			return null;
		}

		$amount = (float) $consumption;

		if ( 'power' === $type ) {
			$settings = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );

			if ( 'mwh' === $settings['power_unit'] ) {
				$amount *= 1000;
			}
		}

		return $amount * (float) $unit_price;
	}

	/**
	 * Return the display unit for a meter type.
	 *
	 * @param string $type Meter type.
	 * @return string
	 */
	private function meter_unit_label( $type ) {
		if ( 'water' === $type ) {
			return 'm³';
		}

		$settings = wp_parse_args( get_option( 'kgvvm_settings', array() ), $this->get_settings_defaults() );
		return 'mwh' === $settings['power_unit'] ? 'MWh' : 'kWh';
	}

	/**
	 * Build colored status badge HTML.
	 *
	 * @param string $label Visible label.
	 * @param string $color green|orange|gray.
	 * @return string
	 */
	private function status_badge( $label, $color ) {
		return '<span class="kgvvm-status kgvvm-status--' . esc_attr( $color ) . '">' . esc_html( $label ) . '</span>';
	}

	/**
	 * Human parcel status label.
	 *
	 * @param string $status Raw status.
	 * @return string
	 */
	private function parcel_status_label( $status ) {
		$map = array(
			'free'     => __( 'Frei', KGVVM_TEXT_DOMAIN ),
			'assigned' => __( 'Vergeben', KGVVM_TEXT_DOMAIN ),
			'reserved' => __( 'Reserviert', KGVVM_TEXT_DOMAIN ),
			'inactive' => __( 'Inaktiv', KGVVM_TEXT_DOMAIN ),
		);

		return isset( $map[ $status ] ) ? $map[ $status ] : $status;
	}

	/**
	 * Human meter type label.
	 *
	 * @param string $type water|power.
	 * @return string
	 */
	private function meter_type_label( $type ) {
		return 'water' === $type ? __( 'Wasser', KGVVM_TEXT_DOMAIN ) : __( 'Strom', KGVVM_TEXT_DOMAIN );
	}

	/**
	 * Format a short meter label for lists and select boxes.
	 *
	 * @param object $meter Meter record.
	 * @return string
	 */
	private function format_meter_short_label( $meter ) {
		$parts = array(
			$this->meter_type_label( $meter->type ),
			$meter->meter_number,
		);

		if ( ! empty( $meter->calibration_year ) ) {
			$parts[] = sprintf( __( 'Eichjahr %s', KGVVM_TEXT_DOMAIN ), $meter->calibration_year );
		}

		return implode( ' – ', array_filter( $parts ) );
	}

	/**
	 * Format one member label for tenant views.
	 *
	 * @param object $member Member row.
	 * @return string
	 */
	private function format_tenant_member_label( $member ) {
		$label = $member->display_name;

		if ( ! empty( $member->parcel_label ) ) {
			$label .= ' – ' . sprintf( __( 'Parzelle %s', KGVVM_TEXT_DOMAIN ), $member->parcel_label );
		}

		return $label;
	}

	/**
	 * Format a full tenant name.
	 *
	 * @param object $tenant Tenant row.
	 * @return string
	 */
	private function format_tenant_full_name( $tenant ) {
		return trim( $tenant->last_name . ', ' . $tenant->first_name, ' ,' );
	}

	/**
	 * Ensure capability.
	 *
	 * @param string $cap Capability name.
	 * @return void
	 */
	private function require_cap( $cap ) {
		if ( ! current_user_can( $cap ) ) {
			wp_die( esc_html__( 'Sie haben keine Berechtigung für diese Aktion.', KGVVM_TEXT_DOMAIN ) );
		}
	}

	/**
	 * Render one-time admin notice.
	 *
	 * @return void
	 */
	private function render_notice() {
		$key    = 'kgvvm_notice_' . get_current_user_id();
		$notice = get_transient( $key );

		if ( empty( $notice['message'] ) ) {
			return;
		}

		delete_transient( $key );
		$type = ! empty( $notice['type'] ) ? $notice['type'] : 'success';

		switch ( $type ) {
			case 'warning':
				$class = 'notice notice-warning';
				break;
			case 'success':
				$class = 'notice notice-success is-dismissible';
				break;
			default:
				$class = 'notice notice-error';
				break;
		}

		echo '<div class="' . esc_attr( $class ) . '"><p>' . esc_html( $notice['message'] ) . '</p></div>';
	}

	/**
	 * Redirect with a transient notice.
	 *
	 * @param string $page    Target page slug.
	 * @param string $type    success|error.
	 * @param string $message Notice text.
	 * @param array  $args    Extra query args.
	 * @return void
	 */
	private function redirect_with_notice( $page, $type, $message, $args = array() ) {
		set_transient(
			'kgvvm_notice_' . get_current_user_id(),
			array(
				'type'    => $type,
				'message' => wp_strip_all_tags( $message ),
			),
			60
		);

		wp_safe_redirect( $this->admin_url( $page, $args ) );
		exit;
	}

	/**
	 * Build admin page URL.
	 *
	 * @param string $page Page slug.
	 * @param array  $args Additional args.
	 * @return string
	 */
	private function admin_url( $page, $args = array() ) {
		return add_query_arg( array_merge( array( 'page' => $page ), $args ), admin_url( 'admin.php' ) );
	}

	/**
	 * Build sortable list URL while preserving filters.
	 *
	 * @param string $page   Page slug.
	 * @param string $column Sort column.
	 * @return string
	 */
	private function sort_url( $page, $column ) {
		$current_orderby = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : '';
		$current_order   = isset( $_GET['order'] ) ? strtolower( sanitize_key( wp_unslash( $_GET['order'] ) ) ) : 'asc';
		$next_order      = ( $current_orderby === $column && 'asc' === $current_order ) ? 'desc' : 'asc';

		$args = array();

		foreach ( $_GET as $key => $value ) {
			if ( in_array( $key, array( '_wpnonce', 'kgvvm_action', 'view', 'id' ), true ) ) {
				continue;
			}
			$args[ $key ] = wp_unslash( $value );
		}

		$args['page']    = $page;
		$args['orderby'] = $column;
		$args['order']   = $next_order;

		return add_query_arg( $args, admin_url( 'admin.php' ) );
	}

	// =========================================================================
	// ARBEITSSTUNDEN
	// =========================================================================

	/**
	 * Save work year configuration.
	 *
	 * @return void
	 */
	private function save_work_year_config() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_work_year_config' );

		$year                  = absint( isset( $_POST['entry_year'] ) ? $_POST['entry_year'] : 0 );
		$required_hours        = isset( $_POST['required_hours'] ) ? (float) $_POST['required_hours'] : 0;
		$price_per_missing_hour = isset( $_POST['price_per_missing_hour'] ) ? (float) $_POST['price_per_missing_hour'] : 0;

		if ( $year < 2000 || $year > 2100 ) {
			$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte ein gültiges Jahr eingeben.', KGVVM_TEXT_DOMAIN ) );
		}

		$this->work->save_year_config( $year, $required_hours, $price_per_missing_hour );
		$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Jahreseinstellungen wurden gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
	}

	/**
	 * Save work job (add/update).
	 *
	 * @return void
	 */
	private function save_work_job() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_work_job' );

		$id   = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$name = sanitize_text_field( wp_unslash( isset( $_POST['job_name'] ) ? $_POST['job_name'] : '' ) );

		if ( '' === $name ) {
			$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte einen Namen für die Arbeitsart eingeben.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
		}

		if ( $this->work->job_name_exists( $name, $id ) ) {
			$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Eine Arbeitsart mit diesem Namen existiert bereits.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
		}

		$this->work->save_job(
			array(
				'name'        => $name,
				'description' => isset( $_POST['job_description'] ) ? wp_unslash( $_POST['job_description'] ) : '',
			),
			$id
		);

		$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitsart wurde gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'tab' => 'jobs' ) );
	}

	/**
	 * Save work log entry (add/update).
	 *
	 * @return void
	 */
	private function save_work_log() {
		$this->require_cap( 'manage_kleingarten' );
		check_admin_referer( 'kgvvm_save_work_log' );

		$id        = absint( isset( $_POST['id'] ) ? $_POST['id'] : 0 );
		$job_id    = absint( isset( $_POST['job_id'] ) ? $_POST['job_id'] : 0 );
		$work_date = sanitize_text_field( wp_unslash( isset( $_POST['work_date'] ) ? $_POST['work_date'] : '' ) );
		$year      = $work_date ? (int) substr( $work_date, 0, 4 ) : (int) current_time( 'Y' );

		if ( '' === $work_date ) {
			$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte ein gültiges Datum eingeben.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
		}

		$raw_members = isset( $_POST['member_hours'] ) ? (array) wp_unslash( $_POST['member_hours'] ) : array();
		$members     = array();
		foreach ( $raw_members as $uid => $hours ) {
			$uid   = absint( $uid );
			$hours = (float) $hours;
			if ( $uid > 0 && $hours > 0 ) {
				$members[ $uid ] = $hours;
			}
		}

		if ( empty( $members ) ) {
			$this->redirect_with_notice( 'kgvvm-arbeit', 'error', __( 'Bitte mindestens ein Mitglied mit Stundenzahl zuordnen.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
		}

		$this->work->save_log(
			array(
				'job_id'    => $job_id,
				'work_date' => $work_date,
				'note'      => isset( $_POST['note'] ) ? wp_unslash( $_POST['note'] ) : '',
			),
			$id,
			$members
		);

		$this->redirect_with_notice( 'kgvvm-arbeit', 'success', __( 'Arbeitseintrag wurde gespeichert.', KGVVM_TEXT_DOMAIN ), array( 'year' => $year ) );
	}

	/**
	 * Render the Arbeitsstunden admin page.
	 *
	 * @return void
	 */
	public function render_work_page() {
		$this->require_cap( 'manage_kleingarten' );

		$tab           = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'logs';
		$selected_year = isset( $_GET['year'] ) ? absint( $_GET['year'] ) : (int) current_time( 'Y' );
		$selected_year = $selected_year > 0 ? $selected_year : (int) current_time( 'Y' );
		$edit_log_id   = absint( isset( $_GET['id'] ) ? $_GET['id'] : 0 );
		$edit_job_id   = absint( isset( $_GET['job_id'] ) ? $_GET['job_id'] : 0 );

		$years       = $this->work->get_years( $selected_year );
		$year_config = $this->work->get_year_config( $selected_year );
		$jobs        = $this->work->get_jobs();
		$all_members = $this->assignments->get_member_users();

		$edit_log = null;
		$edit_job = null;

		if ( $edit_log_id ) {
			$edit_log = $this->work->find_log( $edit_log_id );
		}
		if ( $edit_job_id ) {
			$edit_job = $this->work->find_job( $edit_job_id );
		}

		$logs    = $this->work->search_logs(
			array(
				'year'    => $selected_year,
				'orderby' => isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'work_date',
				'order'   => isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : 'DESC',
				's'       => isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '',
			)
		);
		$summary = $this->work->get_member_summary( $selected_year, $all_members );
		?>
		<div class='wrap'>
			<h1 class='wp-heading-inline'><?php echo esc_html__( 'Arbeitsstunden', KGVVM_TEXT_DOMAIN ); ?></h1>
			<?php $this->render_notice(); ?>

			<?php /* Year filter */ ?>
			<form method='get' class='kgvvm-toolbar' style='margin-top:12px;'>
				<input type='hidden' name='page' value='kgvvm-arbeit' />
				<input type='hidden' name='tab' value='<?php echo esc_attr( $tab ); ?>' />
				<div class='kgvvm-filters'>
					<label for='kgvvm-work-year'><?php echo esc_html__( 'Jahr', KGVVM_TEXT_DOMAIN ); ?></label>
					<select name='year' id='kgvvm-work-year'>
						<?php foreach ( $years as $year ) : ?>
							<option value='<?php echo esc_attr( $year ); ?>' <?php selected( $selected_year, $year ); ?>><?php echo esc_html( $year ); ?></option>
						<?php endforeach; ?>
					</select>
					<button class='button'><?php echo esc_html__( 'Filtern', KGVVM_TEXT_DOMAIN ); ?></button>
				</div>
			</form>

			<?php /* Year config card */ ?>
			<div class='kgvvm-card' style='max-width:560px;margin-top:16px;'>
				<h2><?php echo esc_html( sprintf( __( 'Jahreseinstellungen %d', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></h2>
				<form method='post'>
					<?php wp_nonce_field( 'kgvvm_save_work_year_config' ); ?>
					<input type='hidden' name='kgvvm_action' value='save_work_year_config' />
					<input type='hidden' name='entry_year' value='<?php echo esc_attr( $selected_year ); ?>' />
					<table class='form-table kgvvm-form-table'>
						<tr>
							<th scope='row'><label for='kgvvm-work-required-hours'><?php echo esc_html__( 'Pflichtstunden je Mitglied', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input type='number' min='0' step='0.5' name='required_hours' id='kgvvm-work-required-hours'
								value='<?php echo esc_attr( $year_config ? (float) $year_config->required_hours : 0 ); ?>' /> <?php echo esc_html__( 'Std.', KGVVM_TEXT_DOMAIN ); ?></td>
						</tr>
						<tr>
							<th scope='row'><label for='kgvvm-work-price'><?php echo esc_html__( 'Preis je fehlender Stunde', KGVVM_TEXT_DOMAIN ); ?></label></th>
							<td><input type='number' min='0' step='0.01' name='price_per_missing_hour' id='kgvvm-work-price'
								value='<?php echo esc_attr( $year_config ? (float) $year_config->price_per_missing_hour : 0 ); ?>' /> € / Std.</td>
						</tr>
					</table>
					<?php submit_button( __( 'Einstellungen speichern', KGVVM_TEXT_DOMAIN ), 'secondary' ); ?>
				</form>
			</div>

			<?php /* Tabs */ ?>
			<nav class='nav-tab-wrapper' style='margin-top:20px;'>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'logs' ) ) ); ?>'
					class='nav-tab <?php echo 'logs' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Geleistete Arbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'summary' ) ) ); ?>'
					class='nav-tab <?php echo 'summary' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Mitgliederübersicht', KGVVM_TEXT_DOMAIN ); ?></a>
				<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'jobs' ) ) ); ?>'
					class='nav-tab <?php echo 'jobs' === $tab ? 'nav-tab-active' : ''; ?>'><?php echo esc_html__( 'Arbeitsarten', KGVVM_TEXT_DOMAIN ); ?></a>
			</nav>

			<?php if ( 'jobs' === $tab ) : ?>
				<?php /* ---- ARBEITSARTEN ---- */ ?>
				<div class='kgvvm-card' style='margin-top:0;border-top:none;padding-top:20px;'>
					<h2><?php echo esc_html( $edit_job ? __( 'Arbeitsart bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neue Arbeitsart', KGVVM_TEXT_DOMAIN ) ); ?></h2>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_save_work_job' ); ?>
						<input type='hidden' name='kgvvm_action' value='save_work_job' />
						<input type='hidden' name='id' value='<?php echo esc_attr( $edit_job ? $edit_job->id : 0 ); ?>' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-job-name'><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input type='text' class='regular-text' name='job_name' id='kgvvm-job-name' required
									value='<?php echo esc_attr( $edit_job ? $edit_job->name : '' ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-job-description'><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><textarea name='job_description' id='kgvvm-job-description' rows='3' class='large-text'><?php echo esc_textarea( $edit_job ? $edit_job->description : '' ); ?></textarea></td>
							</tr>
						</table>
						<?php submit_button( $edit_job ? __( 'Arbeitsart aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Arbeitsart anlegen', KGVVM_TEXT_DOMAIN ) ); ?>
						<?php if ( $edit_job ) : ?>
							<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'tab' => 'jobs' ) ) ); ?>' class='button'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
						<?php endif; ?>
					</form>
				</div>

				<?php if ( ! empty( $jobs ) ) : ?>
				<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
					<thead>
						<tr>
							<th><?php echo esc_html__( 'Name', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Beschreibung', KGVVM_TEXT_DOMAIN ); ?></th>
							<th style='width:120px;'><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php foreach ( $jobs as $job ) : ?>
						<tr>
							<td><strong><?php echo esc_html( $job->name ); ?></strong></td>
							<td><?php echo esc_html( $job->description ); ?></td>
							<td>
								<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'tab' => 'jobs', 'year' => $selected_year, 'job_id' => $job->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
								&nbsp;|&nbsp;
								<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-arbeit', array( 'kgvvm_action' => 'delete_work_job', 'id' => $job->id, 'year' => $selected_year, 'tab' => 'jobs' ) ), 'kgvvm_delete_work_job_' . $job->id ) ); ?>'
									class='kgvvm-delete-link' onclick='return confirm("<?php echo esc_attr__( 'Diese Arbeitsart und alle zugehörigen Einträge wirklich löschen?', KGVVM_TEXT_DOMAIN ); ?>")'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
							</td>
						</tr>
						<?php endforeach; ?>
					</tbody>
				</table>
				<?php else : ?>
					<p class='kgvvm-help'><?php echo esc_html__( 'Noch keine Arbeitsarten angelegt.', KGVVM_TEXT_DOMAIN ); ?></p>
				<?php endif; ?>

			<?php elseif ( 'summary' === $tab ) : ?>
				<?php /* ---- MITGLIEDERÜBERSICHT ---- */ ?>
				<div style='margin-top:0;'>
					<?php if ( ! $year_config ) : ?>
						<p class='notice notice-warning inline'><?php echo esc_html__( 'Für dieses Jahr wurden noch keine Jahreseinstellungen hinterlegt.', KGVVM_TEXT_DOMAIN ); ?></p>
					<?php endif; ?>
					<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
						<thead>
							<tr>
								<th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Geleistet (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Pflicht (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Fehlend (Std.)', KGVVM_TEXT_DOMAIN ); ?></th>
								<th><?php echo esc_html__( 'Aufschlag (€)', KGVVM_TEXT_DOMAIN ); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php if ( empty( $summary ) ) : ?>
								<tr><td colspan='5'><?php echo esc_html__( 'Keine Mitglieder gefunden.', KGVVM_TEXT_DOMAIN ); ?></td></tr>
							<?php else : ?>
								<?php foreach ( $summary as $row ) : ?>
								<tr>
									<td><?php echo esc_html( $row->display_name ); ?></td>
									<td><?php echo esc_html( number_format_i18n( $row->completed_hours, 2 ) ); ?></td>
									<td><?php echo esc_html( number_format_i18n( $row->required_hours, 2 ) ); ?></td>
									<td><?php echo $row->missing_hours > 0 ? '<strong style="color:#c00">' . esc_html( number_format_i18n( $row->missing_hours, 2 ) ) . '</strong>' : esc_html( number_format_i18n( 0, 2 ) ); ?></td>
									<td><?php echo $row->surcharge > 0 ? '<strong style="color:#c00">' . esc_html( $this->format_currency( $row->surcharge ) ) . '</strong>' : '—'; ?></td>
								</tr>
								<?php endforeach; ?>
							<?php endif; ?>
						</tbody>
					</table>
				</div>

			<?php else : ?>
				<?php /* ---- GELEISTETE ARBEITEN (logs) ---- */ ?>

				<?php /* New/edit log form */ ?>
				<div class='kgvvm-card' style='margin-top:0;border-top:none;padding-top:20px;'>
					<h2><?php echo esc_html( $edit_log ? __( 'Arbeitseintrag bearbeiten', KGVVM_TEXT_DOMAIN ) : __( 'Neuer Arbeitseintrag', KGVVM_TEXT_DOMAIN ) ); ?></h2>
					<form method='post'>
						<?php wp_nonce_field( 'kgvvm_save_work_log' ); ?>
						<input type='hidden' name='kgvvm_action' value='save_work_log' />
						<input type='hidden' name='id' value='<?php echo esc_attr( $edit_log ? $edit_log->id : 0 ); ?>' />
						<table class='form-table kgvvm-form-table'>
							<tr>
								<th scope='row'><label for='kgvvm-log-date'><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><input type='date' name='work_date' id='kgvvm-log-date' required
									value='<?php echo esc_attr( $edit_log ? esc_attr( $edit_log->work_date ) : current_time( 'Y-m-d' ) ); ?>' /></td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-log-job'><?php echo esc_html__( 'Arbeitsart', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<select name='job_id' id='kgvvm-log-job'>
										<option value='0'><?php echo esc_html__( '— keine Zuordnung —', KGVVM_TEXT_DOMAIN ); ?></option>
										<?php foreach ( $jobs as $job ) : ?>
											<option value='<?php echo esc_attr( $job->id ); ?>' <?php selected( $edit_log ? (int) $edit_log->job_id : 0, (int) $job->id ); ?>><?php echo esc_html( $job->name ); ?></option>
										<?php endforeach; ?>
									</select>
								</td>
							</tr>
							<tr>
								<th scope='row'><label><?php echo esc_html__( 'Mitglieder & Stunden', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td>
									<?php
									$existing_member_hours = array();
									if ( $edit_log && ! empty( $edit_log->members ) ) {
										foreach ( $edit_log->members as $m ) {
											$existing_member_hours[ (int) $m->user_id ] = (float) $m->hours;
										}
									}
									?>
									<table class='kgvvm-subtable'>
										<thead><tr><th><?php echo esc_html__( 'Mitglied', KGVVM_TEXT_DOMAIN ); ?></th><th><?php echo esc_html__( 'Stunden', KGVVM_TEXT_DOMAIN ); ?></th></tr></thead>
										<tbody>
											<?php foreach ( $all_members as $member ) : ?>
											<?php
											$uid       = (int) $member->ID;
											$hours_val = isset( $existing_member_hours[ $uid ] ) ? $existing_member_hours[ $uid ] : '';
											?>
											<tr>
												<td><?php echo esc_html( $member->display_name ); ?></td>
												<td><input type='number' min='0' step='0.25' style='width:80px;'
													name='member_hours[<?php echo esc_attr( $uid ); ?>]'
													value='<?php echo esc_attr( $hours_val ); ?>'
													placeholder='0' /></td>
											</tr>
											<?php endforeach; ?>
										</tbody>
									</table>
									<p class='description'><?php echo esc_html__( 'Nur Mitglieder mit eingetragenen Stunden werden gespeichert.', KGVVM_TEXT_DOMAIN ); ?></p>
								</td>
							</tr>
							<tr>
								<th scope='row'><label for='kgvvm-log-note'><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></label></th>
								<td><textarea name='note' id='kgvvm-log-note' rows='3' class='large-text'><?php echo esc_textarea( $edit_log ? $edit_log->note : '' ); ?></textarea></td>
							</tr>
						</table>
						<?php submit_button( $edit_log ? __( 'Arbeitseintrag aktualisieren', KGVVM_TEXT_DOMAIN ) : __( 'Arbeitseintrag speichern', KGVVM_TEXT_DOMAIN ) ); ?>
						<?php if ( $edit_log ) : ?>
							<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year ) ) ); ?>' class='button'><?php echo esc_html__( 'Abbrechen', KGVVM_TEXT_DOMAIN ); ?></a>
						<?php endif; ?>
					</form>
				</div>

				<?php /* Log list */ ?>
				<?php if ( ! empty( $logs ) ) : ?>
				<table class='wp-list-table widefat fixed striped' style='margin-top:16px;'>
					<thead>
						<tr>
							<th><?php echo esc_html__( 'Datum', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Arbeitsart', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Mitglieder', KGVVM_TEXT_DOMAIN ); ?></th>
							<th><?php echo esc_html__( 'Notiz', KGVVM_TEXT_DOMAIN ); ?></th>
							<th style='width:120px;'><?php echo esc_html__( 'Aktionen', KGVVM_TEXT_DOMAIN ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php foreach ( $logs as $log ) : ?>
						<?php $log_members = $this->work->get_log_members( $log->id ); ?>
						<tr>
							<td><?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $log->work_date ) ) ); ?></td>
							<td><?php echo esc_html( $log->job_name ?: '—' ); ?></td>
							<td>
								<?php foreach ( $log_members as $lm ) :
									$u = get_userdata( $lm->user_id );
									if ( $u ) : ?>
										<?php echo esc_html( $u->display_name ); ?>: <strong><?php echo esc_html( number_format_i18n( (float) $lm->hours, 2 ) ); ?> Std.</strong><br>
									<?php endif; ?>
								<?php endforeach; ?>
							</td>
							<td><?php echo esc_html( $log->note ); ?></td>
							<td>
								<a href='<?php echo esc_url( $this->admin_url( 'kgvvm-arbeit', array( 'year' => $selected_year, 'id' => $log->id ) ) ); ?>'><?php echo esc_html__( 'Bearbeiten', KGVVM_TEXT_DOMAIN ); ?></a>
								&nbsp;|&nbsp;
								<a href='<?php echo esc_url( wp_nonce_url( $this->admin_url( 'kgvvm-arbeit', array( 'kgvvm_action' => 'delete_work_log', 'id' => $log->id, 'year' => $selected_year ) ), 'kgvvm_delete_work_log_' . $log->id ) ); ?>'
									class='kgvvm-delete-link' onclick='return confirm("<?php echo esc_attr__( 'Diesen Arbeitseintrag wirklich löschen?', KGVVM_TEXT_DOMAIN ); ?>")'><?php echo esc_html__( 'Löschen', KGVVM_TEXT_DOMAIN ); ?></a>
							</td>
						</tr>
						<?php endforeach; ?>
					</tbody>
				</table>
				<?php else : ?>
					<p class='kgvvm-help' style='margin-top:12px;'><?php echo esc_html( sprintf( __( 'Keine Arbeitseinträge für %d vorhanden.', KGVVM_TEXT_DOMAIN ), $selected_year ) ); ?></p>
				<?php endif; ?>

			<?php endif; ?>
		</div>
		<?php
	}
}
