<?php

class OrdersProcessor {
  private PDO $pdo;
  private Orders $orders;
  private Cart $cart;
  private int $fallbackCountry;
  
  private $productDetailsFactory;
  private $discountsFactory;

  public function __construct(PDO $pdo, Orders $orders, Cart $cart, int $fallback_country, callable $productDetailsFactory, callable $discountsFactory) {
    $this->pdo = $pdo;
    $this->orders = $orders;
    $this->cart = $cart;
    $this->fallbackCountry = $fallback_country;
    $this->productDetailsFactory = $productDetailsFactory;
    $this->discountsFactory = $discountsFactory;
  }

  public function buildCartView(int $fallback_country): ?array {
    $details = ($this->productDetailsFactory)();
    $details->setPricingFallbackCountry($fallback_country);
    $details->setAttributeVisibility('cart');
    $details->onlyCheckedAttributes(true, ['properties']);
    return $this->cart->getFullSummary($details, $fallback_country);
  }

  public function prepareCheckout(array $post, string $sid, ?int $id_customer, array $opts = []): array {
    $note = null;
    $errors = [];
    $stash = $this->orders->loadCheckoutStash();
    $shipping_mode = (int)($post['r_shipping_address'] ?? ($stash['shipping_mode'] ?? Orders::MODE_SAME));

    if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
      $post = $this->prefillCheckoutPost($post, $stash, $id_customer);
    }

    $selected_country = $this->resolveSelectedCountry($post, $stash, $shipping_mode);

    if ($selected_country <= 0) { 
    	$selected_country = $this->fallbackCountry;
    }

    $shipping_methods = $this->orders->getAvailableShippingMethodsByCountry($selected_country) ?: [];
    $current_method_key = $this->resolveCurrentMethodKey($post, $stash, $shipping_methods, 'standard');
    
    $this->orders->stashCheckoutData(['r_shipping_method' => $current_method_key], $sid);

    $shipping_preview = $this->orders->getShippingPreviewForCart($current_method_key, $selected_country);

    $discounts = ($this->discountsFactory)($this->cart->get()['id_customer'] ?? null);
    $snapshot = $this->orders->buildDiscountSnapshot($shipping_preview ?? null);

    if (isset($post['remove_code_id']) && ctype_digit((string)$post['remove_code_id'])) {
      $discounts->removeDiscountLinkById((int)$this->cart->get()['id'], (int)$post['remove_code_id']);
    } elseif (!empty($post['remove_code'])) {
      $discounts->removeCodeFromCart((int)$this->cart->get()['id'], trim((string)$post['remove_code']));
    }

    if (!empty($post['apply_code']) && isset($post['code']) && trim((string)$post['code']) !== '') {
      try {
        $discounts->applyCodeToCart((int)$this->cart->get()['id'], trim((string)$post['code']), $snapshot);
      } catch (\RuntimeException $e) {
        $note = 'LABEL_'.strtoupper($e->getMessage());
      }
    } else {
      $discounts->refreshAutoDiscounts((int)$this->cart->get()['id'], $snapshot);
    }
    
    $applied_discounts = $discounts->validateAndNormalizeCartDiscounts((int)$this->cart->get()['id'], $snapshot);
    $discount_totals  = $discounts->recalculateTotals($snapshot, $applied_discounts);

    $math = $this->computeCartMath($shipping_preview, $discount_totals);

    if (($post['checkout'] ?? null) === 'checkout') {
      if (isset($post['r_shipping_method']) && strtolower((string)$post['r_shipping_method']) === 'pickup') {
        $post['r_shipping_address'] = (string)Orders::MODE_SAME;
      }
      $resp = $this->orders->stashCheckoutData($post, $sid);
      if ($resp['ok']) {
        return ['redirect' => $this->buildUrl($opts, 'overview')];
      }
      $errors = $resp['errors'] ?? ['unknown'];
      $note = $this->errorsToNote($errors);
    }
    
   	$all_payment_providers = $this->orders->getActivePaymentProviders();
		$payment_providers = array_values(array_filter(
	    $all_payment_providers,
	    fn($payment_provider) => ($payment_provider['create_point'] ?? '') !== 'cart'
		));
		
	  $customer_addresses = [];
	  $cid = $this->cart->get()['id_customer'] ?? null;
	  if ($cid) {
	    $customer_addresses = $this->orders->loadCustomerAddresses($cid) ?? [];
	  }

    return [
      'note'             				=> $note,
      'errors'           				=> $errors,
      'shipping_mode'    				=> $shipping_mode,
      'selected_country' 				=> $selected_country,
      'shipping_methods' 				=> $shipping_methods,
      'current_method_key'  		=> $current_method_key,
      'shipping_preview'     		=> $shipping_preview,
      'applied_discounts'				=> $applied_discounts,
      'discount_totals'  				=> $discount_totals,
      'totals' => [
	      'items_gross_before'      => $math['items_gross_before'],
				'shipping_gross'          => $math['shipping_gross'],
				'subtotal_gross'          => $math['subtotal_gross'],
				'avg_rate_items'          => $math['avg_rate_items'],
				'shipping_rate'           => $math['shipping_rate'],
				'discount_items_gross'    => $math['discount_items_gross'],
				'discount_shipping_gross' => $math['discount_shipping_gross'],
				'total_discounts_gross'   => $math['total_discounts_gross'],
				'final_total_gross'       => $math['final_total_gross'],
			],
      'cart_data'        	 			=> $this->buildCartView($this->fallbackCountry),
	    'payment_providers'  			=> $payment_providers,
	    'customer_addresses' 			=> $customer_addresses,
      'post'             				=> $post,
    ];
  }
  
  public function prepareOverview(array $post, array $opts = []): array {
    $note = null;
    $errors = [];
    $stash = $this->orders->loadCheckoutStash();
    $shipping_mode = (int)($post['r_shipping_address'] ?? ($stash['shipping_mode'] ?? Orders::MODE_SAME));

    $details = $this->buildCartView($this->fallbackCountry);
    if (!$stash || !$details || empty($details['items'])) {
      return ['redirect' => $this->buildUrl($opts, 'cart')];
    }

    if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
      $post = $this->prefillCheckoutPost($post, $stash, $this->cart->get()['id_customer'] ?? null, true);
    }

    $selected_country = $this->resolveSelectedCountry($post, $stash, $shipping_mode);
    if ($selected_country <= 0) { 
    	$selected_country = $this->fallbackCountry;
    }
    $shipping_methods = $this->orders->getAvailableShippingMethodsByCountry($selected_country) ?: [];
    $current_method_key = $this->resolveCurrentMethodKey($post, $stash, $shipping_methods, $shipping_methods[0]['key'] ?? 'pickup');
    $shipping_preview = $this->orders->getShippingPreviewForCart($current_method_key, $selected_country) ?: null;

    $discounts = ($this->discountsFactory)($this->cart->get()['id_customer'] ?? null);
    $snapshot = $this->orders->buildDiscountSnapshot($shipping_preview);
    if (!empty($post['apply_code']) && isset($post['code']) && trim((string)$post['code']) !== '') {
      try { 
      	$discounts->applyCodeToCart((int)$this->cart->get()['id'], trim((string)$post['code']), $snapshot);
      } catch (\RuntimeException $e) { 
      	$note = 'LABEL_'.strtoupper($e->getMessage());
      }
    }
    
    $discounts->refreshAutoDiscounts((int)$this->cart->get()['id'], $snapshot);
    $applied_discounts = $discounts->validateAndNormalizeCartDiscounts((int)$this->cart->get()['id'], $snapshot);
    $discount_totals = $discounts->recalculateTotals($snapshot, $applied_discounts);
    
		$math = $this->computeCartMath($shipping_preview, $discount_totals);
		

    if (($post['order'] ?? null) === 'order') {
      if (!empty($post['website'])) {
        $errors = ['HONEYPOT'];
      } else {
        $resp = $this->orders->createOrderFromCart($this->cart->get()['id_customer'] ?? null);
        if (!$resp['ok']) {
          $errors = $resp['errors'] ?? ['CREATE_FAILED'];
        } else {
          $id_order = (int)$resp['id_order'];
          $stash = $this->orders->loadCheckoutStash();
          $payment_method = $stash['payment']['key'] ?? 'prepayment';

          if ($payment_method === 'prepayment') {
            $this->orders->finalizeOrder($id_order, $this->cart->get()['id_customer'] ?? null, 'pending', 'system');
            return ['redirect' => $this->buildUrl($opts, 'home', ['id_order' => $id_order])];
          }
          
          $pay = FrontendPaymentBridge::startCheckout($this->pdo, $this->orders, $payment_method, $id_order, $this->cart->get()['session_id'] ?? '');
          if (($pay['type'] ?? '') === 'redirect' && !empty($pay['url'])) {
            header('Location: '.$pay['url']);
            exit;
          } elseif (($pay['type'] ?? '') === 'render' && !empty($pay['html'])) {
            print $pay['html'];
            exit;
          } else {
            $errors = ['PAYMENT_ERROR'];
            return ['redirect' => $this->buildPaymentErrorRedirect($opts, $payment_method, $errors)];
          }
        }
      }
      if (!$errors) {
        return ['redirect' => $this->buildUrl($opts, 'home', ['id_order' => $id_order ?? 0])];
      }
      $note = $this->errorsToNote($errors);
    }
    
    $all_payment_providers = $this->orders->getActivePaymentProviders();
		$payment_providers = array_values(array_filter(
	    $all_payment_providers,
	    fn($payment_provider) => ($payment_provider['create_point'] ?? '') !== 'cart'
		));
	  $customer_addresses = [];
	  $cid = $this->cart->get()['id_customer'] ?? null;
	  if ($cid) {
	    $customer_addresses = $this->orders->loadCustomerAddresses($cid) ?? [];
	  }
	  

    return [
      'note'             				=> $note,
      'errors'           				=> $errors,
      'shipping_mode'    				=> $shipping_mode,
      'selected_country' 				=> $selected_country,
      'shipping_methods' 				=> $shipping_methods,
      'current_method_key'  		=> $current_method_key,
      'shipping_preview'     		=> $shipping_preview,
      'applied_discounts'				=> $applied_discounts,
      'discount_totals'  				=> $discount_totals,
      'totals' => [
	      'items_gross_before'      => $math['items_gross_before'],
				'shipping_gross'          => $math['shipping_gross'],
				'subtotal_gross'          => $math['subtotal_gross'],
				'avg_rate_items'          => $math['avg_rate_items'],
				'shipping_rate'           => $math['shipping_rate'],
				'discount_items_gross'    => $math['discount_items_gross'],
				'discount_shipping_gross' => $math['discount_shipping_gross'],
				'total_discounts_gross'   => $math['total_discounts_gross'],
				'final_total_gross'       => $math['final_total_gross'],
			],
      'cart_data'        				=> $details,
	    'payment_providers'  			=> $payment_providers,
	    'customer_addresses' 			=> $customer_addresses,
      'post'             				=> $post,
    ];
  }
  
	private function computeCartMath(?array $shipping_preview, array $discount_totals): array {
	  $cart = $this->cart->get() ?? [];
	
	  $items_gross_before = (float)($cart['totals']['total_gross'] ?? 0.0);
	
	  $shipping_gross = (float)($shipping_preview['price_gross'] ?? 0.0);
	  $subtotal_gross = $items_gross_before + $shipping_gross;
	
	  $subtotal_net = (float)($cart['totals']['subtotal_net'] ?? 0.0);
	  $cart_tax = (float)($cart['totals']['tax'] ?? 0.0);
	  $avg_rate_items = $subtotal_net > 0 ? ($cart_tax / $subtotal_net) : 0.0;
	  $shipping_rate  = isset($shipping_preview['tax_rate']) ? ((float)$shipping_preview['tax_rate'] / 100.0) : 0.0;
	
	  $discount_order_net = (float)($discount_totals['discount_order_net'] ?? 0.0);
	  $discount_shipping_net = (float)($discount_totals['discount_shipping_net'] ?? 0.0);
	
	  $discount_items_gross = $discount_order_net * (1.0 + $avg_rate_items);
	  $discount_shipping_gross = $discount_shipping_net * (1.0 + $shipping_rate);
	  $total_discounts_gross = $discount_items_gross + $discount_shipping_gross;
	
	  $final_total_gross = max(0.0, $subtotal_gross - $total_discounts_gross);
	
	  return [
	    'items_gross_before'    		=> $items_gross_before,
	    'shipping_gross'            => $shipping_gross,
	    'subtotal_gross'        		=> $subtotal_gross,
	    'avg_rate_items'        		=> $avg_rate_items,
	    'shipping_rate'         		=> $shipping_rate,
	    'discount_items_gross'      => $discount_items_gross,
	    'discount_shipping_gross'   => $discount_shipping_gross,
	    'total_discounts_gross' 		=> $total_discounts_gross,
	    'final_total_gross'     		=> $final_total_gross,
	  ];
	}


  private function prefillCheckoutPost(array $post, array $stash, ?int $id_customer, bool $overview = false): array {
  	$all_payment_providers = $this->orders->getActivePaymentProviders();
		$payment_providers = array_values(array_filter(
	    $all_payment_providers,
	    fn($payment_provider) => ($payment_provider['create_point'] ?? '') !== 'cart'
		));
    $prefill = [];

    if (!empty($stash['billing'])) {
      foreach ($stash['billing'] AS $key => $value) {
      	$prefill[$key] = $value;
      }
      if (!empty($stash['billing_raw_state'])) $prefill['id_state'] = $stash['billing_raw_state'];
      if (!empty($stash['billing_raw_country'])) $prefill['id_country'] = $stash['billing_raw_country'];
    }

    $prefill['r_shipping_address'] = (string)($stash['shipping_mode'] ?? $stash['r_shipping_address'] ?? Orders::MODE_SAME);

    if ((int)$prefill['r_shipping_address'] === Orders::MODE_DIFFERENT) {
      if (isset($stash['shipping_address_id'])) {
        $prefill['shipping_address_id'] = (string)(int)$stash['shipping_address_id'];
      }
      if (isset($stash['c_save_address'])) {
        $prefill['c_save_address'] = $stash['c_save_address'] ? '1' : '0';
      } else {
        $prefill['c_save_address'] = (!empty($prefill['shipping_address_id']) && (int)$prefill['shipping_address_id'] > 0) ? '0' : '1';
      }
      if (!empty($stash['shipping'])) {
        foreach ($stash['shipping'] AS $key => $value) { 
        	$prefill['delivery_'.$key] = $value;
        }
        if (!empty($stash['shipping_raw_state'])) $prefill['delivery_id_state'] = $stash['shipping_raw_state'];
        if (!empty($stash['shipping_raw_country'])) $prefill['delivery_id_country'] = $stash['shipping_raw_country'];
      }
    }

    if (!empty($stash['notes_customer'])) $prefill['notes_customer'] = $stash['notes_customer'];
    if (!empty($stash['c_terms'])) $prefill['c_terms'] = '1';
    if (!empty($stash['c_cancellation'])) $prefill['c_cancellation'] = '1';

    $prefill_payment_id = $stash['payment']['id'] ?? null;
    if (!$prefill_payment_id && !empty($stash['payment']['key']) && !empty($payment_providers)) {
      foreach ($payment_providers AS $payment_provider) {
        if (($payment_provider['provider_key'] ?? null) === $stash['payment']['key']) {
          $prefill_payment_id = (string)$payment_provider['id']; break;
        }
      }
    }
    if ($prefill_payment_id) $prefill['r_payment_method'] = (string)$prefill_payment_id;
    elseif (!empty($payment_providers)) $prefill['r_payment_method'] = (string)$payment_providers[0]['id'];

    if (!$overview && $id_customer) {
    	
    }

    return $prefill + $post;
  }

  private function resolveSelectedCountry(array $post, array $stash, int $shipping_mode): int {
    $selected = (int)($post['id_country'] ?? ($stash['billing_raw_country'] ?? 0));
    if ($shipping_mode === Orders::MODE_DIFFERENT) {
      $selected = (int)($post['delivery_id_country'] ?? ($stash['shipping_raw_country'] ?? $selected));
    }
    return $selected > 0 ? $selected : $this->fallbackCountry;
  }

  private function resolveCurrentMethodKey(array $post, array $stash, array $methods, string $default): string {
    $key = trim($post['r_shipping_method'] ?? ($stash['r_shipping_method'] ?? ''));
    if ($key === '' || !array_filter($methods, fn($method) => ($method['key'] ?? null) === $key)) {
      $key = $methods[0]['key'] ?? $default;
    }
    return $key;
  }

  private function errorsToNote(array $errors): string {
    $note = null;
    foreach ($errors AS $error) {
      if ($note) $note .= "<br />";
      $key = 'LABEL_'.strtoupper($error);
      $note .= defined($key) ? constant($key) : $key;
    }
    return $note ?? '';
  }

  private function buildUrl(array $opts, string $route, array $query = []): string {
    $lang_prefix = !empty($opts['c_languages']) ? '/'.($opts['language'] ?? 'de') : '';
    $base = $lang_prefix . '/' . $route . '/';
    if ($query) $base .= '?'.http_build_query($query);
    return $base;
  }

  private function buildPaymentErrorRedirect(array $opts, string $provider, array $errors): string {
    $lang_prefix = !empty($opts['c_languages']) ? '/'.($opts['language'] ?? 'de') : '';
    foreach ($errors AS $error) {
      $key_specific = 'LABEL_'.strtoupper($error).'_'.strtoupper($provider);
      $key_generic  = 'LABEL_'.strtoupper($error);
      if (defined($key_specific)) return $lang_prefix.'/checkout/?note='.$key_specific;
      if (defined($key_generic))  return $lang_prefix.'/checkout/?note='.$key_generic;
    }
    $fallback = defined('LABEL_PAYMENT_ERROR_'.strtoupper($provider)) ? 'LABEL_PAYMENT_ERROR_'.strtoupper($provider) : 'LABEL_PAYMENT_ERROR';
    return $lang_prefix.'/checkout/?note='.$fallback;
  }
}
