<?php
/**
 * Pushes WooCommerce order line items to the Order Items tab (one row per item).
 *
 * @package StoreSheet
 */

declare( strict_types=1 );

namespace StoreSheet\Sync\WcToSheets;

defined( 'ABSPATH' ) || exit;

use StoreSheet\API\GoogleSheetsClient;
use StoreSheet\Sheets\SpreadsheetManager;
use StoreSheet\Sync\RowMapper;

/**
 * Order Items tab pusher. Opt-in (`sync_order_items`), push-only. Each line item
 * is a normal storesheet_row_map entry keyed by resource='order_item',
 * resource_id=order_item_id. No stale-item cleanup (documented limitation).
 *
 * @since 1.2.0
 */
class OrderItemsPusher {

	private const TAB      = 'Order Items';
	private const RESOURCE = 'order_item';

	private GoogleSheetsClient $client;
	private RowMapper $mapper;

	public function __construct( GoogleSheetsClient $client, RowMapper $mapper ) {
		$this->client = $client;
		$this->mapper = $mapper;
	}

	/**
	 * Push one order's line items. No-op when the feature is off or the order's
	 * status is not synced.
	 *
	 * @param int $order_id
	 * @return true|\WP_Error
	 */
	public function push( int $order_id ) {
		$settings = get_option( 'storesheet_settings', array() );
		if ( empty( $settings['sync_order_items'] ) ) {
			return true;
		}

		$order = wc_get_order( $order_id );
		if ( ! $order instanceof \WC_Order ) {
			return new \WP_Error( 'invalid_order', "Order {$order_id} not found." );
		}

		// Same order-set the base Orders tab syncs (mirrors OrderPusher's gate).
		$per_status = ! empty( $settings['orders_per_status_tabs'] );
		$allowed    = $per_status
			? array_keys( OrderPusher::STATUS_TABS )
			: ( $settings['order_statuses'] ?? OrderPusher::DEFAULT_STATUSES );

		if ( ! in_array( $order->get_status(), $allowed, true ) ) {
			return true;
		}

		$this->ensure_tab_ready();

		$rows      = $this->mapper->order_items_to_rows( $order );
		$error_msg = '';
		foreach ( $rows as $entry ) {
			list( $item_id, $row ) = $entry;
			$result                = $this->write_row( (int) $item_id, $row );
			if ( is_wp_error( $result ) ) {
				$error_msg = $result->get_error_message();
			}
		}

		if ( '' !== $error_msg ) {
			$this->log_sync( $order_id, 'failed', $error_msg );
			return new \WP_Error( 'order_items_push_failed', $error_msg );
		}

		$this->log_sync( $order_id, 'success' );
		return true;
	}

	/**
	 * Append (new) or batch-update (existing) a single line item's row.
	 *
	 * @param int   $item_id
	 * @param array $row
	 * @return array|\WP_Error
	 */
	private function write_row( int $item_id, array $row ) {
		$row_index = $this->get_row_index( $item_id );

		if ( $row_index ) {
			return $this->client->batch_update(
				array(
					array(
						'range'  => self::TAB . '!A' . $row_index . ':N' . $row_index,
						'values' => array( $row ),
					),
				)
			);
		}

		$result = $this->client->append( self::TAB . '!A:N', array( $row ) );
		if ( ! is_wp_error( $result ) ) {
			$new_index = $this->parse_appended_row_index( $result );
			if ( $new_index ) {
				$this->save_row_index( $item_id, $new_index );
			}
		}
		return $result;
	}

	/**
	 * Create the Order Items tab once per spreadsheet (transient-guarded).
	 */
	private function ensure_tab_ready(): void {
		$settings = get_option( 'storesheet_settings', array() );
		$sheet_id = $settings['spreadsheet_id'] ?? '';
		if ( '' === $sheet_id ) {
			return;
		}

		$flag = 'storesheet_order_items_tab_ready_' . md5( $sheet_id );
		if ( get_transient( $flag ) ) {
			return;
		}

		$result = ( new SpreadsheetManager( $this->client ) )->ensure_order_items_tab();
		if ( ! is_wp_error( $result ) ) {
			set_transient( $flag, 1, DAY_IN_SECONDS );
		}
	}

	private function get_row_index( int $item_id ): ?int {
		global $wpdb;
		$settings = get_option( 'storesheet_settings', array() );
		$sheet_id = $settings['spreadsheet_id'] ?? '';

		$row = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->prepare(
				"SELECT row_index FROM {$wpdb->prefix}storesheet_row_map WHERE tab_name = %s AND resource = %s AND resource_id = %d AND sheet_id = %s LIMIT 1",
				self::TAB,
				self::RESOURCE,
				$item_id,
				$sheet_id
			)
		);

		return $row ? (int) $row : null;
	}

	private function save_row_index( int $item_id, int $row_index ): void {
		global $wpdb;
		$settings = get_option( 'storesheet_settings', array() );
		$sheet_id = $settings['spreadsheet_id'] ?? '';

		$wpdb->replace( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->prefix . 'storesheet_row_map',
			array(
				'tab_name'    => self::TAB,
				'resource'    => self::RESOURCE,
				'resource_id' => $item_id,
				'row_index'   => $row_index,
				'sheet_id'    => $sheet_id,
			),
			array( '%s', '%s', '%d', '%d', '%s' )
		);
	}

	private function parse_appended_row_index( array $result ): ?int {
		$range = $result['updates']['updatedRange'] ?? '';
		if ( preg_match( '/(\d+)$/', $range, $m ) ) {
			return (int) $m[1];
		}
		return null;
	}

	/**
	 * Log under resource='order' — the sync_log ENUM has no 'order_item'.
	 *
	 * @param int    $order_id
	 * @param string $status
	 * @param string $message
	 */
	private function log_sync( int $order_id, string $status, string $message = '' ): void {
		global $wpdb;
		$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
			$wpdb->prefix . 'storesheet_sync_log',
			array(
				'direction'   => 'wc_to_sheets',
				'resource'    => 'order',
				'resource_id' => $order_id,
				'tab_name'    => self::TAB,
				'status'      => $status,
				'message'     => $message,
			),
			array( '%s', '%s', '%d', '%s', '%s', '%s' )
		);
	}
}
