<?php

declare(strict_types=1);

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Api\CategoryApiInterface;
use Magento\Catalog\Api\CategoryRepositoryInterface;
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\Category\CollectionFactory as CategoryCollectionFactory;
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 WriteTextAI\WriteTextAI\Helper\Categories\Fields as CategoryFieldsHelper;
use Magento\Catalog\Helper\Output as OutputHelper;
use Magento\Framework\UrlInterface;
use Magento\UrlRewrite\Model\UrlFinderInterface;
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteData;

class CategoryApi implements CategoryApiInterface
{
    /**
     * @var CategoryRepositoryInterface
     */
    private $categoryRepository;

    /**
     * @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 CategoryCollectionFactory
     */
    private $categoryCollectionFactory;

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

    /**
     * @var OutputHelper
     */
    private $outputHelper;

    /**
     * @var CategoryFieldsHelper
     */
    private $categoryFieldsHelper;

    /**
     * @var UrlFinderInterface
     */
    private $urlFinder;

    /**
     * CategoryApi constructor
     *
     * @param CategoryRepositoryInterface $categoryRepository
     * @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 CategoryCollectionFactory $categoryCollectionFactory
     * @param UrlRewriteCollectionFactory $urlRewriteCollectionFactory
     * @param OutputHelper $outputHelper
     * @param CategoryFieldsHelper $categoryFieldsHelper
     * @param UrlFinderInterface $urlFinder
     */
    public function __construct(
        CategoryRepositoryInterface $categoryRepository,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        FilterBuilder $filterBuilder,
        SortOrderBuilder $sortOrderBuilder,
        StoreManagerInterface $storeManager,
        LoggerInterface $logger,
        Response $response,
        FieldHelper $fieldHelper,
        HelperData $helperData,
        StoreHelper $storeHelper,
        CategoryCollectionFactory $categoryCollectionFactory,
        UrlRewriteCollectionFactory $urlRewriteCollectionFactory,
        OutputHelper $outputHelper,
        CategoryFieldsHelper $categoryFieldsHelper,
        UrlFinderInterface $urlFinder
    ) {
        $this->categoryRepository = $categoryRepository;
        $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->categoryCollectionFactory = $categoryCollectionFactory;
        $this->urlRewriteCollectionFactory = $urlRewriteCollectionFactory;
        $this->outputHelper = $outputHelper;
        $this->categoryFieldsHelper = $categoryFieldsHelper;
        $this->urlFinder = $urlFinder;
    }

    /**
     * Get categories
     * 
     * @param int $page
     * @param int $pageSize
     * @param string $fromDate
     * @param string $language
     * @param int $storeId
     * @return \Magento\Framework\DataObject
     */
    public function getCategories(
        $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);
                }
            }

            $mappingSettings = $this->categoryFieldsHelper->getMappingSettings();

            // Use category collection for better control over categories
            $collection = $this->categoryCollectionFactory->create();
            $collection->addAttributeToSelect('*')
                      ->setPageSize($pageSize)
                      ->setCurPage($page)
                      ->addIsActiveFilter();

            // Add store filter if provided
            if ($storeId !== null) {
                $collection->setStoreId($storeId);
            }

            // Add date filter if provided
            if ($fromDate !== null) {
                // if (!$this->isValidIsoDate($fromDate)) {
                //     return $this->createErrorResponse('Invalid fromDate format. Expected: yyyy-MM-ddTHH:mm:ss.sssZ', 400);
                // }

                // Convert ISO date to MySQL format for proper filtering
                $mysqlDate = $this->convertIsoToMysqlDate($fromDate);
                $collection->addAttributeToFilter('updated_at', ['gt' => $mysqlDate]);
            }

            // Add sort order by updated_at
            $collection->addOrder('updated_at', 'ASC');

            // Exclude root category (ID 1) and default category (ID 2)
            $collection->addFieldToFilter('entity_id', ['gt' => 2]);

            // Build response data
            $categories = [];
            foreach ($collection as $category) {
                $categoryDescription = $this->outputHelper->categoryAttribute(
                    $category,
                    $category->getData($mappingSettings['category_description'] ?? 'description'),
                    $mappingSettings['category_description'] ?? 'description'
                );
                $categoryUrl = $this->getUrlByCategoryId($category, $category->getId(), $effectiveStoreId);
                
                if (empty($categoryUrl)) {
                    $categoryUrl = $category->setStoreId($effectiveStoreId)->getUrl();
                }

                $categories[] = [
                    'recordId' => (string)$category->getId(),
                    'language' => $this->getFormattedLanguage($effectiveStoreId),
                    'storeId' => (string)$effectiveStoreId,
                    'name' => $category->getName(),
                    'pageTitle' => $category->getMetaTitle() ?: '',
                    'pageDescription' => (string)$category->getMetaDescription() ?: '',
                    'categoryDescription' => $categoryDescription ?: '',
                    'url' => $categoryUrl ?: '',
                    'date' => $this->formatMysqlToIso8601($category->getUpdatedAt())
                ];
            }

            $totalCount = $collection->getSize();
            $pageCount = (int)ceil($totalCount / $pageSize);

            $responseData = [
                'data' => $categories,
                '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('CategoryApi LocalizedException: ' . $e->getMessage());
            return $this->createErrorResponse($e->getMessage(), 500);
        } catch (\Exception $e) {
            $this->logger->error('CategoryApi 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');
    }

    /**
     * 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');
        }
    }

    /**
     * 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
            ]
        ];

        return $this->response
            ->setHttpResponseCode($code)
            ->setBody(json_encode($errorData))
            ->sendResponse();
    }

    

    /**
     * Get category url
     *
     * @param \Magento\Catalog\Model\Category $category
     * @param int $categoryId
     * @param int $storeId
     * @return string
     */
    public function getUrlByCategoryId($category, $categoryId, $storeId)
    {
        $rewrite = $this->urlFinder->findOneByData([
            UrlRewriteData::ENTITY_ID => $categoryId,
            UrlRewriteData::ENTITY_TYPE => 'category',
            UrlRewriteData::STORE_ID => $storeId
        ]);
        
        $category->getUrlInstance()->setScope($storeId);
        
        $targetPath = (!$rewrite) ? $category->getUrl() :
            $this->storeManager->getStore($storeId)->getBaseUrl() . $rewrite->getRequestPath();

        return $targetPath;
    }
}
