<?php
/**
 * AP Family Archive - Branches management (admin).
 *
 * Branch curation over the core branches API (Branches_Table, core
 * 0.11.0): a branch is a named rule - start person, generations of ancestors
 * and descendants, cousin depth, spouses - and "applying" it labels everyone
 * it reaches. The tab is free (data management is free everywhere in the
 * suite); the public branch-scoped display ships in the extension package.
 *
 * @package    APFAAR_Family_Archive
 * @subpackage APFAAR_Family_Archive/admin
 * @since      0.1.0
 */

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

/**
 * Branches tab: list/add/edit/apply/delete.
 *
 * @since 0.1.0
 */
class APFAAR_Admin_Branches {

	/**
	 * Whether the booted core carries the branches API (core 0.11.0).
	 *
	 * @return bool
	 */
	public static function available() {
		return class_exists( 'AncestorPress\Core\Branches_Table' );
	}

	/**
	 * Render the Branches tab.
	 *
	 * @param  string $gedcom Working tree.
	 * @return void
	 */
	public static function render( $gedcom ) {
		if ( ! self::available() ) {
			echo '<div class="apfa-notice-amber">' . esc_html__( 'The data core on this site predates branches (core 0.11.0). Update the suite plugins and reload.', 'ap-family-archive' ) . '</div>';
			return;
		}

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only view routing.
		$action = isset( $_GET['apfaar_action'] ) ? sanitize_key( wp_unslash( $_GET['apfaar_action'] ) ) : '';
		$name   = isset( $_GET['apfaar_branch'] ) ? sanitize_text_field( wp_unslash( $_GET['apfaar_branch'] ) ) : '';
		// phpcs:enable

		if ( 'branch_add' === $action || ( 'branch_edit' === $action && '' !== $name ) ) {
			self::render_form( $gedcom, 'branch_edit' === $action ? $name : '' );
			return;
		}
		self::render_list( $gedcom );
	}

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

	/**
	 * Human summary of a branch rule.
	 *
	 * @param  object $branch Branch row.
	 * @return string
	 */
	private static function rule_summary( $branch ) {
		$bits   = array();
		/* translators: %s: person record id. */
		$bits[] = sprintf( __( 'from %s', 'ap-family-archive' ), $branch->personID );
		if ( (int) $branch->agens > 0 ) {
			/* translators: %d: generations. */
			$bits[] = sprintf( __( '%d up', 'ap-family-archive' ), (int) $branch->agens );
		}
		if ( (int) $branch->dgens > 0 ) {
			/* translators: %d: generations. */
			$bits[] = sprintf( __( '%d down', 'ap-family-archive' ), (int) $branch->dgens );
		}
		if ( (int) $branch->dagens > 0 ) {
			/* translators: %d: generations. */
			$bits[] = sprintf( __( 'cousins %d', 'ap-family-archive' ), (int) $branch->dagens );
		}
		if ( ! empty( $branch->inclspouses ) ) {
			$bits[] = __( 'spouses', 'ap-family-archive' );
		}
		return implode( ' · ', $bits );
	}

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

		echo '<div class="apfa-section-card apfa-table-card" style="padding:16px">';
		echo '<p class="description">' . esc_html__( 'A branch is a named part of the tree - "the Smith line", "Grandma\'s side" - defined by a rule: start from one person and take so many generations of ancestors, descendants, cousins, and optionally spouses. Applying the rule labels everyone it reaches; a person can be in several branches. Branches power the branch filter on the public site (Pro) and, later, per-branch contributor rights. After adding or editing people, re-apply a branch so its labels stay current.', 'ap-family-archive' ) . '</p>';
		echo '<p><a class="button button-primary" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'branch_add' ) ) ) . '">' . esc_html__( 'Add branch', 'ap-family-archive' ) . '</a> ';
		/* translators: %s: number of branches. */
		echo '<span class="apfa-filter-count">' . esc_html( sprintf( __( '%s branches in this tree', 'ap-family-archive' ), number_format_i18n( is_array( $branches ) ? count( $branches ) : 0 ) ) ) . '</span></p>';

		if ( ! empty( $branches ) ) {
			echo '<table class="widefat striped"><thead><tr>'
				. '<th>' . esc_html__( 'Branch', 'ap-family-archive' ) . '</th>'
				. '<th>' . esc_html__( 'Rule', 'ap-family-archive' ) . '</th>'
				. '<th>' . esc_html__( 'People', 'ap-family-archive' ) . '</th>'
				. '<th>' . esc_html__( 'Actions', 'ap-family-archive' ) . '</th>'
				. '</tr></thead><tbody>';
			foreach ( $branches as $b ) {
				$apply_url  = wp_nonce_url(
					admin_url( 'admin-post.php?action=apfaar_branch_apply&tree=' . rawurlencode( $gedcom ) . '&apfaar_branch=' . rawurlencode( $b->branch ) ),
					'apfaar_branch_apply_' . $b->branch
				);
				$delete_url = wp_nonce_url(
					admin_url( 'admin-post.php?action=apfaar_branch_delete&tree=' . rawurlencode( $gedcom ) . '&apfaar_branch=' . rawurlencode( $b->branch ) ),
					'apfaar_branch_delete_' . $b->branch
				);
				echo '<tr>'
					. '<td><strong>' . esc_html( $b->branch ) . '</strong>'
						. ( '' !== (string) $b->description ? '<br /><span class="description">' . esc_html( $b->description ) . '</span>' : '' ) . '</td>'
					. '<td>' . esc_html( self::rule_summary( $b ) ) . '</td>'
					. '<td>' . esc_html( number_format_i18n( \AncestorPress\Core\Branches_Table::count_members( $gedcom, $b->branch ) ) ) . '</td>'
					. '<td><a class="apfa-action-edit" href="' . esc_url( self::url( $gedcom, array( 'apfaar_action' => 'branch_edit', 'apfaar_branch' => $b->branch ) ) ) . '">' . esc_html__( 'Edit', 'ap-family-archive' ) . '</a> | '
						. '<a href="' . esc_url( $apply_url ) . '">' . esc_html__( 'Re-apply', 'ap-family-archive' ) . '</a> | '
						. '<a class="apfa-action-delete" href="' . esc_url( $delete_url ) . '" onclick="return confirm(\'' . esc_js( __( 'Delete this branch and remove its labels from everyone? People and families themselves are untouched. This cannot be undone.', 'ap-family-archive' ) ) . '\')">' . esc_html__( 'Delete', 'ap-family-archive' ) . '</a></td>'
					. '</tr>';
			}
			echo '</tbody></table>';
		} else {
			echo '<p>' . esc_html__( 'No branches yet. Add one - you need at least one person in the tree first (the rule starts from a person).', 'ap-family-archive' ) . '</p>';
		}
		echo '</div>';
	}

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

		echo '<div class="apfa-section-card">';
		echo '<h2 class="apfa-options-section-title">' . ( $branch ? esc_html__( 'Edit branch', 'ap-family-archive' ) : esc_html__( 'Add branch', 'ap-family-archive' ) ) . '</h2>';
		echo '<p class="description">' . esc_html__( 'Saving applies the rule immediately: everyone it reaches is labeled with this branch, and previous labels for this branch are replaced.', 'ap-family-archive' ) . '</p>';

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

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Name', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">';
		if ( $branch ) {
			echo '<strong>' . esc_html( $name ) . '</strong><br /><span class="description">' . esc_html__( 'The name is fixed once created (it is the label carried by every member). To rename, delete this branch and create a new one.', 'ap-family-archive' ) . '</span>';
		} else {
			echo '<input type="text" name="branch_name" required maxlength="100" placeholder="' . esc_attr__( 'e.g. Smith line', 'ap-family-archive' ) . '" />'
				. '<br /><span class="description">' . esc_html__( 'Shown to visitors in the branch filter (Pro). Commas are not allowed.', 'ap-family-archive' ) . '</span>';
		}
		echo '</span></div>';

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Description', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">'
			. '<input type="text" name="description" value="' . esc_attr( $val( 'description' ) ) . '" style="width:100%;max-width:420px" />'
			. '<br /><span class="description">' . esc_html__( 'For your own notes - not shown publicly.', 'ap-family-archive' ) . '</span></span></div>';

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Colour', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">'
			. '<input type="text" name="colour" value="' . esc_attr( $val( 'colour' ) ) . '" placeholder="#7A8B5C" size="10" maxlength="20" />'
			. '<br /><span class="description">' . esc_html__( 'Optional accent shown as a small dot beside the branch name on public person pages (Pro). Any CSS colour, e.g. #7A8B5C. Leave empty for none.', 'ap-family-archive' ) . '</span></span></div>';

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Start from person', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">'
			. '<input type="text" name="personID" value="' . esc_attr( $val( 'personID' ) ) . '" required placeholder="I123" size="8" />'
			. '<br /><span class="description">' . esc_html__( 'The I-number shown on the person\'s edit screen and public page; must exist in this tree. The rule walks outward from this person.', 'ap-family-archive' ) . '</span></span></div>';

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Generations', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">'
			. '<label>' . esc_html__( 'ancestors (up)', 'ap-family-archive' ) . ' <input type="number" name="agens" min="0" max="20" value="' . esc_attr( $val( 'agens', '0' ) ) . '" /></label> '
			. '<label>' . esc_html__( 'descendants (down)', 'ap-family-archive' ) . ' <input type="number" name="dgens" min="0" max="20" value="' . esc_attr( $val( 'dgens', '0' ) ) . '" /></label> '
			. '<label>' . esc_html__( 'cousins (down from each ancestor)', 'ap-family-archive' ) . ' <input type="number" name="dagens" min="0" max="20" value="' . esc_attr( $val( 'dagens', '0' ) ) . '" /></label>'
			. '<br /><span class="description">' . esc_html__( '"Up" collects parents, grandparents and so on; "down" collects children, grandchildren...; "cousins" additionally walks down from each collected ancestor, which is how aunts, uncles and cousins join the branch. 0 means none.', 'ap-family-archive' ) . '</span></span></div>';

		echo '<div class="apfa-field-row"><span class="apfa-field-row-label">' . esc_html__( 'Spouses', 'ap-family-archive' ) . '</span><span class="apfa-field-row-control">'
			. '<label><input type="checkbox" name="inclspouses" value="1"' . checked( '1', $val( 'inclspouses', '0' ), false ) . ' /> '
			. esc_html__( 'Include the husbands/wives/partners of everyone the rule collects.', 'ap-family-archive' ) . '</label></span></div>';

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

	// ── Handler bodies (called from APFAAR_Admin admin-post endpoints) ──────────

	/**
	 * Save (add or update) a branch rule, then apply it.
	 *
	 * @param  string $gedcom Working tree.
	 * @return array          { branch: string, error: string|null, count: int }
	 */
	public static function save( $gedcom ) {
		// phpcs:disable WordPress.Security.NonceVerification.Missing -- caller (APFAAR_Admin) verified cap + nonce.
		$name = isset( $_POST['apfaar_branch'] ) ? sanitize_text_field( wp_unslash( $_POST['apfaar_branch'] ) ) : '';
		$data = array(
			'description' => isset( $_POST['description'] ) ? sanitize_text_field( wp_unslash( $_POST['description'] ) ) : '',
			'colour'      => isset( $_POST['colour'] ) ? sanitize_text_field( wp_unslash( $_POST['colour'] ) ) : '',
			'personID'    => isset( $_POST['personID'] ) ? sanitize_text_field( wp_unslash( $_POST['personID'] ) ) : '',
			'agens'       => isset( $_POST['agens'] ) ? absint( $_POST['agens'] ) : 0,
			'dgens'       => isset( $_POST['dgens'] ) ? absint( $_POST['dgens'] ) : 0,
			'dagens'      => isset( $_POST['dagens'] ) ? absint( $_POST['dagens'] ) : 0,
			'inclspouses' => empty( $_POST['inclspouses'] ) ? 0 : 1,
		);
		$new_name = isset( $_POST['branch_name'] ) ? \AncestorPress\Core\Branches_Table::sanitise_name( sanitize_text_field( wp_unslash( $_POST['branch_name'] ) ) ) : '';
		// phpcs:enable

		if ( '' === $data['personID'] || ! \AncestorPress\Core\People_Table::get_by_ref( $gedcom, $data['personID'] ) ) {
			return array( 'branch' => $name, 'error' => __( 'No person with that ID exists in this tree - check the I-number on the People tab.', 'ap-family-archive' ), 'count' => 0 );
		}
		if ( 0 === $data['agens'] && 0 === $data['dgens'] && 0 === $data['dagens'] && empty( $data['inclspouses'] ) ) {
			return array( 'branch' => $name, 'error' => __( 'The rule collects nobody beyond the start person - give it at least one generation up or down.', 'ap-family-archive' ), 'count' => 0 );
		}

		if ( '' !== $name ) {
			$existing = \AncestorPress\Core\Branches_Table::get_by_ref( $gedcom, $name );
			if ( ! $existing ) {
				return array( 'branch' => $name, 'error' => __( 'Branch not found in this tree.', 'ap-family-archive' ), 'count' => 0 );
			}
			\AncestorPress\Core\Branches_Table::update( (int) $existing->id, $data );
		} else {
			if ( '' === $new_name ) {
				return array( 'branch' => '', 'error' => __( 'Give the branch a name.', 'ap-family-archive' ), 'count' => 0 );
			}
			$data['gedcom'] = $gedcom;
			$data['branch'] = $new_name;
			if ( ! \AncestorPress\Core\Branches_Table::insert( $data ) ) {
				return array( 'branch' => '', 'error' => __( 'A branch with that name already exists in this tree.', 'ap-family-archive' ), 'count' => 0 );
			}
			$name = $new_name;
		}

		$count = \AncestorPress\Core\Branches_Table::apply( $gedcom, $name );
		return array( 'branch' => $name, 'error' => null, 'count' => (int) $count );
	}

	/**
	 * Re-apply a branch rule (handler body).
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $name   Branch name.
	 * @return int|false      People labeled, or false.
	 */
	public static function apply( $gedcom, $name ) {
		return \AncestorPress\Core\Branches_Table::apply( $gedcom, $name );
	}

	/**
	 * Delete a branch (labels removed, people untouched).
	 *
	 * @param  string $gedcom Working tree.
	 * @param  string $name   Branch name.
	 * @return void
	 */
	public static function delete( $gedcom, $name ) {
		$existing = \AncestorPress\Core\Branches_Table::get_by_ref( $gedcom, $name );
		if ( $existing ) {
			\AncestorPress\Core\Branches_Table::delete_cascade( (int) $existing->id );
		}
	}
}
