<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
use Exception;

/**
 * Shopify API connection - same approach as productos-costo-cero(1).php
 * (OAuth client_credentials token + GraphQL).
 */
class ShopifyService
{
    protected string $shop;
    protected string $clientId;
    protected string $clientSecret;
    protected string $apiVersion;

    public function __construct()
    {
        $this->shop = config('services.shopify.shop');
        $this->clientId = config('services.shopify.client_id');
        $this->clientSecret = config('services.shopify.client_secret');
        $this->apiVersion = config('services.shopify.api_version');
    }

    /**
     * Get access token (client_credentials). Cached 23h to avoid repeated calls.
     */
    public function getAccessToken(): string
    {
        $cacheKey = 'shopify_access_token_' . $this->shop;
        return Cache::remember($cacheKey, 60 * 23, function () {
            $url = "https://{$this->shop}.myshopify.com/admin/oauth/access_token";
            $response = Http::asForm()
                ->timeout(30)
                ->post($url, [
                    'grant_type' => 'client_credentials',
                    'client_id' => $this->clientId,
                    'client_secret' => $this->clientSecret,
                ]);

            if (!$response->successful()) {
                throw new Exception('Shopify token HTTP ' . $response->status() . ': ' . $response->body());
            }

            $json = $response->json();
            if (empty($json['access_token'])) {
                throw new Exception('Shopify token invalid: ' . $response->body());
            }

            return $json['access_token'];
        });
    }

    /**
     * Run GraphQL query/mutation (same as shopify_graphql in reference file).
     */
    public function graphql(string $query, array $variables = []): array
    {
        $token = $this->getAccessToken();
        $url = "https://{$this->shop}.myshopify.com/admin/api/{$this->apiVersion}/graphql.json";

        $response = Http::withHeaders([
            'X-Shopify-Access-Token' => $token,
            'Content-Type' => 'application/json',
        ])
            ->timeout(60)
            ->post($url, [
                'query' => $query,
                'variables' => (object) $variables,
            ]);

        if (!$response->successful()) {
            throw new Exception('Shopify GraphQL HTTP ' . $response->status() . ': ' . $response->body());
        }

        $json = $response->json();
        if (!is_array($json)) {
            throw new Exception('Shopify GraphQL invalid JSON: ' . $response->body());
        }
        if (!empty($json['errors'])) {
            throw new Exception('Shopify GraphQL errors: ' . json_encode($json['errors']));
        }

        return $json;
    }

    /**
     * Search products by title (query string for Shopify, same style as reference file).
     */
    public function searchProductsByTitle(string $query, int $first = 50, ?string $cursor = null): array
    {
        $q = $query !== '' ? trim($query) : 'status:active';
        $query = <<<'GQL'
query($cursor: String, $q: String!, $first: Int!) {
  products(first: $first, after: $cursor, query: $q) {
    edges {
      node {
        id
        title
        status
        media(first: 1, query: "media_type:IMAGE") {
          edges {
            node {
              ... on MediaImage {
                image { url }
              }
            }
          }
        }
        variants(first: 100) {
          edges {
            node {
              id
              price
              compareAtPrice
              inventoryItem {
                unitCost { amount }
              }
            }
          }
        }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
GQL;

        $vars = ['q' => $q, 'first' => $first];
        if ($cursor) {
            $vars['cursor'] = $cursor;
        }

        $resp = $this->graphql($query, $vars);
        return $resp['data']['products'] ?? ['edges' => [], 'pageInfo' => ['hasNextPage' => false, 'endCursor' => null]];
    }

    /**
     * Update variant price and compareAtPrice (single variant). Kept for restore.
     */
    public function updateVariantPrice(string $variantId, string $price, ?string $compareAtPrice = null): array
    {
        $query = <<<'GQL'
mutation productVariantUpdate($input: ProductVariantInput!) {
  productVariantUpdate(input: $input) {
    productVariant { id price compareAtPrice }
    userErrors { field message }
  }
}
GQL;

        $input = ['id' => $variantId, 'price' => $this->formatPrice($price)];
        if ($compareAtPrice !== null && $compareAtPrice !== '') {
            $input['compareAtPrice'] = $this->formatPrice($compareAtPrice);
        }

        $resp = $this->graphql($query, ['input' => $input]);
        $data = $resp['data']['productVariantUpdate'] ?? null;
        if ($data && !empty($data['userErrors'])) {
            throw new Exception('Shopify update errors: ' . json_encode($data['userErrors']));
        }
        return $data ?? [];
    }

    /**
     * Bulk update variants by product (same approach as price-update-testing-collection).
     * $variants = [ ['id' => variantGid, 'price' => '123.00', 'compareAtPrice' => '200.00' or null], ... ]
     */
    public function productVariantsBulkUpdate(string $productId, array $variants): array
    {
        $query = <<<'GQL'
mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
  productVariantsBulkUpdate(productId: $productId, variants: $variants) {
    product { id }
    productVariants { id }
    userErrors { field message }
  }
}
GQL;

        $variantsInput = [];
        foreach ($variants as $v) {
            $row = ['id' => $v['id'], 'price' => $this->formatPrice($v['price'])];
            if (isset($v['compareAtPrice']) && $v['compareAtPrice'] !== null && $v['compareAtPrice'] !== '') {
                $row['compareAtPrice'] = $this->formatPrice($v['compareAtPrice']);
            }
            $variantsInput[] = $row;
        }

        $resp = $this->graphql($query, ['productId' => $productId, 'variants' => $variantsInput]);
        $data = $resp['data']['productVariantsBulkUpdate'] ?? null;
        if ($data && !empty($data['userErrors'])) {
            throw new Exception('Shopify bulk update errors: ' . json_encode($data['userErrors']));
        }
        return $data ?? [];
    }

    /** Price string for Shopify API (2 decimals, no thousands). */
    private function formatPrice($value): string
    {
        return number_format((float) $value, 2, '.', '');
    }
}
