<?php
/**
 * AP Family Archive - Families management (admin).
 *
 * Same standalone-toolset discipline as the People tab: every write goes
 * through the core API (Families_Table / Children_Table); dates
 * are stored as recorded with derived *tr values.
 *
 * @package    APFAAR_Family_Archive
 * @subpackage APFAAR_Family_Archive/admin
 * @since      0.1.0
 */

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

/**
 * Families tab: list, add, edit (incl. children links), delete.
 *
 * @since 0.1.0
 */
class APFAAR_Admin_Families {

	/**
	 * Render the Families 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'] ) ) : '';
		$fid    = isset( $_GET['apfaar_fid'] ) ? sanitize_text_field( wp_unslash( $_GET['apfaar_fid'] ) ) : '';
		// phpcs:enable

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

	/**
	 * URL of the Families 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'  => 'families',
					'tree' => rawurlencode( $gedcom ),
				),
				$args
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Partner display: name + ref, or a dash.
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $ref    Person reference or ''.
	 * @return string
	 */
	private static function partner_label( $gedcom, $ref ) {
		if ( '' === (string) $ref ) {
			return '&mdash;';
		}
		$person = \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $ref );
		$name   = $person ? APFAAR_People::display_name( $person ) : __( '(missing)', 'ap-family-archive' );
		return esc_html( $name ) . ' <span class="apfa-gedcom-ref">' . esc_html( $ref ) . '</span>';
	}

	/**
	 * The families list.
	 *
	 * @param  string $gedcom Working tree.
	 * @return void
	 */
	private static function render_list( $gedcom ) {
		$rows = \AncestorPress\Core\Families_Table::get_by_tree( $gedcom );

		echo '<div class="apfa-section-card apfa-table-card" style="padding:16px">';
		echo '<p><a class="button button-primary" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'add' ) ) ) . '">' . esc_html__( 'Add family', 'ap-family-archive' ) . '</a> ';
		/* translators: %s: number of families. */
		echo '<span class="apfa-filter-count">' . esc_html( sprintf( __( '%s families in this tree', 'ap-family-archive' ), number_format_i18n( is_array( $rows ) ? count( $rows ) : 0 ) ) ) . '</span></p>';

		if ( empty( $rows ) ) {
			echo '<p>' . esc_html__( 'No families yet. 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__( 'Partner 1', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Partner 2', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Married', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Children', 'ap-family-archive' ) . '</th>'
			. '<th>' . esc_html__( 'Actions', 'ap-family-archive' ) . '</th>'
			. '</tr></thead><tbody>';
		foreach ( $rows as $f ) {
			$children   = \AncestorPress\Core\Children_Table::get_by_family( $gedcom, $f->familyID );
			$delete_url = wp_nonce_url(
				admin_url( 'admin-post.php?action=apfaar_family_delete&tree=' . rawurlencode( $gedcom ) . '&apfaar_fid=' . rawurlencode( $f->familyID ) ),
				'apfaar_family_delete_' . $f->familyID
			);
			echo '<tr>'
				. '<td><span class="apfa-gedcom-ref">' . esc_html( $f->familyID ) . '</span></td>'
				. '<td>' . wp_kses_post( self::partner_label( $gedcom, (string) $f->husband ) ) . '</td>'
				. '<td>' . wp_kses_post( self::partner_label( $gedcom, (string) $f->wife ) ) . '</td>'
				. '<td>' . esc_html( (string) $f->marrdate ) . '</td>'
				. '<td>' . esc_html( number_format_i18n( is_array( $children ) ? count( $children ) : 0 ) ) . '</td>'
				. '<td><a class="apfa-action-edit" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'edit', 'apfaar_fid' => $f->familyID ) ) ) . '">' . esc_html__( 'Edit', 'ap-family-archive' ) . '</a> | '
					. '<a class="apfa-action-delete" href="' . esc_url( $delete_url ) . '" onclick="return confirm(\'' . esc_js( __( 'Delete this family record and its child links? The people themselves are kept.', 'ap-family-archive' ) ) . '\')">' . esc_html__( 'Delete', 'ap-family-archive' ) . '</a></td>'
				. '</tr>';
		}
		echo '</tbody></table></div>';
	}

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

		echo '<div class="apfa-section-card">';
		echo '<h2 class="apfa-options-section-title">'
			. ( $family
				? esc_html__( 'Edit family', 'ap-family-archive' ) . ' <span class="apfa-gedcom-ref">' . esc_html( $fid ) . '</span>'
				: esc_html__( 'Add family', 'ap-family-archive' ) )
			. '</h2>';
		echo '<p class="description">' . esc_html__( 'Partners and children are linked by their person ID (the I- or P-number on the People tab). Create the people first, then link them here.', 'ap-family-archive' ) . '</p>';

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

		echo '<div class="apfa-form-grid-2"><div class="apfa-form-col">';
		self::text_row( __( 'Partner 1 (husband) ID', 'ap-family-archive' ), 'husband', $val( 'husband' ), 'I123' );
		self::text_row( __( 'Partner 2 (wife) ID', 'ap-family-archive' ), 'wife', $val( 'wife' ), 'I124' );
		echo '</div><div class="apfa-form-col">';
		self::text_row( __( 'Marriage date', 'ap-family-archive' ), 'marrdate', $val( 'marrdate' ) );
		self::text_row( __( 'Marriage place', 'ap-family-archive' ), 'marrplace', $val( 'marrplace' ) );
		self::text_row( __( 'Divorce date', 'ap-family-archive' ), 'divdate', $val( 'divdate' ) );
		self::text_row( __( 'Divorce place', 'ap-family-archive' ), 'divplace', $val( 'divplace' ) );
		echo '</div></div>';

		echo '<div class="apfa-save-bar">';
		submit_button( $family ? __( 'Save changes', 'ap-family-archive' ) : __( 'Add family', '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>';

		// Children management (edit mode only - the family must exist first).
		if ( $family ) {
			echo '<h3 class="apfa-section-heading">' . esc_html__( 'Children', 'ap-family-archive' ) . '</h3>';
			$children = \AncestorPress\Core\Children_Table::get_by_family( $gedcom, $fid );
			if ( ! empty( $children ) ) {
				echo '<table class="widefat striped"><tbody>';
				foreach ( $children as $link ) {
					$remove_url = wp_nonce_url(
						admin_url( 'admin-post.php?action=apfaar_child_remove&tree=' . rawurlencode( $gedcom ) . '&apfaar_fid=' . rawurlencode( $fid ) . '&apfaar_pid=' . rawurlencode( $link->personID ) ),
						'apfaar_child_remove_' . $fid . '_' . $link->personID
					);
					echo '<tr><td>' . wp_kses_post( self::partner_label( $gedcom, $link->personID ) ) . '</td>'
						. '<td style="width:80px"><a class="apfa-action-delete" href="' . esc_url( $remove_url ) . '">' . esc_html__( 'Unlink', 'ap-family-archive' ) . '</a></td></tr>';
				}
				echo '</tbody></table>';
			} else {
				echo '<p class="description">' . esc_html__( 'No children linked yet.', 'ap-family-archive' ) . '</p>';
			}

			echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="margin-top:10px">';
			wp_nonce_field( 'apfaar_child_add' );
			echo '<input type="hidden" name="action" value="apfaar_child_add" />';
			echo '<input type="hidden" name="tree" value="' . esc_attr( $gedcom ) . '" />';
			echo '<input type="hidden" name="apfaar_fid" value="' . esc_attr( $fid ) . '" />';
			echo '<label>' . esc_html__( 'Child person ID', 'ap-family-archive' ) . ' <input type="text" name="apfaar_pid" placeholder="I125" required /></label> ';
			submit_button( __( 'Link child', 'ap-family-archive' ), 'secondary', 'submit', false );
			echo '</form>';

			// Citations: back this family's facts with sources.
			if ( class_exists( 'APFAAR_Admin_Sources' ) ) {
				APFAAR_Admin_Sources::citations_panel( $gedcom, $fid, 'families' );
			}
		}
		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 family. Partner refs are validated against the
	 * tree so a typo cannot create a dangling reference.
	 *
	 * @param  string $gedcom Working tree.
	 * @return array          { fid: string, errors: string[] }
	 */
	public static function save( $gedcom ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- caller (APFAAR_Admin) verified cap + nonce.
		$fid    = isset( $_POST['apfaar_fid'] ) ? sanitize_text_field( wp_unslash( $_POST['apfaar_fid'] ) ) : '';
		$errors = array();
		$data   = array();

		foreach ( array( 'husband', 'wife' ) as $role ) {
			$ref = isset( $_POST[ $role ] ) ? sanitize_text_field( wp_unslash( $_POST[ $role ] ) ) : '';
			if ( '' !== $ref && ! \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $ref ) ) {
				/* translators: %s: person reference. */
				$errors[] = sprintf( __( 'No person "%s" in this tree - partner not linked.', 'ap-family-archive' ), $ref );
				$ref      = '';
			}
			$data[ $role ] = $ref;
		}
		foreach ( array( 'marrplace', 'divplace' ) as $field ) {
			$data[ $field ] = isset( $_POST[ $field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) : '';
		}
		foreach ( array( 'marrdate', 'divdate' ) 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

		if ( '' !== $fid ) {
			$id = \AncestorPress\Core\Families_Table::get_id_by_ref( $gedcom, $fid );
			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\Families_Table::update( $id, $data );
			}
			return array(
				'fid'    => $fid,
				'errors' => $errors,
			);
		}

		$data['gedcom']   = $gedcom;
		$data['familyID'] = \AncestorPress\Core\Families_Table::next_manual_id( $gedcom );
		\AncestorPress\Core\Families_Table::insert( $data );
		return array(
			'fid'    => $data['familyID'],
			'errors' => $errors,
		);
	}

	/**
	 * Delete a family and its child links (people are kept).
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $fid    Family reference.
	 * @return void
	 */
	public static function delete( $gedcom, $fid ) {
		$id = \AncestorPress\Core\Families_Table::get_id_by_ref( $gedcom, $fid );
		if ( $id ) {
			\AncestorPress\Core\Families_Table::delete_cascade( $id );
		}
	}

	/**
	 * Link a child to a family (and stamp the child's famc pointer).
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $fid    Family reference.
	 * @param  string $pid    Child person reference.
	 * @return string|null    Error message or null on success.
	 */
	public static function add_child( $gedcom, $fid, $pid ) {
		// The family must exist in THIS tree before any link is written - a
		// manipulated request must not create an orphan child link or stamp
		// an invalid famc pointer.
		if ( ! \AncestorPress\Core\Families_Table::get_by_ref( $gedcom, $fid ) ) {
			/* translators: %s: family reference. */
			return sprintf( __( 'No family "%s" in this tree.', 'ap-family-archive' ), $fid );
		}
		$person = \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $pid );
		if ( ! $person ) {
			/* translators: %s: person reference. */
			return sprintf( __( 'No person "%s" in this tree.', 'ap-family-archive' ), $pid );
		}
		$existing = \AncestorPress\Core\Children_Table::get_by_family( $gedcom, $fid );
		foreach ( (array) $existing as $link ) {
			if ( $link->personID === $pid ) {
				return __( 'That person is already a child of this family.', 'ap-family-archive' );
			}
		}
		\AncestorPress\Core\Children_Table::insert(
			array(
				'gedcom'   => $gedcom,
				'familyID' => $fid,
				'personID' => $pid,
				'ordernum' => count( (array) $existing ) + 1,
			)
		);
		// Denormalised parents pointer (display cache convention).
		\AncestorPress\Core\People_Table::update( (int) $person->id, array( 'famc' => $fid ) );
		return null;
	}

	/**
	 * Unlink a child from a family (clears famc when it pointed here).
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $fid    Family reference.
	 * @param  string $pid    Child person reference.
	 * @return void
	 */
	public static function remove_child( $gedcom, $fid, $pid ) {
		\AncestorPress\Core\Children_Table::delete_child_link( $gedcom, $fid, $pid );
		$person = \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $pid );
		if ( $person && $fid === (string) $person->famc ) {
			\AncestorPress\Core\People_Table::update( (int) $person->id, array( 'famc' => '' ) );
		}
	}
}
