<?php
/**
 * Class Noesia Cs Impact Map Service.
 *
 * @package Noesia_Commerce_Suite
 */

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

/**
 * Class implementation.
 */
class Noesia_CS_Impact_Map_Service {
	const SNAPSHOT_OPTION = 'noesia_cs_impact_map_snapshot';
	/**
	 * Runtime dependency or state value.
	 *
	 * @var mixed
	 */
	private $order_repository;

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $order_repository Order repository.
	 */
	public function __construct( Noesia_CS_Order_Repository $order_repository = null ) {
		$this->order_repository = $order_repository ? $order_repository : new Noesia_CS_Order_Repository();
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $range Range.
	 */
	public function refresh_snapshot( $range = array() ) {
		$group_by     = $this->sanitize_group_by( Noesia_CS_Settings::get( 'impact_map_group_by', 'country' ) );
		$range        = is_array( $range ) ? $range : array();
		$date_after   = isset( $range['date_after'] ) ? sanitize_text_field( $range['date_after'] ) : '';
		$date_before  = isset( $range['date_before'] ) ? sanitize_text_field( $range['date_before'] ) : '';
		$range_mode   = isset( $range['mode'] ) ? sanitize_key( $range['mode'] ) : 'all';
		$date_from    = isset( $range['from'] ) ? sanitize_text_field( $range['from'] ) : '';
		$date_to      = isset( $range['to'] ) ? sanitize_text_field( $range['to'] ) : '';

		$snapshot = array(
			'generated_at'             => time(),
			'threshold'                => max( 1, absint( Noesia_CS_Settings::get( 'impact_map_threshold', 3 ) ) ),
			'group_by'                 => $group_by,
			'range_mode'               => $range_mode,
			'range_from'               => $date_from,
			'range_to'                 => $date_to,
			'total_orders_considered'  => 0,
			'visible_areas'            => 0,
			'hidden_areas_count'       => 0,
			'areas'                    => array(),
		);

		if ( ! $this->order_repository->is_available() ) {
			$snapshot['notice'] = __( 'WooCommerce data is not available.', 'noesia-commerce-suite' );
			update_option( self::SNAPSHOT_OPTION, $snapshot, false );
			return $snapshot;
		}

		$limit  = max( 50, absint( Noesia_CS_Settings::get( 'impact_snapshot_order_limit', 500 ) ) );
		if ( $date_after || $date_before ) {
			$orders = $this->order_repository->get_orders_between( $date_after, $date_before, $limit );
		} else {
			$orders = $this->order_repository->get_orders_for_snapshot( $limit );
		}

		$areas = array();

		foreach ( $orders as $order ) {
			if ( ! is_a( $order, 'WC_Order' ) ) {
				continue;
			}

			$city     = trim( (string) ( $order->get_shipping_city() ?: $order->get_billing_city() ) );
			$region   = trim( (string) ( $order->get_shipping_state() ?: $order->get_billing_state() ) );
			$country  = trim( (string) ( $order->get_shipping_country() ?: $order->get_billing_country() ) );
			$area     = $this->build_area_from_order_location( $city, $region, $country, $group_by );

			if ( empty( $area['key'] ) ) {
				continue;
			}

			$area_key = $area['key'];

			if ( ! isset( $areas[ $area_key ] ) ) {
				$areas[ $area_key ] = array(
					'label'             => $area['label'],
					'city'              => $area['city'],
					'region'            => $area['region'],
					'country'           => $area['country'],
					'country_label'     => $area['country_label'],
					'group_by'          => $group_by,
					'orders'            => 0,
					'products'          => 0,
					'revenue'           => 0,
					'categories'        => array(),
					'products_by_name'  => array(),
				);
			}

			$areas[ $area_key ]['orders']++;
			$areas[ $area_key ]['revenue'] += (float) $order->get_total();
			$snapshot['total_orders_considered']++;

			foreach ( $order->get_items() as $item ) {
				$quantity  = absint( $item->get_quantity() );
				$name      = sanitize_text_field( $item->get_name() );
				$areas[ $area_key ]['products'] += $quantity;

				if ( $name ) {
					if ( ! isset( $areas[ $area_key ]['products_by_name'][ $name ] ) ) {
						$areas[ $area_key ]['products_by_name'][ $name ] = 0;
					}
					$areas[ $area_key ]['products_by_name'][ $name ] += $quantity;
				}

				$product = method_exists( $item, 'get_product' ) ? $item->get_product() : null;
				if ( $product ) {
					$category_ids = $product->get_category_ids();
					foreach ( $category_ids as $category_id ) {
						$term = get_term( $category_id, 'product_cat' );
						if ( $term && ! is_wp_error( $term ) ) {
							$category_name = sanitize_text_field( $term->name );
							if ( ! isset( $areas[ $area_key ]['categories'][ $category_name ] ) ) {
								$areas[ $area_key ]['categories'][ $category_name ] = 0;
							}
							$areas[ $area_key ]['categories'][ $category_name ] += $quantity;
						}
					}
				}
			}
		}

		uasort(
			$areas,
			function ( $a, $b ) {
				return ( $b['orders'] <=> $a['orders'] );
			}
		);

		foreach ( $areas as $area ) {
			if ( $area['orders'] < $snapshot['threshold'] ) {
				$snapshot['hidden_areas_count']++;
				continue;
			}

			arsort( $area['categories'] );
			arsort( $area['products_by_name'] );

			$snapshot['areas'][] = array(
				'label'           => $area['label'],
				'city'            => $area['city'],
				'region'          => $area['region'],
				'country'         => $area['country'],
				'country_label'   => $area['country_label'] ?? $this->get_country_label( $area['country'] ),
				'group_by'        => $area['group_by'],
				'orders'          => absint( $area['orders'] ),
				'products'        => absint( $area['products'] ),
				'revenue'         => round( (float) $area['revenue'], 2 ),
				'top_categories'  => array_slice( array_keys( $area['categories'] ), 0, 3 ),
				'top_products'    => array_slice( array_keys( $area['products_by_name'] ), 0, 3 ),
			);
		}

		$snapshot['visible_areas'] = count( $snapshot['areas'] );
		update_option( self::SNAPSHOT_OPTION, $snapshot, false );
		return $snapshot;
	}

	/**
	 * Handles the related plugin operation.
	 */
	public function get_snapshot() {
		$snapshot = get_option( self::SNAPSHOT_OPTION, array() );
		if ( ! is_array( $snapshot ) ) {
			$snapshot = array();
		}

		return wp_parse_args(
			$snapshot,
			array(
				'generated_at'             => 0,
				'threshold'                => max( 1, absint( Noesia_CS_Settings::get( 'impact_map_threshold', 3 ) ) ),
				'group_by'                 => $this->sanitize_group_by( Noesia_CS_Settings::get( 'impact_map_group_by', 'country' ) ),
				'total_orders_considered'  => 0,
				'visible_areas'            => 0,
				'hidden_areas_count'       => 0,
				'areas'                    => array(),
				'notice'                   => '',
				'range_mode'               => 'all',
				'range_from'               => '',
				'range_to'                 => '',
			)
		);
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $city City.
	 * @param mixed $region Region.
	 * @param mixed $country Country.
	 * @param mixed $group_by Group by.
	 */
	private function build_area_from_order_location( $city, $region, $country, $group_by ) {
		$city           = sanitize_text_field( $city );
		$region         = sanitize_text_field( $region );
		$country        = strtoupper( sanitize_text_field( $country ) );
		$country_label  = $this->get_country_label( $country );

		if ( 'country' === $group_by ) {
			$parts = array_filter( array( $country_label ) );
		} elseif ( 'region' === $group_by ) {
			$parts = array_filter( array( $region, $country_label ) );
		} else {
			$parts = array_filter( array( $city, $region, $country_label ) );
		}

		if ( empty( $parts ) ) {
			return array(
				'key'            => '',
				'label'          => '',
				'city'           => '',
				'region'         => '',
				'country'        => '',
				'country_label'  => '',
			);
		}

		$label = implode( ', ', $parts );
		return array(
			'key'            => strtolower( $group_by . '|' . $label ),
			'label'          => $label,
			'city'           => $city,
			'region'         => $region,
			'country'        => $country,
			'country_label'  => $country_label,
		);
	}

	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $country_code Country code.
	 */
	private function get_country_label( $country_code ) {
		$country_code = strtoupper( sanitize_text_field( $country_code ) );
		if ( ! $country_code ) {
			return '';
		}

		if ( function_exists( 'WC' ) && WC() && isset( WC()->countries ) ) {
			$countries = WC()->countries->get_countries();
			if ( isset( $countries[ $country_code ] ) ) {
				return sanitize_text_field( $countries[ $country_code ] );
			}
		}

		return $country_code;
	}


	/**
	 * Handles the related plugin operation.
	 *
	 * @param mixed $group_by Group by.
	 */
	private function sanitize_group_by( $group_by ) {
		$group_by = sanitize_key( $group_by );
		return in_array( $group_by, array( 'city', 'region', 'country' ), true ) ? $group_by : 'country';
	}
}
