<?php
defined( 'ABSPATH' ) || exit;

/**
 * Data aggregation layer.
 * All WooCommerce queries use wc_get_orders() / wc_get_products() so they are
 * compatible with both legacy post-based storage and HPOS.
 */
class WAD_Data {

	// ─── Date helpers ────────────────────────────────────────────────────────

	public static function get_date_range( string $range ): array {
		$end = current_time( 'Y-m-d 23:59:59' );
		switch ( $range ) {
			case '7d': $start = date( 'Y-m-d 00:00:00', strtotime( '-7 days' ) ); break;
			case '1m': $start = date( 'Y-m-d 00:00:00', strtotime( '-1 month' ) ); break;
			case '3m': $start = date( 'Y-m-d 00:00:00', strtotime( '-3 months' ) ); break;
			case '6m': $start = date( 'Y-m-d 00:00:00', strtotime( '-6 months' ) ); break;
			case '1y': $start = date( 'Y-m-d 00:00:00', strtotime( '-1 year' ) ); break;
			default:   $start = date( 'Y-m-d 00:00:00', strtotime( '-6 months' ) );
		}
		return [ 'start' => $start, 'end' => $end ];
	}

	// ─── Overview KPIs ───────────────────────────────────────────────────────

	public static function get_overview( string $range = '6m' ): array {
		$cache_key = 'wad_overview_' . $range;
		$cached    = get_transient( $cache_key );
		if ( $cached !== false ) {
			return $cached;
		}

		$dates      = self::get_date_range( $range );
		$prev_start = date( 'Y-m-d 00:00:00', strtotime( $dates['start'] . ' -' . self::range_to_interval( $range ) ) );
		$prev_end   = date( 'Y-m-d 23:59:59', strtotime( $dates['start'] . ' -1 second' ) );

		$orders      = self::query_orders( $dates['start'], $dates['end'] );
		$prev_orders = self::query_orders( $prev_start, $prev_end );

		$revenue      = array_sum( array_column( $orders, 'total' ) );
		$prev_revenue = array_sum( array_column( $prev_orders, 'total' ) );

		$customer_ids      = array_unique( array_column( $orders, 'customer_id' ) );
		$prev_customer_ids = array_unique( array_column( $prev_orders, 'customer_id' ) );

		$data = [
			'kpis'           => [
				'revenue'    => [
					'value'  => $revenue,
					'change' => self::pct_change( $prev_revenue, $revenue ),
					'prev'   => $prev_revenue,
				],
				'orders'     => [
					'value'  => count( $orders ),
					'change' => self::pct_change( count( $prev_orders ), count( $orders ) ),
					'avg'    => count( $orders ) ? round( $revenue / count( $orders ), 2 ) : 0,
				],
				'customers'  => [
					'value'  => count( $customer_ids ),
					'change' => self::pct_change( count( $prev_customer_ids ), count( $customer_ids ) ),
				],
			],
			'revenue_chart'  => self::get_revenue_chart( $range ),
			'category_chart' => self::get_category_breakdown( $dates['start'], $dates['end'] ),
			'daily_revenue'  => self::get_daily_revenue(),
			'customer_growth'=> self::get_customer_growth(),
			'recent_orders'  => self::get_recent_orders( 6 ),
			'top_products'   => self::get_top_products( 5, $dates['start'], $dates['end'] ),
		];

		set_transient( $cache_key, $data, 15 * MINUTE_IN_SECONDS );
		return $data;
	}

	// ─── Revenue chart ───────────────────────────────────────────────────────

	public static function get_revenue_chart( string $range = '6m' ): array {
		$cache_key = 'wad_revenue_chart_' . $range;
		$cached    = get_transient( $cache_key );
		if ( $cached !== false ) {
			return $cached;
		}

		$dates  = self::get_date_range( $range );
		$months = self::months_between( $dates['start'], $dates['end'] );
		$data   = [];

		foreach ( $months as $month ) {
			$start    = $month . '-01 00:00:00';
			$end      = date( 'Y-m-t 23:59:59', strtotime( $month . '-01' ) );
			$ly_start = date( 'Y-m-01 00:00:00', strtotime( $start . ' -1 year' ) );
			$ly_end   = date( 'Y-m-t 23:59:59', strtotime( $end . ' -1 year' ) );

			$orders    = self::query_orders( $start, $end );
			$ly_orders = self::query_orders( $ly_start, $ly_end );

			$data[] = [
				'month'    => date( 'M', strtotime( $month . '-01' ) ),
				'revenue'  => array_sum( array_column( $orders, 'total' ) ),
				'orders'   => count( $orders ),
				'lastYear' => array_sum( array_column( $ly_orders, 'total' ) ),
			];
		}

		set_transient( $cache_key, $data, HOUR_IN_SECONDS );
		return $data;
	}

	// ─── Daily revenue (current week) ────────────────────────────────────────

	public static function get_daily_revenue(): array {
		$days   = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ];
		$monday = date( 'Y-m-d', strtotime( 'monday this week' ) );
		$data   = [];

		foreach ( $days as $i => $label ) {
			$day    = date( 'Y-m-d', strtotime( $monday . ' +' . $i . ' days' ) );
			$orders = self::query_orders( $day . ' 00:00:00', $day . ' 23:59:59' );
			$data[] = [ 'day' => $label, 'amount' => array_sum( array_column( $orders, 'total' ) ) ];
		}

		return $data;
	}

	// ─── Customer growth (last 6 months) ─────────────────────────────────────

	public static function get_customer_growth(): array {
		$months = self::months_between(
			date( 'Y-m-d', strtotime( '-5 months' ) ),
			current_time( 'Y-m-d' )
		);
		$data   = [];

		foreach ( $months as $month ) {
			$start    = $month . '-01 00:00:00';
			$end      = date( 'Y-m-t 23:59:59', strtotime( $month . '-01' ) );
			$orders   = self::query_orders( $start, $end );
			$new_ids  = [];
			$ret_ids  = [];

			foreach ( $orders as $order ) {
				$cid   = $order['customer_id'];
				$count = self::customer_order_count_before( $cid, $start );
				if ( $count === 0 ) {
					$new_ids[] = $cid;
				} else {
					$ret_ids[] = $cid;
				}
			}

			$data[] = [
				'month'     => date( 'M', strtotime( $month . '-01' ) ),
				'new'       => count( array_unique( $new_ids ) ),
				'returning' => count( array_unique( $ret_ids ) ),
			];
		}

		return $data;
	}

	// ─── Category breakdown ──────────────────────────────────────────────────

	public static function get_category_breakdown( string $start, string $end ): array {
		$orders = self::query_orders( $start, $end );
		$totals = [];

		foreach ( $orders as $order_data ) {
			$order = wc_get_order( $order_data['id'] );
			if ( ! $order ) {
				continue;
			}
			foreach ( $order->get_items() as $item ) {
				$product = $item->get_product();
				if ( ! $product ) {
					continue;
				}
				$cats = get_the_terms( $product->get_id(), 'product_cat' );
				$cat  = ( $cats && ! is_wp_error( $cats ) ) ? $cats[0]->name : 'Uncategorised';
				$totals[ $cat ] = ( $totals[ $cat ] ?? 0 ) + $item->get_total();
			}
		}

		$grand = array_sum( $totals );
		if ( ! $grand ) {
			return [];
		}

		arsort( $totals );
		$colors  = [ '#E8C872', '#72B4E8', '#E87272', '#72E8A8', '#B472E8', '#E8A072' ];
		$result  = [];
		$i       = 0;

		foreach ( $totals as $name => $value ) {
			$result[] = [
				'name'  => $name,
				'value' => round( ( $value / $grand ) * 100, 1 ),
				'color' => $colors[ $i % count( $colors ) ],
			];
			$i++;
		}

		return $result;
	}

	// ─── Orders ──────────────────────────────────────────────────────────────

	public static function get_orders_list( array $args = [] ): array {
		$page       = max( 1, (int) ( $args['page'] ?? 1 ) );
		$per_page   = min( 100, max( 1, (int) ( $args['per_page'] ?? 20 ) ) );
		$status     = $args['status'] ?? 'any';
		$statuses   = $status === 'any'
			? [ 'wc-completed', 'wc-processing', 'wc-on-hold', 'wc-refunded', 'wc-pending', 'wc-cancelled' ]
			: [ 'wc-' . $status ];

		$wc_orders = wc_get_orders( [
			'status'  => $statuses,
			'limit'   => $per_page,
			'paged'   => $page,
			'orderby' => 'date',
			'order'   => 'DESC',
		] );

		$total = wc_get_orders( [
			'status'  => $statuses,
			'limit'   => -1,
			'return'  => 'ids',
		] );

		$total_count = count( $total );

		$status_counts = [];
		foreach ( [ 'completed', 'processing', 'on-hold', 'refunded', 'pending', 'cancelled' ] as $s ) {
			$status_counts[ $s ] = wc_orders_count( 'wc-' . $s );
		}

		return [
			'orders'        => array_map( [ __CLASS__, 'format_order' ], $wc_orders ),
			'total'         => $total_count,
			'page'          => $page,
			'per_page'      => $per_page,
			'total_pages'   => (int) ceil( $total_count / $per_page ),
			'status_counts' => $status_counts,
		];
	}

	public static function get_single_order( int $id ): ?array {
		$order = wc_get_order( $id );
		if ( ! $order ) {
			return null;
		}
		return self::format_order( $order, true );
	}

	public static function get_recent_orders( int $limit = 6 ): array {
		$cached = get_transient( 'wad_recent_orders_' . $limit );
		if ( $cached !== false ) {
			return $cached;
		}

		$orders = wc_get_orders( [
			'status'  => [ 'wc-completed', 'wc-processing', 'wc-on-hold', 'wc-refunded' ],
			'limit'   => $limit,
			'orderby' => 'date',
			'order'   => 'DESC',
		] );

		$data = array_map( [ __CLASS__, 'format_order' ], $orders );
		set_transient( 'wad_recent_orders_' . $limit, $data, 5 * MINUTE_IN_SECONDS );
		return $data;
	}

	// ─── Products ─────────────────────────────────────────────────────────────

	public static function get_products_list( array $args = [] ): array {
		$page     = max( 1, (int) ( $args['page'] ?? 1 ) );
		$per_page = min( 100, max( 1, (int) ( $args['per_page'] ?? 20 ) ) );

		$products = wc_get_products( [
			'status'   => 'publish',
			'limit'    => $per_page,
			'page'     => $page,
			'orderby'  => 'date',
			'order'    => 'DESC',
		] );

		$total = (int) wp_count_posts( 'product' )->publish;

		return [
			'products'    => array_map( [ __CLASS__, 'format_product_summary' ], $products ),
			'total'       => $total,
			'page'        => $page,
			'per_page'    => $per_page,
			'total_pages' => (int) ceil( $total / $per_page ),
		];
	}

	public static function get_single_product( int $id ): ?array {
		$product = wc_get_product( $id );
		if ( ! $product ) {
			return null;
		}
		return self::format_product_detail( $product );
	}

	public static function get_top_products( int $limit = 5, string $start = '', string $end = '' ): array {
		if ( ! $start ) {
			$dates = self::get_date_range( '6m' );
			$start = $dates['start'];
			$end   = $dates['end'];
		}

		$orders = self::query_orders( $start, $end );
		$sales  = [];

		foreach ( $orders as $order_data ) {
			$order = wc_get_order( $order_data['id'] );
			if ( ! $order ) {
				continue;
			}
			foreach ( $order->get_items() as $item ) {
				$pid           = $item->get_product_id();
				$sales[ $pid ] = ( $sales[ $pid ] ?? 0 ) + $item->get_total();
			}
		}

		arsort( $sales );
		$top    = array_slice( $sales, 0, $limit, true );
		$result = [];

		foreach ( $top as $pid => $revenue ) {
			$product = wc_get_product( $pid );
			if ( ! $product ) {
				continue;
			}
			$cats = get_the_terms( $pid, 'product_cat' );
			$cat  = ( $cats && ! is_wp_error( $cats ) ) ? $cats[0]->name : '';
			$result[] = [
				'id'      => (string) $pid,
				'name'    => $product->get_name(),
				'revenue' => round( $revenue, 2 ),
				'sold'    => (int) $product->get_total_sales(),
				'price'   => (float) $product->get_price(),
				'stock'   => $product->get_stock_quantity(),
				'sku'     => $product->get_sku(),
				'cat'     => $cat,
				'rating'  => round( (float) $product->get_average_rating(), 1 ),
				'reviews' => (int) $product->get_review_count(),
				'img'     => (string) ( wp_get_attachment_url( $product->get_image_id() ) ?: '' ),
				'trend'   => 0,
			];
		}

		return $result;
	}

	// ─── Customers ────────────────────────────────────────────────────────────

	public static function get_customers_list( array $args = [] ): array {
		$sort     = $args['sort'] ?? 'spent';
		$page     = max( 1, (int) ( $args['page'] ?? 1 ) );
		$per_page = min( 100, max( 1, (int) ( $args['per_page'] ?? 20 ) ) );

		$users = get_users( [
			'role__in'   => [ 'customer', 'subscriber', 'shop_manager', 'administrator' ],
			'number'     => $per_page,
			'paged'      => $page,
			'orderby'    => 'registered',
			'order'      => 'DESC',
			'fields'     => 'all',
		] );

		$customers = [];
		foreach ( $users as $user ) {
			$customer = new WC_Customer( $user->ID );
			$customers[] = self::format_customer( $customer );
		}

		if ( $sort === 'spent' ) {
			usort( $customers, fn( $a, $b ) => $b['spent'] <=> $a['spent'] );
		} elseif ( $sort === 'orders' ) {
			usort( $customers, fn( $a, $b ) => $b['orders'] <=> $a['orders'] );
		} else {
			usort( $customers, fn( $a, $b ) => strcmp( $a['name'], $b['name'] ) );
		}

		$role_counts = count_users()['avail_roles'];
		$total_count = 0;
		foreach ( [ 'customer', 'subscriber', 'shop_manager', 'administrator' ] as $role ) {
			$total_count += $role_counts[ $role ] ?? 0;
		}

		return [
			'customers'   => $customers,
			'total'       => $total_count,
			'page'        => $page,
			'per_page'    => $per_page,
			'total_pages' => (int) ceil( $total_count / $per_page ),
		];
	}

	public static function get_single_customer( int $id ): ?array {
		$customer = new WC_Customer( $id );
		if ( ! $customer->get_id() ) {
			return null;
		}
		return self::format_customer( $customer, true );
	}

	// ─── Formatters ──────────────────────────────────────────────────────────

	private static function format_order( WC_Order $order, bool $detailed = false ): array {
		$customer_id   = $order->get_customer_id();
		$customer_name = trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() );

		$base = [
			'id'         => $order->get_id(),
			'number'     => '#WC-' . $order->get_order_number(),
			'customer'   => $customer_name ?: __( 'Guest', 'woo-analytics-dashboard' ),
			'custId'     => $customer_id,
			'email'      => $order->get_billing_email(),
			'items'      => $order->get_item_count(),
			'total'      => (float) $order->get_total(),
			'status'     => str_replace( 'wc-', '', $order->get_status() ),
			'date'       => $order->get_date_created()?->date( 'M j, Y' ),
			'time'       => human_time_diff( $order->get_date_created()?->getTimestamp(), time() ) . ' ago',
			'method'     => $order->get_payment_method_title(),
			'shipping'   => $order->get_shipping_method(),
		];

		if ( $detailed ) {
			$products = [];
			foreach ( $order->get_items() as $item ) {
				$product    = $item->get_product();
				$products[] = [
					'name'  => $item->get_name(),
					'price' => $product ? (float) $product->get_price() : 0,
					'img'   => $product ? wp_get_attachment_url( $product->get_image_id() ) : '',
					'id'    => $item->get_product_id(),
				];
			}
			$base['products'] = $products;
		} else {
			$base['products'] = array_map( fn( $i ) => $i->get_name(), $order->get_items() );
		}

		return $base;
	}

	private static function format_product_summary( WC_Product $product ): array {
		$cats = get_the_terms( $product->get_id(), 'product_cat' );
		$cat  = ( $cats && ! is_wp_error( $cats ) ) ? $cats[0]->name : '';

		return [
			'id'     => (string) $product->get_id(),
			'name'   => $product->get_name(),
			'sku'    => $product->get_sku(),
			'price'  => (float) $product->get_price(),
			'stock'  => $product->get_stock_quantity(),
			'sold'   => (int) $product->get_total_sales(),
			'cat'    => $cat,
			'rating' => round( (float) $product->get_average_rating(), 1 ),
			'reviews'=> (int) $product->get_review_count(),
			'img'    => wp_get_attachment_url( $product->get_image_id() ),
			'trend'  => 0,
			'revenue'=> 0,
		];
	}

	private static function format_product_detail( WC_Product $product ): array {
		$summary                   = self::format_product_summary( $product );
		$summary['history']        = self::get_product_weekly_sales( $product->get_id() );
		$summary['related_orders'] = self::get_product_related_orders( $product->get_id() );
		return $summary;
	}

	private static function get_product_related_orders( int $product_id ): array {
		$recent = wc_get_orders( [
			'limit'   => 100,
			'orderby' => 'date',
			'order'   => 'DESC',
			'status'  => [ 'wc-completed', 'wc-processing', 'wc-on-hold' ],
		] );

		$related = [];
		foreach ( $recent as $order ) {
			foreach ( $order->get_items() as $item ) {
				if ( (int) $item->get_product_id() === $product_id ) {
					$related[] = self::format_order( $order );
					break;
				}
			}
			if ( count( $related ) >= 20 ) {
				break;
			}
		}
		return $related;
	}

	private static function format_customer( WC_Customer $customer, bool $detailed = false ): array {
		$name   = trim( $customer->get_first_name() . ' ' . $customer->get_last_name() );
		$name   = $name ?: $customer->get_display_name();
		$initials = implode( '', array_map( fn( $w ) => strtoupper( $w[0] ), array_filter( explode( ' ', $name ) ) ) );
		$spent  = (float) $customer->get_total_spent();
		$tier   = self::get_customer_tier( $spent );

		$orders = wc_get_orders( [
			'customer_id' => $customer->get_id(),
			'limit'       => 1,
			'orderby'     => 'date',
			'order'       => 'DESC',
		] );

		$base = [
			'id'        => $customer->get_id(),
			'name'      => $name,
			'email'     => $customer->get_email(),
			'city'      => $customer->get_billing_city() ?: $customer->get_shipping_city(),
			'avatar'    => substr( $initials, 0, 2 ),
			'orders'    => $customer->get_order_count(),
			'spent'     => round( $spent, 2 ),
			'tier'      => $tier,
			'joined'    => $customer->get_date_created()?->date( 'M Y' ),
			'lastOrder' => $orders ? ( human_time_diff( $orders[0]->get_date_created()?->getTimestamp(), time() ) . ' ago' ) : 'Never',
		];

		if ( $detailed ) {
			$base['spendHistory'] = self::get_customer_spend_history( $customer->get_id() );
			$base['orders_list']  = array_map(
				[ __CLASS__, 'format_order' ],
				wc_get_orders( [ 'customer_id' => $customer->get_id(), 'limit' => 20, 'orderby' => 'date', 'order' => 'DESC' ] )
			);
		}

		return $base;
	}

	// ─── Helpers ─────────────────────────────────────────────────────────────

	private static function query_orders( string $start, string $end ): array {
		$orders = wc_get_orders( [
			'status'       => [ 'wc-completed', 'wc-processing' ],
			'date_created' => strtotime( $start ) . '...' . strtotime( $end ),
			'limit'        => -1,
			'return'       => 'objects',
		] );

		return array_map( fn( $o ) => [
			'id'          => $o->get_id(),
			'total'       => (float) $o->get_total(),
			'customer_id' => $o->get_customer_id(),
		], $orders );
	}

	private static function pct_change( float $from, float $to ): float {
		if ( $from == 0 ) {
			return $to > 0 ? 100 : 0;
		}
		return round( ( ( $to - $from ) / $from ) * 100, 1 );
	}

	private static function range_to_interval( string $range ): string {
		return match ( $range ) {
			'7d'    => '7 days',
			'1m'    => '1 month',
			'3m'    => '3 months',
			'1y'    => '1 year',
			default => '6 months',
		};
	}

	private static function months_between( string $start, string $end ): array {
		$months  = [];
		$current = date( 'Y-m', strtotime( $start ) );
		$last    = date( 'Y-m', strtotime( $end ) );
		while ( $current <= $last ) {
			$months[]  = $current;
			$current = date( 'Y-m', strtotime( $current . '-01 +1 month' ) );
		}
		return $months;
	}

	private static function customer_order_count_before( int $customer_id, string $before ): int {
		if ( ! $customer_id ) {
			return 0;
		}
		$orders = wc_get_orders( [
			'customer_id'  => $customer_id,
			'date_created' => '<' . strtotime( $before ),
			'limit'        => -1,
			'return'       => 'ids',
		] );
		return count( $orders );
	}

	private static function get_product_weekly_sales( int $product_id ): array {
		$data = [];
		for ( $w = 7; $w >= 0; $w-- ) {
			$start   = date( 'Y-m-d 00:00:00', strtotime( "-{$w} weeks monday" ) );
			$end     = date( 'Y-m-d 23:59:59', strtotime( "-{$w} weeks sunday" ) );
			$orders  = self::query_orders( $start, $end );
			$units   = 0;
			foreach ( $orders as $od ) {
				$order = wc_get_order( $od['id'] );
				if ( $order ) {
					foreach ( $order->get_items() as $item ) {
						if ( $item->get_product_id() == $product_id ) {
							$units += $item->get_quantity();
						}
					}
				}
			}
			$data[] = [ 'week' => 'W' . ( 8 - $w ), 'units' => $units ];
		}
		return $data;
	}

	private static function get_customer_spend_history( int $customer_id ): array {
		$data = [];
		for ( $m = 5; $m >= 0; $m-- ) {
			$start   = date( 'Y-m-01 00:00:00', strtotime( "-{$m} months" ) );
			$end     = date( 'Y-m-t 23:59:59', strtotime( "-{$m} months" ) );
			$orders  = wc_get_orders( [
				'customer_id'  => $customer_id,
				'date_created' => strtotime( $start ) . '...' . strtotime( $end ),
				'status'       => [ 'wc-completed', 'wc-processing' ],
				'limit'        => -1,
			] );
			$amount  = array_sum( array_map( fn( $o ) => (float) $o->get_total(), $orders ) );
			$data[]  = [ 'month' => date( 'M', strtotime( $start ) ), 'amount' => round( $amount, 2 ) ];
		}
		return $data;
	}

	public static function get_customer_tier( float $spent ): string {
		$platinum = (float) get_option( 'wad_tier_platinum', 3000 );
		$gold     = (float) get_option( 'wad_tier_gold', 1500 );
		$silver   = (float) get_option( 'wad_tier_silver', 500 );

		if ( $spent >= $platinum ) return 'Platinum';
		if ( $spent >= $gold )     return 'Gold';
		if ( $spent >= $silver )   return 'Silver';
		return 'Bronze';
	}
}
