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

use Google\Shopping\Merchant\Products\V1\Client\ProductsServiceClient;
use Google\Shopping\Merchant\Products\V1\ListProductsRequest;
use Google\Shopping\Merchant\Products\V1\Product;
use Google\Shopping\Merchant\Products\V1\ProductStatus\ItemLevelIssue\Severity;

/**
 * Scans all products in Google Merchant Center and collects issue data.
 * Generates a report grouped by issue code (image_link_missing, etc.)
 * and stores in a transient for admin display + CSV export.
 */
class JRA_Merchant_Report_Service {

    private JRA_Merchant_Auth   $auth;
    private JRA_Merchant_Logger $logger;
    private array               $reverseOfferIdMap = [];

    public function __construct() {
        $this->auth   = new JRA_Merchant_Auth();
        $this->logger = new JRA_Merchant_Logger();

        // Build reverse map: google_offer_id → local_offer_id for post ID resolution
        foreach ((new JRA_Merchant_Offer_Id_Map())->getAll(10000) as $row) {
            $this->reverseOfferIdMap[$row['google_offer_id']] = $row['local_offer_id'];
        }
    }

    // -------------------------------------------------------------------------
    // Public API
    // -------------------------------------------------------------------------

    /**
     * Generate an issue report by scanning products in our data source.
     * Stores result in transient 'jra_merchant_report_data'.
     * Call this from Action Scheduler for background execution.
     */
    public function generateIssueReport(): void {
        try {
            $this->logger->info('Starting report generation', 'report');

            $merchantId = $this->auth->getMerchantId();
            if ($merchantId === '') {
                throw new RuntimeException('No Merchant ID configured.');
            }
            if (!$this->auth->isConnected()) {
                throw new RuntimeException('Not connected to Google.');
            }

            $dataSource = $this->auth->getOrCreateDataSourceName();
            $client     = new ProductsServiceClient($this->auth->gapicClientOptions());
            $request    = new ListProductsRequest([
                'parent'    => ProductsServiceClient::accountName($merchantId),
                'page_size' => 250,
            ]);

            // Scan all products in our data source
            $issues = [];
            $pageCount = 0;
            $totalIssuesFound = 0;

            foreach ($client->listProducts($request)->iteratePages() as $page) {
                $pageCount++;

                foreach ($page as $product) {
                    /** @var Product $product */
                    if ($product->getDataSource() !== $dataSource) {
                        continue;
                    }

                    $offerId = $product->getOfferId();
                    $status  = $product->getProductStatus();
                    if (!$status) {
                        continue;
                    }

                    foreach ($status->getItemLevelIssues() ?? [] as $issue) {
                        $code = $issue->getCode() ?: 'unknown';

                        if (!isset($issues[$code])) {
                            $issues[$code] = [
                                'description' => $issue->getDescription(),
                                'severity'    => strtolower(Severity::name($issue->getSeverity())),
                                'count'       => 0,
                                'products'    => [],
                            ];
                        }

                        $totalIssuesFound++;

                        // One entry per unique product (keyed by offer ID); merge countries across markets
                        if (!isset($issues[$code]['products'][$offerId])) {
                            $issues[$code]['count']++;
                            $issues[$code]['products'][$offerId] = [
                                'offer_id'  => $offerId,
                                'post_id'   => $this->resolveWordPressId($offerId),
                                'title'     => $product->getProductAttributes()?->getTitle() ?? '',
                                'detail'    => $issue->getDetail() ?? '',
                                'attribute' => $issue->getAttribute() ?? '',
                                'countries' => [],
                            ];
                        }

                        $issues[$code]['products'][$offerId]['countries'] = array_values(array_unique(array_merge(
                            $issues[$code]['products'][$offerId]['countries'],
                            iterator_to_array($issue->getApplicableCountries() ?? [])
                        )));
                    }
                }

                // Update transient with progress
                set_transient('jra_merchant_report_data', [
                    'generated_at'    => current_time('mysql', true),
                    'status'          => 'running',
                    'error_message'   => null,
                    'pages_scanned'   => $pageCount,
                    'total_issues'    => $totalIssuesFound,
                    'issues'          => $issues,
                ], 3600);
            }

            // Sort issues by count (descending)
            uasort($issues, fn($a, $b) => $b['count'] <=> $a['count']);

            // Store result
            $reportData = [
                'generated_at'  => current_time('mysql', true),
                'status'        => 'complete',
                'error_message' => null,
                'pages_scanned' => $pageCount,
                'total_issues'  => array_sum(array_column($issues, 'count')),
                'issues'        => $issues,
            ];

            set_transient('jra_merchant_report_data', $reportData, 86400); // 24 hours

            $this->logger->info(
                sprintf('Report complete — %d issue types, %d total issues, %d pages scanned',
                    count($issues),
                    $reportData['total_issues'],
                    $pageCount
                ),
                'report'
            );

        } catch (\Throwable $e) {
            $error = $e->getMessage();
            $this->logger->error('Report generation failed: ' . $error, 'report');

            set_transient('jra_merchant_report_data', [
                'generated_at'  => current_time('mysql', true),
                'status'        => 'failed',
                'error_message' => $error,
                'pages_scanned' => 0,
                'total_issues'  => 0,
                'issues'        => [],
            ], 3600); // 1 hour
        }
    }

    /**
     * Get the current report data from transient.
     */
    public function getReportData(): ?array {
        $data = get_transient('jra_merchant_report_data');
        return $data === false ? null : $data;
    }

    /**
     * Check if a report is currently running.
     */
    public function isReportRunning(): bool {
        $data = $this->getReportData();
        return $data && ($data['status'] === 'running' || $data['status'] === 'generating');
    }

    /**
     * Export report as CSV string.
     */
    public function exportCSV(?string $issueFilter = null): string {
        $data = $this->getReportData();
        if (!$data) {
            return '';
        }

        $csv = "Offer ID,Title,Issue Code,Issue Description,Issue Detail,Attribute,Severity,Applicable Countries\n";

        foreach ($data['issues'] as $code => $issueData) {
            if ($issueFilter && $code !== $issueFilter) {
                continue;
            }

            foreach ($issueData['products'] as $product) {
                $row = [
                    $product['offer_id'],
                    $product['title'],
                    $code,
                    $issueData['description'],
                    $product['detail'] ?? '',
                    $product['attribute'] ?? '',
                    $issueData['severity'],
                    implode('; ', $product['countries']),
                ];

                $csv .= '"' . implode('","', array_map(fn($v) => str_replace('"', '""', $v), $row)) . "\"\n";
            }
        }

        return $csv;
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    private function resolveWordPressId(string $offerId): int {
        $localId = $this->reverseOfferIdMap[$offerId] ?? $offerId;

        $rawId = null;

        if (str_starts_with($localId, 'PID-')) {
            $rawId = (int) substr($localId, 4);
        } elseif (str_starts_with($localId, 'SKU-')) {
            $sku   = substr($localId, 4);
            $found = wc_get_product_id_by_sku($sku);

            if ($found) {
                $rawId = (int) $found;
            } elseif (is_numeric($sku)) {
                // Products with no SKU get offer ID = SKU-{post_id}; verify the post exists
                $postId = (int) $sku;
                $post   = get_post($postId);
                if ($post && in_array($post->post_type, ['product', 'product_variation'], true)) {
                    $rawId = $postId;
                }
            }
        }

        if (!$rawId) {
            return 0;
        }

        // Variations have no standalone edit screen — return the parent product ID instead
        $post = get_post($rawId);
        if ($post && $post->post_type === 'product_variation') {
            return (int) $post->post_parent;
        }

        return $rawId;
    }
}
