<?php

declare(strict_types=1);

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Api\ProductApiInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SortOrderBuilder;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Psr\Log\LoggerInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Webapi\Rest\Response;
use WriteTextAI\WriteTextAI\Helper\Data as HelperData;
use WriteTextAI\WriteTextAI\Helper\Fields as FieldHelper;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
use Magento\Framework\UrlInterface;

class ProductApi implements ProductApiInterface
{
    /**
     * @var ProductRepositoryInterface
     */
    private $productRepository;

    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var FilterBuilder
     */
    private $filterBuilder;

    /**
     * @var SortOrderBuilder
     */
    private $sortOrderBuilder;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var Response
     */
    private $response;

    /**
     * @var HelperData
     */
    private $helperData;

    /**
     * @var FieldHelper
     */
    private $fieldHelper;

    /**
     * @var StoreHelper
     */
    private $storeHelper;

    /**
     * @var ProductCollectionFactory
     */
    private $productCollectionFactory;

    /**
     * @var UrlRewriteCollectionFactory
     */
    private $urlRewriteCollectionFactory;

    /**
     * ProductApi constructor
     *
     * @param ProductRepositoryInterface $productRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param FilterBuilder $filterBuilder
     * @param SortOrderBuilder $sortOrderBuilder
     * @param StoreManagerInterface $storeManager
     * @param LoggerInterface $logger
     * @param Response $response
     * @param FieldHelper $fieldHelper
     * @param HelperData $helperData
     * @param StoreHelper $storeHelper
     * @param ProductCollectionFactory $productCollectionFactory
     * @param UrlRewriteCollectionFactory $urlRewriteCollectionFactory
     */
    public function __construct(
        ProductRepositoryInterface $productRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        FilterBuilder $filterBuilder,
        SortOrderBuilder $sortOrderBuilder,
        StoreManagerInterface $storeManager,
        LoggerInterface $logger,
        Response $response,
        FieldHelper $fieldHelper,
        HelperData $helperData,
        StoreHelper $storeHelper,
        ProductCollectionFactory $productCollectionFactory,
        UrlRewriteCollectionFactory $urlRewriteCollectionFactory
    ) {
        $this->productRepository = $productRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->filterBuilder = $filterBuilder;
        $this->sortOrderBuilder = $sortOrderBuilder;
        $this->storeManager = $storeManager;
        $this->logger = $logger;
        $this->response = $response;
        $this->fieldHelper = $fieldHelper;
        $this->helperData = $helperData;
        $this->storeHelper = $storeHelper;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->urlRewriteCollectionFactory = $urlRewriteCollectionFactory;
    }

    /**
     * Get products
     *
     * @param int $page
     * @param int $pageSize
     * @param string $fromDate
     * @param string $language
     * @param int $storeId
     * @return \Magento\Framework\DataObject
     */
    public function getProducts(
        $page = 1,
        $pageSize = 50,
        $fromDate = null,
        $language = null,
        $storeId = null
    ) {
        try {
            // Validate inputs
            $page = max(1, $page);
            $pageSize = min(max(1, $pageSize), 100); // Limit max page size to 100

            if ($storeId !== null && $language !== null && (string)$language !== '') {
                $this->validateLanguageMatchesStoreId($language, (int)$storeId);
            }

            if ($storeId === null && $language !== null && (string)$language !== '') {
                $storeId = $this->getStoreIdByLanguage($language);
            }

            $effectiveStoreId = (int)($storeId ?? $this->storeManager->getDefaultStoreView()->getId());

            // Validate store if provided
            if ($storeId !== null) {
                try {
                    $this->storeManager->getStore($storeId);
                } catch (NoSuchEntityException $e) {
                    return $this->createErrorResponse('Invalid store ID provided', 400);
                }
            }

            // Build search criteria
            $this->searchCriteriaBuilder->setPageSize($pageSize);
            $this->searchCriteriaBuilder->setCurrentPage($page);

            // Add store filter if provided
            if ($storeId !== null) {
                $storeFilter = $this->filterBuilder
                    ->setField('store_id')
                    ->setValue($storeId)
                    ->setConditionType('eq')
                    ->create();
                $this->searchCriteriaBuilder->addFilters([$storeFilter]);
            }

            // Add date filter if provided
            if ($fromDate !== null && !empty($fromDate)) {
                // Convert ISO date to MySQL format for proper filtering
                $mysqlDate = $this->convertIsoToMysqlDate($fromDate);
                $dateFilter = $this->filterBuilder
                    ->setField('updated_at')
                    ->setValue($mysqlDate)
                    ->setConditionType('gt')
                    ->create();
                $this->searchCriteriaBuilder->addFilters([$dateFilter]);
            }

            // Add sort order by updated_at
            $sortOrder = $this->sortOrderBuilder
                ->setField('updated_at')
                ->setDirection(\Magento\Framework\Api\SortOrder::SORT_ASC)
                ->create();
            $this->searchCriteriaBuilder->setSortOrders([$sortOrder]);

            $searchCriteria = $this->searchCriteriaBuilder->create();
            $searchResults = $this->productRepository->getList($searchCriteria);
            // Build response data
            $products = [];
            foreach ($searchResults->getItems() as $product) {
                $customOpenGraph = $this->helperData->getCustomOpenGraph();
                $pageTitle = $this->fieldHelper->getAttributesValues($product, 'page_title');
                $pageDescription = $this->fieldHelper->getAttributesValues($product, 'page_description');
                $productDescription = $this->fieldHelper->getAttributesValues($product, 'product_description');
                $shortProductDescription = $this->fieldHelper->getAttributesValues(
                    $product,
                    'short_product_description'
                );
                $openGraph = $this->fieldHelper->getAttributesValues($product, 'open_graph');
                $productUrl = $this->getProductUrl($product->getId(), $effectiveStoreId);
                if (empty($productUrl)) {
                    $productUrl = $product->setStoreId($effectiveStoreId)->getUrlInStore();
                }
                $products[] = [
                    'recordId' => (string)$product->getId(),
                    'language' => $this->getFormattedLanguage($effectiveStoreId),
                    'storeId' => (string)$effectiveStoreId,
                    'name' => $product->getName(),
                    'pageTitle' => $pageTitle ?: '',
                    'pageDescription' => $pageDescription ?: '',
                    'excerpt' => $shortProductDescription ?: '',
                    'openGraphText' => $customOpenGraph ? $openGraph : '',
                    'productDescription' => $productDescription ?: '',
                    'sku' => $product->getSku(),
                    'url' => $productUrl ?: '',
                    'categoryRecordIds' => $this->getCategoryIds($product),
                    'date' => $this->formatMysqlToIso8601($product->getUpdatedAt())
                ];
            }

            $totalCount = $searchResults->getTotalCount();
            $pageCount = (int)ceil($totalCount / $pageSize);

            $responseData = [
                'data' => $products,
                'pageCount' => $pageCount
            ];

            $this->response->setHeader('Content-Type', 'application/json; charset=utf-8');
            return $this->response->setBody(json_encode($responseData))->sendResponse();
        } catch (\InvalidArgumentException $e) {
            return $this->createErrorResponse($e->getMessage(), 400);
        } catch (LocalizedException $e) {
            $this->logger->error('ProductApi LocalizedException: ' . $e->getMessage());
            return $this->createErrorResponse($e->getMessage(), 500);
        } catch (\Exception $e) {
            $this->logger->error('ProductApi Exception: ' . $e->getMessage());
            return $this->createErrorResponse('Internal server error', 500);
        }
    }

    /**
     * Validate ISO 8601 date format
     *
     * @param string $date
     * @return bool
     */
    private function isValidIsoDate(string $date): bool
    {
        // Validate format: yyyy-MM-ddTHH:mm:ss.sssZ
        $pattern = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/';
        if (!preg_match($pattern, $date)) {
            return false;
        }

        // Additional validation using DateTime
        try {
            $dateTime = new \DateTime($date);
            return $dateTime !== false;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Convert ISO date to MySQL format
     *
     * @param string $isoDate
     * @return string
     */
    private function convertIsoToMysqlDate(string $isoDate): string
    {
        try {
            $dateTime = new \DateTime($isoDate);
            return $dateTime->format('Y-m-d H:i:s');
        } catch (\Exception $e) {
            return $isoDate; // fallback to original if conversion fails
        }
    }

    /**
     * Convert MySQL datetime (Y-m-d H:i:s) to ISO 8601 UTC format with milliseconds.
     *
     * @param string|null $mysqlDate
     * @return string
     */
    private function formatMysqlToIso8601(?string $mysqlDate): string
    {
        if ($mysqlDate === null || $mysqlDate === '') {
            return '';
        }

        try {
            $utc = new \DateTimeZone('UTC');
            $dateTime = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $mysqlDate, $utc);

            if ($dateTime === false) {
                $dateTime = new \DateTimeImmutable($mysqlDate, $utc);
            }

            return $dateTime
                ->setTimezone($utc)
                ->format('Y-m-d\\TH:i:s.v\\Z');
        } catch (\Exception $e) {
            return $mysqlDate;
        }
    }

    /**
     * Get store language/locale
     *
     * @param int|null $storeId
     * @return string
     */
    private function getStoreLanguage(?int $storeId): string
    {
        try {
            if ($storeId !== null) {
                $store = $this->storeManager->getStore($storeId);
            } else {
                $store = $this->storeManager->getDefaultStoreView();
            }
            return $store->getConfig('general/locale/code') ?: 'en_US';
        } catch (\Exception $e) {
            return 'en_US';
        }
    }

    /**
     * Get formatted language
     *
     * @param int $storeId
     * @return string
     */
    public function getFormattedLanguage($storeId)
    {
        $language = $this->getStoreLanguage($storeId);
        $language = str_replace('_', '-', $language);

        return $language;
    }

    /**
     * Get store id by formatted language (e.g. en-US or en_US)
     *
     * @param string $language
     * @return int
     */
    private function getStoreIdByLanguage($language): int
    {
        $language = (string)$language;
        $normalizedLanguage = strtolower(str_replace('_', '-', $language));

        foreach ($this->storeManager->getStores(true) as $store) {
            $storeLanguage = strtolower($this->storeHelper->getFormattedLanguage((int)$store->getId()));
            if ($storeLanguage === $normalizedLanguage) {
                return (int)$store->getId();
            }
        }

        throw new \InvalidArgumentException('Invalid language provided ' . $language);
    }

    /**
     * Validate that a provided language matches the locale of a given store ID.
     *
     * @param mixed $language
     * @param int $storeId
     * @return void
     */
    private function validateLanguageMatchesStoreId($language, int $storeId): void
    {
        $requestedLanguage = strtolower(str_replace('_', '-', (string)$language));
        $storeLanguage = strtolower($this->storeHelper->getFormattedLanguage($storeId));

        if ($requestedLanguage !== $storeLanguage) {
            throw new \InvalidArgumentException('Language does not match storeId');
        }
    }
    
    /**
     * Get category IDs for product
     *
     * @param \Magento\Catalog\Api\Data\ProductInterface $product
     * @return array
     */
    private function getCategoryIds(\Magento\Catalog\Api\Data\ProductInterface $product): array
    {
        $categoryIds = $product->getCategoryIds();
        return array_map('strval', $categoryIds ?: []);
    }

    /**
     * Create error response
     *
     * @param string $message
     * @param int $code
     * @return \Magento\Framework\DataObject
     */
    private function createErrorResponse(string $message, int $code)
    {
        $errorData = [
            'data' => [],
            'pageCount' => 0,
            'error' => [
                'message' => $message,
                'code' => (string)$code
            ]
        ];

        //$this->response->setHeader('Content-Type', 'application/json');
        return $this->response
            ->setHttpResponseCode($code)
            ->setBody(json_encode($errorData))
            ->sendResponse();
    }

    /**
     * Get product URL
     *
     * @param String $productId
     * @param int $storeId
     * @return string
     */
    private function getProductUrl(String $productId, int $storeId): string
    {
        try {
            // Use Magento's UrlRewriteCollectionFactory for proper ORM approach
            $urlRewriteCollection = $this->urlRewriteCollectionFactory->create();
            $urlRewriteCollection
                ->addFieldToFilter('entity_type', 'product')
                ->addFieldToFilter('entity_id', $productId)
                ->addFieldToFilter('store_id', $storeId)
                ->addFieldToFilter('redirect_type', 0) // Only get direct URLs, not redirects
                ->setOrder('url_rewrite_id', 'DESC') // Get the latest rewrite
                ->setPageSize(1)
                ->setCurPage(1);
            
            $urlRewrite = $urlRewriteCollection->getFirstItem();
            $requestPath = $urlRewrite->getRequestPath();
            
            if (!$requestPath) {
                // Fallback: try with store_id = 0 (default/global store view)
                $urlRewriteCollection = $this->urlRewriteCollectionFactory->create();
                $urlRewriteCollection
                    ->addFieldToFilter('entity_type', 'product')
                    ->addFieldToFilter('entity_id', $productId)
                    ->addFieldToFilter('store_id', 0)
                    ->addFieldToFilter('redirect_type', 0)
                    ->setOrder('url_rewrite_id', 'DESC')
                    ->setPageSize(1)
                    ->setCurPage(1);
                
                $urlRewrite = $urlRewriteCollection->getFirstItem();
                $requestPath = $urlRewrite->getRequestPath();
            }
            
            if (!$requestPath) {
                $this->logger->warning(
                    'WriteTextAI ProductApi: No URL rewrite found for product ID: '
                    . $productId
                    . ', store ID: '
                    . $storeId
                );
                return '';
            }
            
            // Get the store base URL
            try {
                $store = $this->storeManager->getStore($storeId);
                $baseUrl = $store->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_WEB);
                
                // Remove trailing slash from base URL if present
                $baseUrl = rtrim($baseUrl, '/');
                
                // Form the complete URL
                $fullUrl = $baseUrl . '/' . $requestPath;
                
                return $fullUrl;
            } catch (NoSuchEntityException $e) {
                $this->logger->warning(
                    'WriteTextAI ProductApi: Invalid store ID: ' . $storeId . ' for product ID: ' . $productId
                );
                return '';
            }
        } catch (\Exception $e) {
            $this->logger->warning(
                'WriteTextAI ProductApi: Error getting product URL for product ID: ' . $product->getId() .
                ', store ID: ' . $storeId . ': ' . $e->getMessage()
            );
            return '';
        }
    }
}
