<?php
/**
 * AP Family Archive - People management (admin).
 *
 * Standalone-toolset principle (spec-02 Section 7): Archive offers full
 * people management even though AP Family Trees has equivalent screens -
 * users edit where they are. EVERY write goes through the core API
 * (People_Table insert/update/delete_cascade), which owns the names
 * dual-write and referential cascades; this class never writes SQL.
 * As-recorded date strings are stored verbatim; sortable *tr values are
 * derived via \AncestorPress\Core\Tables::normalise_date (spec-00 Section 3).
 *
 * @package    APFAAR_Family_Archive
 * @subpackage APFAAR_Family_Archive/admin
 * @since      0.1.0
 */

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

/**
 * People tab: list, add, edit, delete.
 *
 * @since 0.1.0
 */
class APFAAR_Admin_People {

	/**
	 * Rows per admin list page.
	 */
	const PER_PAGE = 50;

	/**
	 * Person columns editable on the form: field => as-recorded date partner.
	 * (Dates listed here get an automatic *tr translation on save.)
	 *
	 * @var array
	 */
	private static $date_fields = array( 'birthdate', 'baptdate', 'deathdate', 'burialdate' );

	/**
	 * Render the People tab (list or form).
	 *
	 * @param  string $gedcom Working tree.
	 * @return void
	 */
	public static function render( $gedcom ) {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only view routing.
		$action = isset( $_GET['apfaar_action'] ) ? sanitize_key( wp_unslash( $_GET['apfaar_action'] ) ) : '';
		$pid    = isset( $_GET['apfaar_pid'] ) ? sanitize_text_field( wp_unslash( $_GET['apfaar_pid'] ) ) : '';
		// phpcs:enable

		if ( 'add' === $action || ( 'edit' === $action && '' !== $pid ) ) {
			self::render_form( $gedcom, 'edit' === $action ? $pid : '' );
			return;
		}
		self::render_list( $gedcom );
	}

	/**
	 * URL of the People tab with args.
	 *
	 * @param  string $gedcom Working tree.
	 * @param  array  $args   Extra query args.
	 * @return string
	 */
	public static function url( $gedcom, $args = array() ) {
		return add_query_arg(
			array_merge(
				array(
					'page' => 'apfaar',
					'tab'  => 'people',
					'tree' => rawurlencode( $gedcom ),
				),
				$args
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * The paginated, searchable people list.
	 *
	 * @param  string $gedcom Working tree.
	 * @return void
	 */
	private static function render_list( $gedcom ) {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only list filters.
		$search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
		$paged  = isset( $_GET['pg'] ) ? max( 1, absint( $_GET['pg'] ) ) : 1;
		// phpcs:enable

		$args = array(
			'include_living'  => true,
			'include_private' => true,
			'search'          => $search,
			'limit'           => self::PER_PAGE,
			'offset'          => ( $paged - 1 ) * self::PER_PAGE,
		);
		$rows  = \AncestorPress\Core\People_Table::get_by_tree( $gedcom, $args );
		$total = \AncestorPress\Core\People_Table::count_by_tree( $gedcom );

		echo '<div class="apfa-section-card apfa-table-card" style="padding:16px">';
		echo '<form method="get" class="apfa-filter-bar">';
		echo '<input type="hidden" name="page" value="apfaar" /><input type="hidden" name="tab" value="people" />';
		echo '<input type="hidden" name="tree" value="' . esc_attr( $gedcom ) . '" />';
		echo '<input type="search" name="s" value="' . esc_attr( $search ) . '" placeholder="' . esc_attr__( 'Search name...', 'ap-family-archive' ) . '" /> ';
		submit_button( __( 'Search', 'ap-family-archive' ), 'secondary', 'submit', false );
		echo ' <a class="button button-primary" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'add' ) ) ) . '">' . esc_html__( 'Add person', 'ap-family-archive' ) . '</a>';
		/* translators: %s: number of people. */
		echo '<span class="apfa-filter-count">' . esc_html( sprintf( __( '%s people in this tree', 'ap-family-archive' ), number_format_i18n( $total ) ) ) . '</span>';
		echo '</form>';

		if ( empty( $rows ) ) {
			echo '<p>' . esc_html__( 'No people found. Add one, or import a GEDCOM on the Import / Export tab.', 'ap-family-archive' ) . '</p></div>';
			return;
		}

		echo '<table class="widefat striped"><thead><tr>'
			. '<th>' . esc_html__( 'ID', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Name', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Sex', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Born', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Died', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Flags', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Actions', 'ap-family-archive' ) . '</th>'
			. '</tr></thead><tbody>';
		foreach ( $rows as $p ) {
			$flags = array();
			if ( ! empty( $p->living ) ) {
				$flags[] = '<span class="apfa-living-dot" title="' . esc_attr__( 'Living', 'ap-family-archive' ) . '"></span> ' . esc_html__( 'Living', 'ap-family-archive' );
			}
			if ( ! empty( $p->private ) ) {
				$flags[] = esc_html__( 'Private', 'ap-family-archive' );
			}
			$delete_url = wp_nonce_url(
				admin_url( 'admin-post.php?action=apfaar_person_delete&tree=' . rawurlencode( $gedcom ) . '&apfaar_pid=' . rawurlencode( $p->personID ) ),
				'apfaar_person_delete_' . $p->personID
			);
			echo '<tr>'
				. '<td><span class="apfa-gedcom-ref">' . esc_html( $p->personID ) . '</span></td>'
				. '<td><a href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'edit', 'apfaar_pid' => $p->personID ) ) ) . '">'
					. esc_html( APFAAR_People::display_name( $p ) ) . '</a></td>'
				. '<td>' . esc_html( (string) $p->sex ) . '</td>'
				. '<td>' . esc_html( (string) $p->birthdate ) . '</td>'
				. '<td>' . esc_html( (string) $p->deathdate ) . '</td>'
				. '<td>' . wp_kses_post( implode( ' ', $flags ) ) . '</td>'
				. '<td><a class="apfa-action-edit" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'edit', 'apfaar_pid' => $p->personID ) ) ) . '">' . esc_html__( 'Edit', 'ap-family-archive' ) . '</a> | '
					. '<a class="apfa-action-delete" href="' . esc_url( $delete_url ) . '" onclick="return confirm(\'' . esc_js( __( 'Delete this person and all their links (events, family references, names)? This cannot be undone.', 'ap-family-archive' ) ) . '\')">' . esc_html__( 'Delete', 'ap-family-archive' ) . '</a></td>'
				. '</tr>';
		}
		echo '</tbody></table>';

		$pages = (int) ceil( $total / self::PER_PAGE );
		if ( $pages > 1 && '' === $search ) {
			echo '<p>';
			if ( $paged > 1 ) {
				echo '<a href="' . esc_url( self::url( $gedcom, array( 'pg' => $paged - 1 ) ) ) . '">&laquo; ' . esc_html__( 'Previous', 'ap-family-archive' ) . '</a> ';
			}
			/* translators: 1: current page, 2: total pages. */
			echo esc_html( sprintf( __( 'Page %1$d of %2$d', 'ap-family-archive' ), $paged, $pages ) );
			if ( $paged < $pages ) {
				echo ' <a href="' . esc_url( self::url( $gedcom, array( 'pg' => $paged + 1 ) ) ) . '">' . esc_html__( 'Next', 'ap-family-archive' ) . ' &raquo;</a>';
			}
			echo '</p>';
		}
		echo '</div>';
	}

	/**
	 * The add/edit person form.
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $pid    Person to edit ('' = add).
	 * @return void
	 */
	private static function render_form( $gedcom, $pid ) {
		$person = '' !== $pid ? \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $pid ) : null;
		if ( '' !== $pid && ! $person ) {
			echo '<div class="apfa-notice-amber">' . esc_html__( 'Person not found in this tree.', 'ap-family-archive' ) . '</div>';
			return;
		}
		$val = function ( $field ) use ( $person ) {
			return $person ? (string) ( $person->{$field} ?? '' ) : '';
		};

		echo '<div class="apfa-section-card">';
		echo '<h2 class="apfa-options-section-title">'
			. ( $person
				? esc_html__( 'Edit person', 'ap-family-archive' ) . ' <span class="apfa-gedcom-ref">' . esc_html( $pid ) . '</span>'
				: esc_html__( 'Add person', 'ap-family-archive' ) )
			. '</h2>';
		echo '<p class="description">' . esc_html__( 'Dates are stored exactly as you type them (e.g. "12 MAR 1861", "ABT 1790", "Q3 1901") - a sortable value is derived automatically and your original is never rewritten.', 'ap-family-archive' ) . '</p>';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		wp_nonce_field( 'apfaar_person_save' );
		echo '<input type="hidden" name="action" value="apfaar_person_save" />';
		echo '<input type="hidden" name="tree" value="' . esc_attr( $gedcom ) . '" />';
		echo '<input type="hidden" name="apfaar_pid" value="' . esc_attr( $pid ) . '" />';

		echo '<div class="apfa-form-grid-2"><div class="apfa-form-col">';
		self::text_row( __( 'First / given names', 'ap-family-archive' ), 'firstname', $val( 'firstname' ) );
		self::text_row( __( 'Surname prefix', 'ap-family-archive' ), 'lnprefix', $val( 'lnprefix' ), __( 'e.g. van, de, O\'', 'ap-family-archive' ) );
		self::text_row( __( 'Surname', 'ap-family-archive' ), 'lastname', $val( 'lastname' ) );
		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Sex', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control"><select name="sex">';
		foreach ( array(
			''  => __( 'Unknown', 'ap-family-archive' ),
			'M' => __( 'Male', 'ap-family-archive' ),
			'F' => __( 'Female', 'ap-family-archive' ),
		) as $k => $label ) {
			echo '<option value="' . esc_attr( $k ) . '"' . selected( $val( 'sex' ), $k, false ) . '>' . esc_html( $label ) . '</option>';
		}
		echo '</select></span></div>';
		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Privacy', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">';
		echo '<label class="apfa-check-label"><input type="checkbox" name="living" value="1"' . checked( (bool) $val( 'living' ), true, false ) . ' /> ' . esc_html__( 'Living - never shown to visitors', 'ap-family-archive' ) . '</label>';
		echo '<label class="apfa-check-label"><input type="checkbox" name="private" value="1"' . checked( (bool) $val( 'private' ), true, false ) . ' /> ' . esc_html__( 'Private - hidden from non-privileged viewers', 'ap-family-archive' ) . '</label>';
		echo '</span></div>';

		// Main branch (Model C single-select, junction-backed): one dropdown
		// now; the schema is many-to-many so this can widen to multi-select
		// later with no migration.
		if ( class_exists( 'AncestorPress\Core\Branches_Table' ) ) {
			$branches = \AncestorPress\Core\Branches_Table::get_by_tree( $gedcom );
			if ( ! empty( $branches ) ) {
				$current = $person ? \AncestorPress\Core\Branches_Table::primary_branch_for( $gedcom, $pid ) : '';
				echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Main branch', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control"><select name="apfaar_main_branch">';
				echo '<option value="">' . esc_html__( '- none -', 'ap-family-archive' ) . '</option>';
				foreach ( $branches as $b ) {
					echo '<option value="' . esc_attr( $b->branch ) . '"' . selected( $current, $b->branch, false ) . '>' . esc_html( $b->branch ) . '</option>';
				}
				echo '</select><br /><span class="description">'
					. esc_html__( 'The branch this person is shown under by default. Choosing one also makes them a member of it (and that membership survives branch re-applies); memberships in other branches - from branch rules on the Branches tab - are unchanged. "- none -" clears the choice without removing any membership.', 'ap-family-archive' )
					. '</span></span></div>';
			}
		}
		echo '</div><div class="apfa-form-col">';
		self::text_row( __( 'Birth date', 'ap-family-archive' ), 'birthdate', $val( 'birthdate' ) );
		self::text_row( __( 'Birth place', 'ap-family-archive' ), 'birthplace', $val( 'birthplace' ) );
		self::text_row( __( 'Baptism date', 'ap-family-archive' ), 'baptdate', $val( 'baptdate' ) );
		self::text_row( __( 'Baptism place', 'ap-family-archive' ), 'baptplace', $val( 'baptplace' ) );
		self::text_row( __( 'Death date', 'ap-family-archive' ), 'deathdate', $val( 'deathdate' ) );
		self::text_row( __( 'Death place', 'ap-family-archive' ), 'deathplace', $val( 'deathplace' ) );
		self::text_row( __( 'Burial date', 'ap-family-archive' ), 'burialdate', $val( 'burialdate' ) );
		self::text_row( __( 'Burial place', 'ap-family-archive' ), 'burialplace', $val( 'burialplace' ) );
		echo '</div></div>';

		echo '<div class="apfa-save-bar">';
		submit_button( $person ? __( 'Save changes', 'ap-family-archive' ) : __( 'Add person', 'ap-family-archive' ), 'primary', 'submit', false );
		echo ' <a class="button button-secondary" href="' . esc_url( self::url( $gedcom ) ) . '">' . esc_html__( 'Back to list', 'ap-family-archive' ) . '</a>';
		echo '</div></form>';

		// Citations: back this record's facts with sources (edit mode only).
		if ( $person && class_exists( 'APFAAR_Admin_Sources' ) ) {
			APFAAR_Admin_Sources::citations_panel( $gedcom, $pid, 'people' );
		}

		// Audit trail (P7): who created / last changed this record.
		if ( $person ) {
			$bits = array();
			if ( ! empty( $person->created_by_user_id ) ) {
				$creator = get_userdata( (int) $person->created_by_user_id );
				if ( $creator ) {
					/* translators: 1: user name, 2: date. */
					$bits[] = sprintf( __( 'Created by %1$s on %2$s.', 'ap-family-archive' ), $creator->display_name, mysql2date( get_option( 'date_format' ), (string) $person->created_at ) );
				}
			}
			if ( ! empty( $person->changedby ) ) {
				/* translators: 1: user login, 2: date. */
				$bits[] = sprintf( __( 'Last changed by %1$s on %2$s.', 'ap-family-archive' ), $person->changedby, mysql2date( get_option( 'date_format' ), (string) $person->changedate ) );
			}
			if ( $bits ) {
				echo '<p class="description apfa-audit-line">' . esc_html( implode( ' ', $bits ) ) . '</p>';
			}
		}
		echo '</div>';
	}

	/**
	 * One labelled text input row.
	 *
	 * @param  string $label Row label.
	 * @param  string $name  Field name.
	 * @param  string $value Current value.
	 * @param  string $hint  Optional placeholder.
	 * @return void
	 */
	private static function text_row( $label, $name, $value, $hint = '' ) {
		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html( $label ) . '</span>'
			. '<span class="apfa-field-row-control"><input type="text" name="' . esc_attr( $name ) . '" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $hint ) . '" /></span></div>';
	}

	// ── Handlers (called from APFAAR_Admin admin-post endpoints) ────────────────

	/**
	 * Save (add or update) a person. All writes via the core API.
	 *
	 * @param  string $gedcom Working tree.
	 * @return string         The personID saved.
	 */
	public static function save( $gedcom ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- caller (APFAAR_Admin) verified cap + nonce.
		$pid  = isset( $_POST['apfaar_pid'] ) ? sanitize_text_field( wp_unslash( $_POST['apfaar_pid'] ) ) : '';
		$data = array(
			'sex'     => isset( $_POST['sex'] ) && in_array( $_POST['sex'], array( 'M', 'F' ), true ) ? sanitize_text_field( wp_unslash( $_POST['sex'] ) ) : null,
			'living'  => empty( $_POST['living'] ) ? 0 : 1,
			'private' => empty( $_POST['private'] ) ? 0 : 1,
		);
		foreach ( array( 'firstname', 'lnprefix', 'lastname', 'birthplace', 'baptplace', 'deathplace', 'burialplace' ) as $field ) {
			$data[ $field ] = isset( $_POST[ $field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) : '';
		}
		foreach ( self::$date_fields as $field ) {
			$raw                    = isset( $_POST[ $field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) : '';
			$data[ $field ]         = $raw; // As recorded, verbatim.
			$data[ $field . 'tr' ]  = \AncestorPress\Core\Tables::normalise_date( $raw );
		}
		// phpcs:enable
		$data['metaphone'] = '' !== $data['lastname'] ? metaphone( $data['lastname'] ) : '';

		if ( '' !== $pid ) {
			$id = \AncestorPress\Core\People_Table::get_id_by_ref( $gedcom, $pid );
			if ( $id ) {
				// Audit trail (P7): stamp the TNG-shaped change columns via
				// the existing core update API - no core change needed.
				$user               = wp_get_current_user();
				$data['changedby']  = $user && $user->exists() ? $user->user_login : '';
				$data['changedate'] = current_time( 'mysql', true );
				\AncestorPress\Core\People_Table::update( $id, $data );
			}
			self::save_main_branch( $gedcom, $pid );
			return $pid;
		}

		$data['gedcom']   = $gedcom;
		$data['personID'] = \AncestorPress\Core\People_Table::next_manual_person_id( $gedcom );
		\AncestorPress\Core\People_Table::insert( $data );
		self::save_main_branch( $gedcom, $data['personID'] );
		return $data['personID'];
	}

	/**
	 * Persist the Main branch single-select (Model C is_primary) when the
	 * form offered it. Only acts when the field was submitted, so saves from
	 * a branch-less tree change nothing.
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $pid    Person reference.
	 * @return void
	 */
	private static function save_main_branch( $gedcom, $pid ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- caller (APFAAR_Admin) verified cap + nonce.
		if ( ! class_exists( 'AncestorPress\Core\Branches_Table' ) || ! isset( $_POST['apfaar_main_branch'] ) ) {
			return;
		}
		$branch = sanitize_text_field( wp_unslash( $_POST['apfaar_main_branch'] ) );
		// phpcs:enable
		if ( \AncestorPress\Core\Branches_Table::primary_branch_for( $gedcom, $pid ) !== $branch ) {
			\AncestorPress\Core\Branches_Table::set_primary( $gedcom, $pid, $branch );
		}
	}

	/**
	 * Delete a person and every row that references them.
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $pid    Person reference.
	 * @return void
	 */
	public static function delete( $gedcom, $pid ) {
		$id = \AncestorPress\Core\People_Table::get_id_by_ref( $gedcom, $pid );
		if ( $id ) {
			\AncestorPress\Core\People_Table::delete_cascade( $id );
		}
	}
}
