<?php
namespace R2_Offload\Storage;

defined( 'ABSPATH' ) || exit;

/**
 * Estimates monthly + one-time cost of offloading a given library to a provider,
 * using the public list-price tables in {@see Providers} and live byte/object
 * counts from the offload queue. Estimate only — not a billing quote.
 */
final class Cost_Estimator {

	private const GIB = 1073741824; // 1024^3

	/**
	 * @param string     $provider           Provider id.
	 * @param int        $bytes              Total stored bytes.
	 * @param int        $objects            Total object (file) count.
	 * @param float|null $monthly_egress_gb  Assumed monthly egress; null/0 = stored size served once.
	 * @return array Breakdown with monthly_total, onetime_total and line items.
	 */
	public static function estimate( string $provider, int $bytes, int $objects, ?float $monthly_egress_gb = null ): array {
		$p       = Providers::get( $provider );
		$price   = $p['pricing'] ?? [];
		$stored  = $bytes / self::GIB;
		$objects = max( 0, $objects );

		$storage_rate = (float) ( $price['storage_gb_month'] ?? 0 );
		$egress_rate  = (float) ( $price['egress_gb'] ?? 0 );
		$included_gb  = (float) ( $price['included_gb'] ?? 0 );
		$min_gb       = (float) ( $price['min_storage_gb'] ?? 0 );
		$base_monthly = (float) ( $price['base_monthly'] ?? 0 );
		$incl_egress  = (float) ( $price['included_egress_gb'] ?? 0 );
		$free_egress  = (float) ( $price['free_egress_gb'] ?? 0 );
		$egress_free  = ! empty( $price['egress_free'] );
		$egress_mult  = (float) ( $price['egress_free_multiple'] ?? 0 );
		$class_a      = (float) ( $price['class_a_per_10k'] ?? 0 );
		$class_b      = (float) ( $price['class_b_per_10k'] ?? 0 );

		// --- Storage ---
		$effective_storage = max( $stored, $min_gb );
		$billable_storage  = max( $effective_storage - $included_gb, 0 );
		$storage_cost      = $billable_storage * $storage_rate;

		// --- Egress (assume each stored GB served once/month if not specified) ---
		$egress_gb = $monthly_egress_gb !== null && $monthly_egress_gb > 0 ? $monthly_egress_gb : $stored;
		if ( $egress_free ) {
			$billable_egress = 0.0;
			$egress_cost     = 0.0;
		} elseif ( $egress_mult > 0 ) {
			$free            = $stored * $egress_mult;
			$billable_egress = max( $egress_gb - $free, 0 );
			$egress_cost     = $billable_egress * $egress_rate;
		} else {
			$free            = $free_egress + $incl_egress;
			$billable_egress = max( $egress_gb - $free, 0 );
			$egress_cost     = $billable_egress * $egress_rate;
		}

		// --- Operations ---
		$write_cost      = ( $objects / 10000 ) * $class_a; // one-time, full upload
		$monthly_reads   = $objects;                        // assume each object served ~once/mo
		$read_cost       = ( $monthly_reads / 10000 ) * $class_b;

		$monthly_total = $storage_cost + $egress_cost + $read_cost + $base_monthly;
		$onetime_total = $write_cost;

		$min_account = (float) ( $price['min_account_monthly'] ?? 0 );
		if ( $min_account > 0 ) {
			$monthly_total = max( $monthly_total, $min_account );
		}

		return [
			'provider'      => $provider,
			'label'         => Providers::label( $provider ),
			'stored_gb'     => round( $stored, 2 ),
			'objects'       => $objects,
			'assumed_egress_gb' => round( $egress_gb, 2 ),
			'monthly'       => [
				'storage' => round( $storage_cost, 2 ),
				'egress'  => round( $egress_cost, 2 ),
				'reads'   => round( $read_cost, 2 ),
				'base'    => round( $base_monthly, 2 ),
				'total'   => round( $monthly_total, 2 ),
			],
			'onetime'       => [
				'writes' => round( $write_cost, 2 ),
				'total'  => round( $onetime_total, 2 ),
			],
			'hidden_costs'  => self::hidden_costs( $provider, $price ),
		];
	}

	/** Estimate every provider at once for side-by-side comparison. */
	public static function estimate_all( int $bytes, int $objects, ?float $monthly_egress_gb = null ): array {
		$out = [];
		foreach ( array_keys( Providers::all() ) as $id ) {
			$out[ $id ] = self::estimate( $id, $bytes, $objects, $monthly_egress_gb );
		}
		uasort( $out, static fn( $a, $b ) => $a['monthly']['total'] <=> $b['monthly']['total'] );
		return $out;
	}

	/** @return string[] Human notes about non-obvious charges. */
	private static function hidden_costs( string $provider, array $price ): array {
		$notes = [];
		if ( ! empty( $price['min_storage_gb'] ) ) {
			$notes[] = sprintf( 'Minimum billable storage: %s GB regardless of actual usage.', number_format_i18n( (float) $price['min_storage_gb'] ) );
		}
		if ( ! empty( $price['min_storage_days'] ) ) {
			$notes[] = sprintf( 'Minimum storage duration: %d days — deleting early still bills the full period.', (int) $price['min_storage_days'] );
		}
		if ( ! empty( $price['min_account_monthly'] ) ) {
			$notes[] = sprintf( 'Account minimum spend: $%.2f/mo.', (float) $price['min_account_monthly'] );
		}
		if ( ! empty( $price['base_monthly'] ) ) {
			$notes[] = sprintf( 'Flat base fee: $%.2f/mo (includes %s GB storage / %s GB egress).', (float) $price['base_monthly'], number_format_i18n( (float) ( $price['included_gb'] ?? 0 ) ), number_format_i18n( (float) ( $price['included_egress_gb'] ?? 0 ) ) );
		}
		if ( empty( $price['egress_free'] ) && (float) ( $price['egress_gb'] ?? 0 ) > 0 ) {
			$notes[] = sprintf( 'Egress billed at $%.3f/GB beyond any free tier — high-traffic media can dominate the bill.', (float) $price['egress_gb'] );
		}
		if ( ! empty( $price['egress_free'] ) ) {
			$notes[] = 'No egress fees — bandwidth is free.';
		}
		if ( ( (float) ( $price['class_a_per_10k'] ?? 0 ) ) > 0 || ( (float) ( $price['class_b_per_10k'] ?? 0 ) ) > 0 ) {
			$notes[] = 'Per-request charges apply (uploads/reads); large libraries add request costs.';
		}
		$notes[] = 'Estimate uses public list prices; taxes, premium regions and CDN fees are not included.';
		return $notes;
	}
}
