<?php
/**
 * ANCP Core - Import maps table data access.
 *
 * Saved column-mapping templates (OPS spec v0.2 Section 3.5): a named,
 * reusable mapping from a spreadsheet's column headings to occurrence fields,
 * plus fixed defaults (e.g. record type). Keyed by NORMALISED HEADING, not
 * position, so a workbook keeps importing when its columns move around.
 *
 * gedcom '' = site-wide template (the normal case); a dataset-scoped template
 * passes its gedcom.
 *
 * @package AncestorPress\Core
 * @since   0.12.0
 */

namespace AncestorPress\Core;

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

/**
 * Import maps table CRUD.
 *
 * @since 0.12.0
 */
class Import_Maps_Table {

	/**
	 * Fully-qualified table name.
	 *
	 * @return string
	 */
	public static function table() {
		return Tables::import_maps();
	}

	/**
	 * All templates visible to a dataset: its own plus site-wide ones.
	 *
	 * @param  string $gedcom Tree identifier ('' for site-wide only).
	 * @return array  name => { mapping: array, defaults: array } (decoded).
	 */
	public static function get_all( $gedcom = '' ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		$rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM %i WHERE gedcom = %s OR gedcom = '' ORDER BY name ASC", self::table(), $gedcom ) );

		$out = array();
		foreach ( $rows as $row ) {
			$out[ $row->name ] = self::decode( $row );
		}
		return $out;
	}

	/**
	 * One template by name (dataset-scoped first, site-wide fallback).
	 *
	 * @param  string $name   Template name.
	 * @param  string $gedcom Tree identifier.
	 * @return array { mapping: array, defaults: array }, empty when absent.
	 */
	public static function get_by_name( $name, $gedcom = '' ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		$row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM %i WHERE name = %s AND ( gedcom = %s OR gedcom = '' ) ORDER BY gedcom DESC LIMIT 1", self::table(), $name, $gedcom ) );
		return $row ? self::decode( $row ) : array();
	}

	/**
	 * Save (insert or replace) a named template.
	 *
	 * @param  string $name     Template name.
	 * @param  array  $mapping  Normalised heading => target field.
	 * @param  array  $defaults Fixed defaults (e.g. { recordtype: census }).
	 * @param  string $gedcom   '' for site-wide (default).
	 * @return bool
	 */
	public static function save( $name, $mapping, $defaults = array(), $gedcom = '' ) {
		global $wpdb;
		$name = trim( (string) $name );
		if ( '' === $name ) {
			return false;
		}

		$data = array(
			'mapping'    => wp_json_encode( (array) $mapping ),
			'defaults'   => wp_json_encode( (array) $defaults ),
			'updated_at' => Tables::now(),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		$existing_id = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT id FROM %i WHERE gedcom = %s AND name = %s LIMIT 1', self::table(), $gedcom, $name ) );
		if ( $existing_id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table.
			return false !== $wpdb->update( self::table(), $data, array( 'id' => $existing_id ) );
		}

		$data['gedcom']             = $gedcom;
		$data['name']               = $name;
		$data['created_by_user_id'] = get_current_user_id();
		$data['created_at']         = $data['updated_at'];
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table.
		return (bool) $wpdb->insert( self::table(), $data );
	}

	/**
	 * Delete a template by name.
	 *
	 * @param  string $name   Template name.
	 * @param  string $gedcom '' for site-wide.
	 * @return int|false
	 */
	public static function delete_by_name( $name, $gedcom = '' ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table.
		return $wpdb->delete(
			self::table(),
			array(
				'gedcom' => $gedcom,
				'name'   => $name,
			)
		);
	}

	/**
	 * Decode a stored row's JSON fields.
	 *
	 * @param  object $row Table row.
	 * @return array { mapping: array, defaults: array }
	 */
	private static function decode( $row ) {
		$mapping  = json_decode( (string) $row->mapping, true );
		$defaults = json_decode( (string) $row->defaults, true );
		return array(
			'mapping'  => is_array( $mapping ) ? $mapping : array(),
			'defaults' => is_array( $defaults ) ? $defaults : array(),
		);
	}
}
