<?php

namespace Pixeldev\SquareWooSync\REST;

use Pixeldev\SquareWooSync\Abstracts\RESTController;
use Pixeldev\SquareWooSync\Logger\Logger;
use Pixeldev\SquareWooSync\Square\SquareHelper;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

if (!defined('ABSPATH')) {
  exit; // Exit if accessed directly
}

/**
 * API SettingsController class for plugin settings.
 *
 * @since 0.5.0
 */
class OrdersController extends RESTController
{

  /**
   * Endpoint namespace.
   *
   * @var string
   */
  protected $namespace = 'sws/v1';

  /**
   * Route base.
   *
   * @var string
   */
  protected $base = 'orders';

  /**
   * Register routes for settings.
   *
   * @return void
   */
  public function register_routes()
  {
    register_rest_route(
      $this->namespace,
      '/' . $this->base,
      [
        [
          'methods' => WP_REST_Server::READABLE,
          'callback' => [$this, 'get_orders'],
          'permission_callback' => [$this, 'check_permission'],
        ],
        [
          'methods' => WP_REST_Server::EDITABLE,
          'callback' => [$this, 'create_square_order'],
          'permission_callback' => [$this, 'check_permission'],
        ]
      ]
    );
  }

  /**
   * Create an order in Square based on WooCommerce order data.
   *
   * @param WP_REST_Request $request Full details about the request.
   * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   */
  public function create_square_order(WP_REST_Request $request): WP_REST_Response
  {
    // Initialize SquareHelper
    $square = new SquareHelper();
    $order_id = intval($request->get_param('order_id'));
    $order = wc_get_order($order_id);

    if (!$order) {
      return new WP_REST_Response(['error' => 'Order not found'], 404);
    }

    try {
      // Retrieve or create Square customer ID
      $square_customer_id = $this->getOrCreateSquareCustomer($order, $square);

      if (isset($square_customer_id['error'])) {
        Logger::log('error', esc_html__('Square Orders error: ', 'squarewoosync') . esc_html($square_customer_id['error']));
        // Return the detailed error message in the response
        return new WP_REST_Response(['error' => esc_html__('Square Orders error: ', 'squarewoosync') . esc_html($square_customer_id['error'])], 400);
      }

      // Prepare order data for Square
      $order_data = $this->prepareSquareOrderData($order, $square_customer_id);
      $response = $this->createOrderInSquare($order_data, $square);

      // Check for errors in the response
      if (isset($response['error'])) {
        Logger::log('error', esc_html__('Square Orders API error: ', 'squarewoosync') . esc_html($response['error']));
        // Log this error appropriately
        return new WP_REST_Response(['error' => esc_html__('Square API error: ', 'squarewoosync') . esc_html($response['error'])], 502);
      }


      $payment_gateways = WC()->payment_gateways->payment_gateways();

      $settings = get_option('square-woo-sync_settings', []);
      if ((isset($settings['orders']['transactions']) && $settings['orders']['transactions'] === true) || (isset($payment_gateways['squaresync_credit']) && $payment_gateways['squaresync_credit']->enabled === 'yes')) {
        $payResponse = $this->payForOrder($response['data']['order'], $square);

        if (isset($payResponse['error'])) {
          Logger::log('error', esc_html__('Square Payment API error: ', 'squarewoosync') . esc_html($payResponse['error']));
          // Line 93
          return new WP_REST_Response(['error' => esc_html__('Square API error: ', 'squarewoosync') . esc_html($payResponse['error'])], 502);
        }

        if (isset($response['data']['order']['id']) && isset($payResponse['data']['payment']['id'])) {
          $square_data = ['order' => $response, 'payment' => $payResponse];

          // Save Square order ID and payment ID to WooCommerce order meta
          $order->update_meta_data('square_data', wp_json_encode($square_data));
          if (isset($response['data']['order']['id'])) {
            $order->update_meta_data('square_order_id',  $response['data']['order']['id']);
          }

          // Save changes to the order
          $order->save();
        }

        Logger::log('success', esc_html__('Order and Transaction created in Square, receipt: #', 'squarewoosync') . sanitize_text_field($payResponse['data']['payment']['receipt_number']));
        return new WP_REST_Response(['data' => ['order' => $response, 'payment' => $payResponse]], 200);
      } else {



        // No transaction ID, so no payment update response
        $square_data = [
          'order' => $response,
          'payment' => '',
        ];

        // Save the order details
        $order->update_meta_data('square_data', wp_json_encode($square_data));
        if (isset($response['data']['order']['id'])) {
          $order->update_meta_data('square_order_id',  $response['data']['order']['id']);
        }
        $order->save();


        Logger::log('success', esc_html__('Order created in Square', 'squarewoosync'));
        return new WP_REST_Response(['data' => $square_data], 200);
      }
    } catch (\Exception $e) {
      if ($e->getMessage() == 'Square location not set') {
        return new WP_REST_Response(['error' => $e->getMessage()], 401);
      }
      Logger::log('error', esc_html__('Failed to create order: ', 'squarewoosync') . esc_html($e->getMessage()));

      return new WP_REST_Response(['error' => esc_html($e->getMessage())], 500);
    }
  }


  public function payForOrder(array $order_data, $square): array
  {
    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['location'])) {
      throw new \Exception('Square location not set', 401);
    }
    // The endpoint for creating an order in Square
    $endpoint = '/payments';

    $payment_data = [];
    $payment_data['idempotency_key'] = uniqid();
    $payment_data['location_id'] = $settings['location'];
    $payment_data['amount_money'] = [
      'amount' => (int)$order_data['total_money']['amount'], // Ensure this is an integer in the smallest currency unit
      //'currency' => 'AUD',
      'currency' => $order_data['total_money']['currency'], // e.g., 'USD'
    ];
    $payment_data['source_id'] = 'EXTERNAL'; // Make sure this is appropriate for your payment method
    $payment_data['order_id'] = $order_data['id'];
    $payment_data['external_details'] = ['source' => 'Website Order', 'type' => 'OTHER'];

    // Making the API request to Square to create the order
    $response = $square->square_api_request($endpoint, 'POST', $payment_data);

    return $response;
  }

  public function createOrderInSquare(array $order_data, $square): array
  {

    // The endpoint for creating an order in Square
    $endpoint = '/orders';

    // Making the API request to Square to create the order
    $response = $square->square_api_request($endpoint, 'POST', $order_data);

    return $response;
  }


  public function getOrCreateSquareCustomer($order, $square)
  {
    // Get the user ID from the order
    $user_id = $order->get_user_id();

    // Check if the user has the 'square_customer_id' meta field
    if ($user_id) {
      $square_customer_id = get_user_meta($user_id, 'square_customer_id', true);

      if (!empty($square_customer_id)) {
        // Return the Square customer ID from user meta
        return $square_customer_id;
      }
    }

    // Proceed to search for the customer via Square API using the billing email
    $woo_customer_email = $order->get_billing_email();
    $search_customer_result = $square->square_api_request('/customers/search', 'POST', [
      'query' => ['filter' => ['email_address' => ["exact" => $woo_customer_email]]]
    ]);

    // If customer already exists in Square, return the ID and update user meta
    if (!empty($search_customer_result['data']['customers'])) {
      $square_customer_id = $search_customer_result['data']['customers'][0]['id'];

      // Save the Square customer ID to user meta for future use
      if ($user_id) {
        update_user_meta($user_id, 'square_customer_id', $square_customer_id);
      }

      return $square_customer_id;
    }

    // Customer doesn't exist in Square, attempt to create a new one
    $new_customer_data = $this->formatNewCustomerData($order);
    $create_customer_result = $square->square_api_request('/customers', 'POST', $new_customer_data);

    if (!empty($create_customer_result['data']['customer']['id'])) {
      $square_customer_id = $create_customer_result['data']['customer']['id'];

      // Save the new Square customer ID to user meta
      if ($user_id) {
        update_user_meta($user_id, 'square_customer_id', $square_customer_id);
      }

      // Return the new customer ID
      return $square_customer_id;
    }

    // Handle cases where customer creation failed
    return $create_customer_result;
  }




  public function prepareSquareOrderData($order, string $square_customer_id, $location_id = null, $state = 'OPEN'): array
  {
    $settings = get_option('square-woo-sync_settings', []);
    if (!isset($settings['location'])) {
      throw new \Exception('Square location not set', 401);
    }

    $locationId = isset($settings['location']) && !empty($settings['location']) && !is_null($settings['location']) ? $settings['location'] : ($location_id ?? '');



    // Prepare the final order data with all line items, taxes, and fulfillment details
    $order_data = [
      'order' => [
        'location_id' => $locationId,
        'customer_id' => $square_customer_id,
        'metadata' => ['woo_order_id' => strval($order->get_id())],
        'state' => $state,
        'ticket_name' => 'WooCommerce - #' . $order->get_id(),
        'reference_id' => 'WooCommerce - #' . $order->get_id(),
        'source' => ['name' => 'WooCommerce - #' . $order->get_id()],
      ]
    ];

    // Add the note field only if the WooCommerce order contains a note
    $customer_note = $order->get_customer_note();
    if (!empty($customer_note)) {
      $order_data['order']['note'] = mb_substr($customer_note, 0, 500); // Limit to 500 characters
    }

    $taxes_enabled = get_option('woocommerce_calc_taxes') === 'yes';
    $is_tax_inclusive = get_option('woocommerce_prices_include_tax') === 'yes';
    $tax_uids = [];

    // Apply tax as additive, Square will calculate based on this.
    if ($taxes_enabled) {
      // Get taxes for regular items
      foreach ($order->get_items('tax') as $item_id => $item) {
        $tax_rate_id = $item->get_rate_id();
        $tax_percentage_string = \WC_Tax::get_rate_percent($tax_rate_id); // Get the tax percentage (e.g., "10%")
        $tax_percentage = rtrim($tax_percentage_string, '%'); // Strip the "%" symbol, keep it as a string

        // Create unique tax UID using rate ID to avoid duplicates
        $tax_uid = 'order-tax-' . $tax_rate_id;

        // Apply tax data to the order
        $order_data['order']['taxes'][] = [
          'uid' => $tax_uid,
          'name' => $item->get_label(),
          'percentage' => $tax_percentage, // Tax percentage
          'scope' => 'ORDER',
          'inclusion_type' => $is_tax_inclusive ? 'INCLUSIVE' : 'ADDITIVE', // Set inclusion type based on WooCommerce setting
        ];
        $tax_uids[] = $tax_uid;
      }
    }

    // Fetch line items with updated tax handling
    $line_items = $this->getOrderLineItems($order, $tax_uids);
    $order_data['order']['line_items'] = $line_items;

    // Handle discounts (ensure discounts are subtracted after tax is calculated)
    $discounts = $this->getOrderDiscounts($order);
    if (!empty($discounts)) {
      $order_data['order']['discounts'] = $discounts;
    }

    // Fulfillments
    $fulfillments = $this->getOrderFulfillments($order);
    if (!empty($fulfillments)) {
      $order_data['order']['fulfillments'] = $fulfillments;
    }

    // Add shipping as a line item if applicable
    $shipping_line_item = $this->addShippingServiceCharge($order);
    if (!empty($shipping_line_item)) {
      $order_data['order']['service_charges'] = $shipping_line_item;
    }

    // Add order fees as service charges
    $order_fee_items = $this->addOrderFeesAsServiceCharges($order);
    if (!empty($order_fee_items)) {
      // If there are already service charges (e.g., shipping), merge them
      if (!empty($order_data['order']['service_charges'])) {
        $order_data['order']['service_charges'] = array_merge($order_data['order']['service_charges'], $order_fee_items);
      } else {
        $order_data['order']['service_charges'] = $order_fee_items;
      }
    }
    return $order_data;
  }

  public function get_currency_multiplier()
  {
    $currency = get_woocommerce_currency();
    $zero_decimal_currencies = ['BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'];

    if (in_array($currency, $zero_decimal_currencies)) {
      return 1;
    } else {
      return pow(10, wc_get_price_decimals());
    }
  }

  /**
   * Get the Square location ID for an order.
   * Can be filtered to allow custom location logic based on order properties.
   *
   * @param \WC_Order $order The WooCommerce order object.
   * @return string The Square location ID.
   */
  public function getLocationId($order)
  {
    $settings = get_option('square-woo-sync_settings', []);
    $location_id = $settings['location'] ?? '';

    /**
     * Filter the Square location ID for an order.
     *
     * Allows customization of which Square location to use based on order properties
     * such as shipping address, product categories, or custom meta data.
     *
     * @param string $location_id The default location ID from settings.
     * @param \WC_Order $order The WooCommerce order object.
     * @return string The filtered location ID.
     */
    return apply_filters('square_woo_sync_order_location_id', $location_id, $order);
  }

  public function addOrderFeesAsServiceCharges($order): array
  {
    $service_charges = [];
    $currency = $order->get_currency(); // Order currency
    $multiplier = $this->get_currency_multiplier();

    foreach ($order->get_fees() as $fee) {
      $fee_amount = $fee->get_total(); // Fee amount
      $fee_name = $fee->get_name(); // Fee name

      // Skip the loyalty discount fee
      if (
        $fee_name === 'Loyalty Discount'
      ) {

        continue; // Skip this fee if it's the loyalty discount
      }

      if ($fee_amount != 0) {
        $service_charges[] = [
          'name' => $fee_name ?: 'Additional fee',
          "scope" => "ORDER",
          'calculation_phase' => wc_prices_include_tax() === 1 ? 'SUBTOTAL_PHASE' : 'TOTAL_PHASE', // Assume no tax for simplicity
          'amount_money' => [
            'amount' => round($fee_amount * $multiplier),
            'currency' => $currency,
          ],
          'taxable' => wc_prices_include_tax() === 1 ? false : true,
        ];
      }
    }

    return $service_charges;
  }



  public function getOrderDiscounts($order): array
  {
    $discounts = [];
    $currency = $order->get_currency(); // Order currency
    $total_discount = $order->get_total_discount();

    $multiplier = $this->get_currency_multiplier();

    // Handle coupon discounts
    foreach ($order->get_items('coupon') as $coupon_item) {
      $coupon_code = $coupon_item->get_code();

      if ($total_discount > 0) {
        $discounts[] = [
          'uid' => 'order-discount-' . $coupon_code,  // Unique ID for the discount
          'name' => 'Total Discount (' . $coupon_code . ')',
          'amount_money' => [
            'amount' => round($coupon_item->get_discount() * $multiplier),  // Convert to cents
            'currency' => $currency,
          ],
          'scope' => 'ORDER',  // Apply the discount to the entire order
        ];
      }
    }



    $settings = get_option('square-woo-sync_settings', []);
    if (isset($settings['loyalty']['redemptionMethod']) && $settings['loyalty']['redemptionMethod'] === 'custom') {
      // Handle loyalty discount as an additional discount
      foreach ($order->get_fees() as $fee) {
        $fee_name = $fee->get_name();
        if ($fee_name === 'Loyalty Discount') {
          $fee_amount = $fee->get_total(); // Loyalty discount amount

          if ($fee_amount != 0) {
            $discounts[] = [
              'uid' => 'loyalty-discount',  // Unique ID for the loyalty discount
              'name' => 'Loyalty Discount',
              'amount_money' => [
                'amount' => round(abs($fee_amount) * $multiplier), // Convert to cents, abs ensures the amount is positive
                'currency' => $currency,
              ],
              'scope' => 'ORDER',  // Apply the discount to the entire order
            ];
          }
        }
      }
    }


    return $discounts;
  }


  public function formatNewCustomerData($order): array
  {
    // Initialize the base structure with potentially always available data
    $customerData = [
      "address" => [], // Ensures the address is structured correctly
    ];

    // Define a helper function to safely add data if not empty
    $addIfNotEmpty = function ($key, $value) use (&$customerData) {
      if (!empty($value)) {
        // Check if the key is related to the address
        if (strpos($key, 'address_') === 0) { // Address-related key
          // Remove the first occurrence of 'address_' from the key
          // This is done by replacing 'address_' with '', but only for the first occurrence
          $adjustedKey = preg_replace('/^address_/', '', $key, 1);
          // Add to the 'address' array with the correctly formatted key
          $customerData['address'][$adjustedKey] = $value;
        } else {
          $customerData[$key] = $value;
        }
      }
    };

    // Add address details with correct key formatting
    $addIfNotEmpty('address_address_line_1', $order->get_billing_address_1());
    $addIfNotEmpty('address_address_line_2', $order->get_billing_address_2());
    $addIfNotEmpty('address_country', $order->get_billing_country());
    $addIfNotEmpty('address_administrative_district_level_1', $order->get_billing_state());
    $addIfNotEmpty('address_locality', $order->get_billing_city());
    $addIfNotEmpty('address_postal_code', $order->get_billing_postcode());
    $addIfNotEmpty('address_first_name', $order->get_billing_first_name());
    $addIfNotEmpty('address_last_name', $order->get_billing_last_name());

    // Add other details
    $addIfNotEmpty('company_name', $order->get_billing_company());
    $addIfNotEmpty('email_address', $order->get_billing_email());
    $addIfNotEmpty('family_name', $order->get_billing_last_name());
    $addIfNotEmpty('given_name', $order->get_billing_first_name());

    return $customerData;
  }



  public function mapOrderStatus(string $status): string
  {
    $statusMap = [
      'pending' => 'DRAFT',
      'processing' => 'OPEN',
      'on-hold' => 'OPEN',
      'completed' => 'COMPLETED',
      'cancelled' => 'CANCELLED',
      'refunded' => 'CANCELLED',
      'failed' => 'CANCELLED',
    ];

    //return $statusMap[$status] ?? 'DRAFT';
    return 'OPEN';
  }

  private function get_modifier_price_incl_tax($modifier_id)
  {
    // Get the modifier settings
    $settings = get_option('square-woo-sync_settings', []);
    $modifier_mappings = isset($settings['modifiers']) ? $settings['modifiers'] : [];

    foreach ($modifier_mappings as $mapping) {
      foreach ($mapping['modifiers'] as $id => $modifier) {
        if ($id === $modifier_id) {
          if (isset($modifier['price'])) {
            return floatval($modifier['price']);
          } else {
            return 0;
          }
        }
      }
    }
    return 0;
  }

  public function getOrderLineItems($order, $tax_uids = []): array
  {
    $line_items = [];
    $currency = $order->get_currency();

    // Determine if taxes are enabled and if prices are tax-inclusive
    $taxes_enabled = get_option('woocommerce_calc_taxes') === 'yes';
    $is_tax_inclusive = get_option('woocommerce_prices_include_tax') === 'yes';

    $multiplier = $this->get_currency_multiplier();

    // Get the tax rate percentage from WooCommerce settings
    $tax_percentage = 0;
    $tax_rate_percentage = '0';
    if ($taxes_enabled) {
      $tax_rates = \WC_Tax::get_rates('');
      if (!empty($tax_rates)) {
        $tax_rate = reset($tax_rates);
        $tax_percentage = floatval($tax_rate['rate']) / 100;
        $tax_rate_percentage = strval($tax_rate['rate']);
      }
    }

    // Get the modifier mappings from settings
    $settings = get_option('square-woo-sync_settings', []);
    $modifier_mappings = isset($settings['modifiers']) ? $settings['modifiers'] : [];

    foreach ($order->get_items() as $item_id => $item) {
      $product = $item->get_product();
      $quantity = $item->get_quantity();

      // Use the actual price paid from the order item, not the current product price
      // This ensures that if a product was on sale at the time of purchase, we sync the sale price
      if ($taxes_enabled && $is_tax_inclusive) {
        // Get the line total excluding tax (what the customer paid before tax)
        $price = $item->get_subtotal() / $quantity;
      } else {
        // Get the line total (what the customer actually paid per item)
        $price = $item->get_total() / $quantity;
      }
      $price_cents = round($price * $multiplier);



      // Prepare the line item for Square
      $line_item = [
        'name'             => $item->get_name(),
        'quantity'         => strval($quantity),
        'base_price_money' => [
          'amount'   => $price_cents,
          'currency' => $currency,
        ],
        'item_type'        => 'ITEM',

      ];

      if (!empty($tax_uids)) {
        $line_item['applied_taxes'] = array_map(function($uid) {
          return ['tax_uid' => $uid];
        }, $tax_uids);
      }

      // Collect modifiers for the line item
      $line_item_modifiers = [];

      // Get the meta data of the line item
      $meta_data_objects = $item->get_meta_data();

      // Normalize meta data keys to lowercase and collect values
      $normalized_meta_data = [];
      foreach ($meta_data_objects as $meta_data) {
        $key = strtolower($meta_data->key);
        $value = $meta_data->value;
        if (!isset($normalized_meta_data[$key])) {
          $normalized_meta_data[$key] = [];
        }
        $normalized_meta_data[$key][] = $value;
      }

      // Process modifiers based on the mappings
      foreach ($modifier_mappings as $mapping) {
        $parent_key = strtolower($mapping['parent']);

        if (isset($normalized_meta_data[$parent_key])) {
          $meta_values = $normalized_meta_data[$parent_key];

          // Loop over meta values
          foreach ($meta_values as $meta_value) {
            // Extract modifier name from meta value
            // Example meta_value: "Cheese ($1.50)"
            $modifier_name = $meta_value;

            // Loop over modifiers in the mapping
            foreach ($mapping['modifiers'] as $modifier_id => $modifier_data) {
              $modifier_value_lower = strtolower($modifier_data['value']);

              // Check if the mapped value is contained within the modifier name (case-insensitive)
              if (strpos(strtolower($modifier_name), $modifier_value_lower) !== false) {
                // Get the modifier price from settings
                $modifier_price_incl_tax = floatval($modifier_data['price']);

                // Calculate price excluding tax if necessary
                $modifier_price_excl_tax = $modifier_price_incl_tax;
                if ($is_tax_inclusive && $tax_percentage > 0) {
                  $modifier_price_excl_tax = $modifier_price_incl_tax / (1 + $tax_percentage);
                }

                // Convert to cents
                $modifier_price_cents = round($modifier_price_excl_tax * $multiplier);

                // Add to line item modifiers
                $line_item_modifiers[] = [
                  'catalog_object_id' => $modifier_id,
                  'quantity'          => '1',
                  'base_price_money'  => [
                    'amount'   => $modifier_price_cents,
                    'currency' => $currency,
                  ],
                ];
              }
            }
          }
        }
      }

      // Add modifiers to the line item if any
      if (!empty($line_item_modifiers)) {
        $line_item['modifiers'] = $line_item_modifiers;
      }

      $line_items[] = $line_item;
    }

    // Return line items
    return $line_items;
  }

  public function getCommonProductTaxRate($order)
  {
    foreach ($order->get_items() as $item_id => $item) {
      $_product = $item->get_product();
      if (!$_product) continue;

      $tax_class = $_product->get_tax_class();
      $tax_rates = \WC_Tax::get_base_tax_rates($tax_class); // Retrieve tax rates based on product's tax class

      $rate = !empty($tax_rates) ? reset($tax_rates) : null;
      return $rate ? $rate['rate'] : 0; // Return the first product's tax rate, or 0 if not applicable
    }

    return 0; // Default to 0 if no products found
  }


  public function addShippingServiceCharge($order): array
  {
    $shipping_total = $order->get_shipping_total(); // Total shipping cost
    $currency = $order->get_currency(); // Order currency
    $taxes_enabled = get_option('woocommerce_calc_taxes') === 'yes';
    $prices_include_tax = wc_prices_include_tax(); // Check if prices include tax

    // Initialize variables to store tax applicability and percentage
    $shipping_taxable = false;
    $tax_percentage = 0;

    if ($taxes_enabled) {
      // Get the shipping tax class from WooCommerce settings
      $shipping_tax_class = get_option('woocommerce_shipping_tax_class');

      // If shipping tax class is set to 'inherit', use the first available tax class from cart items
      if ($shipping_tax_class === 'inherit') {
        $shipping_tax_class = ''; // Fallback to the standard rate if 'inherit'
      }

      // Get the shipping tax rates applicable in the current cart/checkout context
      $shipping_tax_rates = \WC_Tax::get_shipping_tax_rates();

      // Check if any tax rate is applied to shipping
      $shipping_taxable = false;
      $shipping_tax = $order->get_shipping_tax(); // Get the total shipping tax

      // If there is shipping tax, check the rates
      if ($shipping_tax > 0 && !empty($shipping_tax_rates)) {
        foreach ($shipping_tax_rates as $tax_rate_id => $tax_rate) {
          if (isset($tax_rate['shipping']) && $tax_rate['shipping']) {
            $shipping_taxable = true; // Shipping is taxable
            $tax_percentage_string = $tax_rate['rate']; // Tax percentage (e.g., "10.000")
            $tax_percentage = rtrim($tax_percentage_string, '%'); // Remove the '%' symbol if needed
            break; // Exit once a matching shipping tax rate is found
          }
        }
      }
    }

    $multiplier = $this->get_currency_multiplier();
    // // Apply the tax percentage to the shipping total if shipping is taxable and prices include tax
    // if ($shipping_taxable && $prices_include_tax) {
    //     $shipping_total += ($shipping_total * $tax_percentage / 100); // Add tax percentage to the shipping total
    // }

    // Build the service charge array
    if ($shipping_total > 0) {
      $service_charge = [
        'name' => 'Shipping',
        "scope" => "ORDER",
        'calculation_phase' =>  $shipping_taxable ? 'SUBTOTAL_PHASE' : 'TOTAL_PHASE',
        'amount_money' => [
          'amount' => wc_round_tax_total($shipping_total * $multiplier),
          'currency' => $currency,
        ],
        'taxable' => $shipping_taxable, // Set taxable to true if applicable
      ];

      return [$service_charge];
    }

    return [];
  }


  public function addFeeLineItems($order): array
  {
    $line_items = [];
    $currency = $order->get_currency(); // Order currency
    $multiplier = $this->get_currency_multiplier();

    foreach ($order->get_fees() as $fee_key => $fee) {
      $fee_amount = $fee->get_amount(); // Fee amount
      $fee_name = $fee->get_name(); // Fee name

      // Skip the loyalty discount fee
      if ($fee_name === 'Loyalty Discount') {
        continue; // Skip this fee if it's the loyalty discount
      }

      if ($fee_amount != 0) {
        $line_items[] = [
          'name' => $fee_name ?: 'Additional Fee',
          'quantity' => '1',
          'base_price_money' => [
            'amount' => round($fee_amount * $multiplier), // Convert to cents
            'currency' => $currency,
          ],
          'item_type' => 'ITEM',
        ];
      }
    }

    return $line_items;
  }


  public function getProductTaxRate($_product)
  {
    if (!$_product) return 0;

    $tax_class = $_product->get_tax_class();
    $tax_rates = \WC_Tax::get_base_tax_rates($tax_class); // Retrieve tax rates based on product's tax class

    // Assuming single tax rate per class for simplicity, otherwise, you'd need to handle multiple rates
    $rate = !empty($tax_rates) ? reset($tax_rates) : null;

    return $rate ? $rate['rate'] : 0; // Return the tax rate, or 0 if not applicable
  }


  public function getOrderFulfillments($order): array
  {
    // Retrieve the settings from the options table
    $settings = get_option('square-woo-sync_settings', []);
    $order_settings = $settings['orders'] ?? []; // Corrected key

    // Default values
    $pickup_method = $order_settings['pickupMethod'] ?? 'local_pickup';

    // Check if the order has shipping details
    $has_shipping = $order->get_shipping_address_1() || $order->get_shipping_address_2() || $order->get_shipping_city() || $order->get_shipping_postcode() || $order->get_shipping_country();

    // Check if the order is for pickup
    $shipping_lines = $order->get_shipping_methods();
    $is_pickup = false;

    foreach ($shipping_lines as $shipping_item) {
      if ($shipping_item->get_method_id() === $pickup_method) {
        $is_pickup = true;
        break;
      }
    }

    // If not pickup, return shipment fulfillment details
    if ($has_shipping && !$is_pickup) {
      return [
        [
          'type' => 'SHIPMENT',
          'state' => 'PROPOSED',
          'shipment_details' => [
            'recipient' => [
              'address' => [
                'address_line_1' => $order->get_shipping_address_1(),
                'address_line_2' => $order->get_shipping_address_2(),
                'administrative_district_level_1' => $order->get_shipping_state(),
                'locality' => $order->get_shipping_city(),
                'postal_code' => $order->get_shipping_postcode(),
                'country' => $order->get_shipping_country(),
              ],
              'display_name' => $order->get_formatted_billing_full_name(),
              'email_address' => $order->get_billing_email(),
              'phone_number' => $order->get_billing_phone(),
            ],
            'carrier' => '', // Optional
            'shipping_note' => '', // Optional
            'tracking_number' => '', // Optional
            'tracking_url' => '', // Optional
          ]
        ]
      ];
    }

    // Set pickup variables only if pickup is true
    if ($is_pickup) {
      $preparation_time = $order_settings['preparationTime'] ?? 60; // Default to 60 minutes
      $pickup_window_duration = $order_settings['pickupWindowDuration'] ?? 'P1D'; // Default to 1 day
      $pickup_schedule = $order_settings['pickupSchedule'] ?? [];

      // Calculate the preparation time duration in ISO 8601 format (e.g., 'PT60M' for 60 minutes)
      $prep_time_duration = 'PT' . $preparation_time . 'M';

      // Get the timezone set in WordPress settings
      $wp_timezone_string = get_option('timezone_string') ?: 'UTC';
      try {
        $timezone = new \DateTimeZone($wp_timezone_string);
      } catch (\Exception $e) {
        error_log("Error setting timezone: " . $e->getMessage());
        $timezone = new \DateTimeZone('UTC');
      }


      // Calculate initial pickup time (current time + preparation time) using the WordPress timezone
      $pickup_at = (new \DateTime('now', $timezone))
        ->add(new \DateInterval($prep_time_duration));

      // Adjust the pickup_at time based on the pickup schedule
      if (empty($pickup_schedule)) {
        $pickup_at = (new \DateTime('now', $timezone))->add(new \DateInterval('PT30M'));
      } else {
        $pickup_at = $this->adjustPickupTime($pickup_at, $pickup_schedule, $timezone);
      }
      // Convert the pickup time to UTC for the Square API
      $pickup_at_formatted = $pickup_at->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339);

      $expires_at = (new \DateTime('now', new \DateTimeZone('UTC')))
        ->add(new \DateInterval('P7D'))
        ->format(\DateTime::RFC3339);

      return [
        [
          'type' => 'PICKUP',
          'state' => 'PROPOSED',
          'pickup_details' => [
            'recipient' => [
              'display_name' => $order->get_formatted_billing_full_name(),
              'email_address' => $order->get_billing_email(),
              'phone_number' => $order->get_billing_phone(),
            ],
            'expires_at' => $expires_at,
            'auto_complete_duration' => 'P1W',
            'schedule_type' => 'SCHEDULED',
            'pickup_at' => $pickup_at_formatted,
            'pickup_window_duration' => $pickup_window_duration,
            'prep_time_duration' => $prep_time_duration,
            'note' => 'Please pick up your order at the front desk.',
          ]
        ]
      ];
    }

    return [];
  }
  /**
   * Adjusts the pickup time based on the pickup schedule.
   *
   * @param \DateTime $pickup_at The initial pickup time.
   * @param array $pickup_schedule The pickup schedule from settings.
   * @param \DateTimeZone $timezone The timezone for adjustments.
   * @return \DateTime Adjusted pickup time.
   */
  private function adjustPickupTime(\DateTime $pickup_at, array $pickup_schedule, \DateTimeZone $timezone): \DateTime
  {
    $pickup_day = $pickup_at->format('l');
    $pickup_date = $pickup_at->format('Y-m-d');
    $day_schedule = $pickup_schedule[$pickup_day] ?? [];

    // Default time values
    $default_from = '09:00';
    $default_to = '17:00';

    // Get the enabled status and times from the schedule, or apply defaults.
    $enabled = $day_schedule['enabled'] ?? false;
    $from_time = $day_schedule['from'] ?? $default_from;
    $to_time = $day_schedule['to'] ?? $default_to;


    // Create DateTime objects for the start and end of the available window
    $available_start = new \DateTime("$pickup_date $from_time", $timezone);
    $available_end = new \DateTime("$pickup_date $to_time", $timezone);

    // If the pickup time falls within the available window today, use it.
    if ($enabled && $pickup_at >= $available_start && $pickup_at <= $available_end) {
      return $pickup_at;
    }

    // If the pickup time is before today's available window, adjust to the start time.
    if ($enabled && $pickup_at < $available_start) {
      return $available_start;
    }

    // If today isn't suitable, move to the next day's window.
    $pickup_at->modify('+1 day')->setTime(0, 0);
    return $this->adjustPickupTime($pickup_at, $pickup_schedule, $timezone);
  }




  /**
   * Retrieves orders from WooCommerce with pagination.
   *
   * @param WP_REST_Request $request Request object.
   * @return WP_REST_Response Response object on success, or a response object encapsulating WP_Error on failure.
   */
  public function get_orders(WP_REST_Request $request): WP_REST_Response
  {
    $woocommerceInstalled = $this->check_woocommerce();
    if (!$woocommerceInstalled) {
      // Consistently create a WP_REST_Response object for the error
      return new WP_REST_Response(['error' => esc_html__('Woocommerce not installed or activated', 'squarewoosync')], 424);
    }

    $page = $request->get_param('page') ? intval($request->get_param('page')) : 1;
    $per_page = $request->get_param('per_page') ? intval($request->get_param('per_page')) : 10;

    $args = [
      'limit' => $per_page ? $per_page : -1,
      'pagination' => true,
      'page' => $page ? $page : 0,
    ];

    $orders = wc_get_orders($args);

    if (is_wp_error($orders)) {
      $error_message = $orders->get_error_message();
      $error_data = $orders->get_error_data();
      $status = isset($error_data['status']) ? $error_data['status'] : 500;
      // Return a consistent error object
      return new WP_REST_Response(['error' => esc_html($error_message)], $status);
    }

    if (empty($orders)) {
      return new WP_REST_Response([], 200);
    }

    $orders_data = array_map(function ($order) {
      if (!($order instanceof \WC_Order)) {
        // Handle non-WC_Order objects, such as WC_Order_Refund, or simply skip them
        return null; // or continue with some other logic
      }

      // Directly retrieving customer information from the order
      $customer_data = [
        'email' => $order->get_billing_email(),
        'company' => $order->get_billing_company(),
        'first_name' => $order->get_billing_first_name(),
        'last_name' => $order->get_billing_last_name(),
        // Include additional customer details as necessary
      ];

      // Conditionally add the customer ID if it exists
      if (method_exists($order, 'get_customer_id') && $order->get_customer_id()) {
        $customer_data['id'] = $order->get_customer_id();
      }

      // Line items
      $line_items_data = array_map(function ($item) {
        // Get the product object
        $product = $item->get_product();

        // Check if $product is not false
        if (!$product) {
          return [
            'product_name' => $item->get_name(),
            'product_id' => $item->get_product_id(),
            'sku' => '',
            'image' => '',
            'quantity' => $item->get_quantity(),
            'price' => '',
            'square_product_id' => '',
            'total' => $item->get_total(),
            'meta_data' => [], // No meta data if product is not found
          ];
        }

        $product_id = $product->get_id(); // Get the product ID
        $image_id = get_post_thumbnail_id($product_id);
        $featured_image_url = wp_get_attachment_image_url($image_id, 'thumbnail');
        // Retrieve the square_product_id meta value for the product
        $square_product_id = $product ? $product->get_meta('square_product_id') : '';

        return [
          'product_name' => $item->get_name(),
          'product_id' => $item->get_product_id(),
          'sku' => $product->get_sku(),
          'image' => $featured_image_url,
          'quantity' => $item->get_quantity(),
          'price' => $product->get_price(),
          'square_product_id' => $square_product_id,
          'total' => $item->get_total(),
        ];
      }, $order->get_items());

      $square_data = $order->get_meta('square_data', true);

      // Retrieve all order meta data
      $order_meta = [];
      $meta_data = $order->get_meta_data();
      foreach ($meta_data as $meta) {
        $order_meta[$meta->key] = $meta->value;
      }

      return [
        'id' => $order->get_id(),
        'status' => $order->get_status(),
        'total' => $order->get_total(),
        'order_total' => $order->get_total(), // Total amount of the order.
        'order_subtotal' => $order->get_subtotal(), // Total amount before taxes and shipping.
        'total_tax' => $order->get_total_tax(), // Total tax amount for the order.
        'shipping_total' => $order->get_shipping_total(), // Total shipping amount for the order.
        'discount_total' => $order->get_total_discount(), // Total discount amount applied to the order.
        'date' => get_date_from_gmt($order->get_date_created(), 'M jS Y, g:ia'),
        'customer' => $customer_data,
        'line_items' => array_values($line_items_data), // Ensure the array is reindexed
        'square_data' => !empty($square_data) ? $square_data : null,
        'meta_data' => $order_meta, // Include all order meta data
      ];
    }, $orders);

    return new WP_REST_Response([
      'orders' => $orders_data,
      'total' => count($orders),
      'max_num_pages' => ceil(count($orders) / $per_page),
    ], 200);
  }

  /**
   * Extract tip amount from order.
   * Free version doesn't support tips, so always returns 0.
   * Premium version would extract tip from order meta.
   *
   * @param \WC_Order $order The WooCommerce order object.
   * @return float The tip amount (always 0 in free version).
   */
  public function extractTipAmount($order)
  {
    // Free version doesn't support tips
    return 0.0;
  }
}
