<?php

declare(strict_types=1);

namespace App\Module\Agency\Service;

use App\Entity\Tenant\Category;
use App\Entity\Tenant\News;
use App\Entity\Tenant\NewsAgencyConfig;
use App\Entity\Tenant\NewsAgencyItem;
use App\Module\Agency\Service\Adapter\AgencyAdapterInterface;
use App\Module\Seo\Service\SeoScorer;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\String\Slugger\SluggerInterface;

class AgencyManager
{
    /** @var AgencyAdapterInterface[] */
    private array $adapters = [];

    /**
     * @param iterable<AgencyAdapterInterface> $agencyAdapters
     */
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly SluggerInterface $slugger,
        private readonly SeoScorer $seoScorer,
        private readonly string $projectDir,
        iterable $agencyAdapters,
    ) {
        foreach ($agencyAdapters as $adapter) {
            $this->adapters[$adapter->getSlug()] = $adapter;
        }
    }

    /**
     * Tum mevcut ajans adaptorlerini dondurur.
     *
     * @return AgencyAdapterInterface[]
     */
    public function getAvailableAdapters(): array
    {
        return $this->adapters;
    }

    /**
     * Belirli bir adaptor dondurur.
     */
    public function getAdapter(string $slug): ?AgencyAdapterInterface
    {
        return $this->adapters[$slug] ?? null;
    }

    /**
     * Tum ajans konfigurasyonlarini veritabanindan alir.
     * Eger veritabaninda yoksa, mevcut adaptorler icin varsayilan kayitlar olusturur.
     *
     * @return NewsAgencyConfig[]
     */
    public function getAgencyConfigs(): array
    {
        $repo = $this->entityManager->getRepository(NewsAgencyConfig::class);
        $configs = $repo->findAll();

        $existingSlugs = [];
        foreach ($configs as $config) {
            $existingSlugs[$config->getAgencySlug()] = true;
        }

        // Veritabaninda olmayan adaptorler icin varsayilan kayit olustur
        foreach ($this->adapters as $slug => $adapter) {
            if (!isset($existingSlugs[$slug])) {
                $newConfig = new NewsAgencyConfig();
                $newConfig->setAgencySlug($slug);
                $newConfig->setAgencyName($adapter->getName());
                $this->entityManager->persist($newConfig);
                $configs[] = $newConfig;
            }
        }

        if (count($existingSlugs) < count($this->adapters)) {
            $this->entityManager->flush();
        }

        return $configs;
    }

    /**
     * Belirli bir ajans konfigurasyonunu slug ile alir.
     */
    public function getAgencyConfig(string $slug): ?NewsAgencyConfig
    {
        return $this->entityManager->getRepository(NewsAgencyConfig::class)
            ->findOneBy(['agencySlug' => $slug]);
    }

    /**
     * Ajans konfigurasyonunu ID ile alir.
     */
    public function getAgencyConfigById(int $id): ?NewsAgencyConfig
    {
        return $this->entityManager->getRepository(NewsAgencyConfig::class)->find($id);
    }

    /**
     * Ajans konfigurasyonunu kaydeder.
     */
    public function saveConfig(NewsAgencyConfig $config): void
    {
        $this->entityManager->persist($config);
        $this->entityManager->flush();
    }

    /**
     * Ajans baglantisini test eder.
     */
    public function testConnection(NewsAgencyConfig $config): bool
    {
        $adapter = $this->getAdapter($config->getAgencySlug());
        if (!$adapter) {
            return false;
        }

        $adapter->configure([
            'api_url' => $config->getApiUrl(),
            'api_key' => $config->getApiKey(),
            'api_secret' => $config->getApiSecret(),
            'config' => $config->getConfig() ?? [],
        ]);

        return $adapter->testConnection();
    }

    /**
     * Belirli bir ajanstan haberleri ceker.
     *
     * @return NewsAgencyItem[]
     */
    public function fetchNews(NewsAgencyConfig $config): array
    {
        $adapter = $this->getAdapter($config->getAgencySlug());
        if (!$adapter) {
            return [];
        }

        $adapter->configure([
            'api_url' => $config->getApiUrl(),
            'api_key' => $config->getApiKey(),
            'api_secret' => $config->getApiSecret(),
            'config' => $config->getConfig() ?? [],
        ]);

        $since = $config->getLastFetchedAt() ?? new \DateTimeImmutable('-1 day');
        $rawItems = $adapter->fetchLatest($since);

        $items = [];
        $seenExternalIds = [];
        foreach ($rawItems as $rawItem) {
            $externalId = $rawItem['external_id'] ?? '';

            // Ayni batch icinde duplicate kontrolu
            if (isset($seenExternalIds[$externalId])) {
                continue;
            }

            // Daha once cekilmis mi kontrol et
            $existing = $this->entityManager->getRepository(NewsAgencyItem::class)
                ->findOneBy([
                    'agencySlug' => $config->getAgencySlug(),
                    'externalId' => $externalId,
                ]);

            if ($existing) {
                $seenExternalIds[$externalId] = true;
                continue;
            }

            $item = new NewsAgencyItem();
            $item->setAgencySlug($config->getAgencySlug());
            $item->setExternalId($externalId ?: uniqid());
            $item->setTitle($rawItem['title'] ?? 'Baslıksız');
            $item->setContent($rawItem['content'] ?? null);
            $item->setSummary($rawItem['summary'] ?? null);
            $item->setCategory($rawItem['category'] ?? null);
            $item->setImages($rawItem['images'] ?? null);
            $item->setReceivedAt(new \DateTimeImmutable());
            $item->setStatus('pending');

            $this->entityManager->persist($item);
            $items[] = $item;
            $seenExternalIds[$externalId] = true;
        }

        // Son cekme zamanini guncelle
        $config->setLastFetchedAt(new \DateTimeImmutable());

        try {
            $this->entityManager->flush();
        } catch (\Throwable) {
            // Duplicate varsa entity manager'i resetle ve tekrar dene
            $this->entityManager->clear();
            $config = $this->getAgencyConfig($config->getAgencySlug());
            if ($config) {
                $config->setLastFetchedAt(new \DateTimeImmutable());
                $this->entityManager->flush();
            }
            return [];
        }

        // Otomatik yayinlama aktifse haberleri import et
        if ($config->isAutoPublish()) {
            foreach ($items as $item) {
                $this->importItem($item, $config);
            }
        }

        return $items;
    }

    /**
     * Ajans haberini sisteme import eder (haber olarak kaydeder).
     */
    public function importItem(NewsAgencyItem $item, ?NewsAgencyConfig $config = null): ?News
    {
        if ($item->getStatus() === 'imported') {
            return null;
        }

        if (!$config) {
            $config = $this->getAgencyConfig($item->getAgencySlug());
        }

        $news = new News();
        $news->setTitle($item->getTitle());
        $slug = (string) $this->slugger->slug($item->getTitle())->lower();
        $news->setSlug($slug);
        $news->setContent($item->getContent());
        $news->setSpot($item->getSummary());
        $news->setAgencySource($item->getAgencySlug());
        $news->setAgencyItemId($item->getExternalId());
        $news->setStatus('published');
        $news->setPublishedAt(new \DateTimeImmutable());

        // 1) Kategori eslestirme: item category string -> Category entity
        $category = $this->matchOrCreateCategory($item->getCategory(), $config);
        if ($category) {
            $news->setCategory($category);
            $category->setNewsCount(($category->getNewsCount() ?? 0) + 1);
        }

        // 2) Gorseli yerel dizine indir
        $images = $item->getImages();
        if (!empty($images) && isset($images[0])) {
            $localPath = $this->downloadImage($images[0], $slug);
            $news->setFeaturedImage($localPath);
        }

        // 3) SEO alanlarini otomatik doldur
        $this->fillSeoFields($news, $category);

        $this->entityManager->persist($news);

        // Item durumunu guncelle
        $item->setStatus('imported');
        $item->setImportedNewsId($news->getId());

        $this->entityManager->flush();

        // Import sonrasi ID'yi guncelle
        $item->setImportedNewsId($news->getId());
        $this->entityManager->flush();

        return $news;
    }

    /**
     * Ajans haberinin kategori stringini mevcut kategorilerle eslestirir.
     * Eslesen yoksa yeni kategori olusturur.
     */
    private function matchOrCreateCategory(?string $categoryName, ?NewsAgencyConfig $config): ?Category
    {
        if (!$categoryName || trim($categoryName) === '') {
            // Kategori bilgisi yoksa varsayilan kategoriyi kullan
            if ($config && $config->getDefaultCategoryId()) {
                return $this->entityManager->getRepository(Category::class)
                    ->find($config->getDefaultCategoryId());
            }
            return null;
        }

        $categoryName = trim($categoryName);
        $categoryRepo = $this->entityManager->getRepository(Category::class);

        // Oncelikle title ile exact match ara
        $category = $categoryRepo->findOneBy(['title' => $categoryName]);
        if ($category) {
            return $category;
        }

        // Slug ile ara
        $categorySlug = (string) $this->slugger->slug($categoryName)->lower();
        $category = $categoryRepo->findOneBy(['slug' => $categorySlug]);
        if ($category) {
            return $category;
        }

        // Case-insensitive arama (LIKE)
        $category = $categoryRepo->createQueryBuilder('c')
            ->where('LOWER(c.title) = LOWER(:title)')
            ->setParameter('title', $categoryName)
            ->setMaxResults(1)
            ->getQuery()
            ->getOneOrNullResult();
        if ($category) {
            return $category;
        }

        // Bulunamadi, yeni kategori olustur
        $maxPosition = (int) $categoryRepo->createQueryBuilder('c')
            ->select('MAX(c.position)')
            ->getQuery()
            ->getSingleScalarResult();

        $newCategory = new Category();
        $newCategory->setTitle($categoryName);
        $newCategory->setSlug($categorySlug);
        $newCategory->setStatus('active');
        $newCategory->setPosition($maxPosition + 1);
        $this->entityManager->persist($newCategory);

        return $newCategory;
    }

    /**
     * Gorsel URL'sini indirir ve yerel dizine kaydeder.
     * Basarisiz olursa orijinal URL'yi dondurur.
     */
    private function downloadImage(string $imageUrl, string $slug): string
    {
        try {
            $uploadDir = $this->projectDir . '/public/uploads/agency';
            if (!is_dir($uploadDir)) {
                mkdir($uploadDir, 0755, true);
            }

            // Dosya uzantisini belirle
            $urlPath = parse_url($imageUrl, PHP_URL_PATH);
            $ext = pathinfo($urlPath ?? '', PATHINFO_EXTENSION);
            if (!in_array(strtolower($ext), ['jpg', 'jpeg', 'png', 'webp', 'gif'], true)) {
                $ext = 'jpg';
            }

            $filename = time() . '_' . mb_substr($slug, 0, 50) . '.' . $ext;
            $filepath = $uploadDir . '/' . $filename;

            // Gorseli curl ile indir
            $ch = curl_init($imageUrl);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
            curl_setopt($ch, CURLOPT_TIMEOUT, 15);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; HaberCMS/1.0)');
            $imageData = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($imageData === false || $httpCode !== 200 || strlen($imageData) < 100) {
                return $imageUrl; // Indirme basarisiz, orijinal URL'yi kullan
            }

            file_put_contents($filepath, $imageData);

            return '/uploads/agency/' . $filename;
        } catch (\Throwable $e) {
            error_log('AgencyManager downloadImage error: ' . $e->getMessage());
            return $imageUrl; // Hata durumunda orijinal URL'yi kullan
        }
    }

    /**
     * SEO alanlarini otomatik doldurur ve skor hesaplar.
     */
    private function fillSeoFields(News $news, ?Category $category): void
    {
        $title = $news->getTitle() ?? '';
        $content = $news->getContent() ?? '';
        $spot = $news->getSpot() ?? '';

        // SEO Basligi: Haber basligindan (max 60 karakter)
        $seoTitle = mb_strlen($title) > 60 ? mb_substr($title, 0, 57) . '...' : $title;
        $news->setSeoTitle($seoTitle);

        // SEO Aciklamasi: spot/summary (max 160 karakter), yoksa content'in basi
        $descSource = $spot !== '' ? $spot : strip_tags($content);
        $seoDesc = mb_strlen($descSource) > 160 ? mb_substr($descSource, 0, 157) . '...' : $descSource;
        $news->setSeoDescription($seoDesc);

        // Odak Anahtar Kelime: Kategori adi
        $focusKeyword = $category ? $category->getTitle() : '';
        $news->setFocusKeyword($focusKeyword);

        // Robots Meta
        $news->setRobotsMeta('index, follow');

        // SEO Skoru hesapla
        $seoResult = $this->seoScorer->analyze(
            $title,
            $content,
            $seoTitle,
            $seoDesc,
            $focusKeyword,
            $news->getSlug() ?? '',
        );
        $news->setSeoScore($seoResult->score);
    }

    /**
     * Bekleyen ajans haberlerini getirir.
     *
     * @return NewsAgencyItem[]
     */
    public function getPendingItems(?string $agencySlug = null, int $limit = 50): array
    {
        $qb = $this->entityManager->getRepository(NewsAgencyItem::class)
            ->createQueryBuilder('i')
            ->where('i.status = :status')
            ->setParameter('status', 'pending')
            ->orderBy('i.receivedAt', 'DESC')
            ->setMaxResults($limit);

        if ($agencySlug) {
            $qb->andWhere('i.agencySlug = :slug')
                ->setParameter('slug', $agencySlug);
        }

        return $qb->getQuery()->getResult();
    }

    /**
     * Ajans haberlerini getirir (tum durumlar).
     *
     * @return NewsAgencyItem[]
     */
    public function getItems(?string $agencySlug = null, ?string $status = null, int $limit = 50, int $offset = 0): array
    {
        $qb = $this->entityManager->getRepository(NewsAgencyItem::class)
            ->createQueryBuilder('i')
            ->orderBy('i.receivedAt', 'DESC')
            ->setMaxResults($limit)
            ->setFirstResult($offset);

        if ($agencySlug) {
            $qb->andWhere('i.agencySlug = :slug')
                ->setParameter('slug', $agencySlug);
        }

        if ($status) {
            $qb->andWhere('i.status = :status')
                ->setParameter('status', $status);
        }

        return $qb->getQuery()->getResult();
    }

    /**
     * Ajans haberi sayisini getirir.
     */
    public function getItemCount(?string $agencySlug = null, ?string $status = null): int
    {
        $qb = $this->entityManager->getRepository(NewsAgencyItem::class)
            ->createQueryBuilder('i')
            ->select('COUNT(i.id)');

        if ($agencySlug) {
            $qb->andWhere('i.agencySlug = :slug')
                ->setParameter('slug', $agencySlug);
        }

        if ($status) {
            $qb->andWhere('i.status = :status')
                ->setParameter('status', $status);
        }

        return (int) $qb->getQuery()->getSingleScalarResult();
    }

    /**
     * Ajans haberini ID ile getirir.
     */
    public function getItemById(int $id): ?NewsAgencyItem
    {
        return $this->entityManager->getRepository(NewsAgencyItem::class)->find($id);
    }

    /**
     * Tum bekleyen ajans haberlerini toplu import eder.
     * Performans icin batch flush kullanir.
     */
    public function importAllPending(string $agencySlug): int
    {
        $config = $this->getAgencyConfig($agencySlug);
        $pendingItems = $this->entityManager->getRepository(NewsAgencyItem::class)
            ->findBy(['agencySlug' => $agencySlug, 'status' => 'pending']);

        $importedCount = 0;
        $batchSize = 30;
        $categoryCache = [];

        foreach ($pendingItems as $item) {
            $news = new News();
            $news->setTitle($item->getTitle());
            $slug = (string) $this->slugger->slug($item->getTitle())->lower() . '-' . uniqid();
            $news->setSlug($slug);
            $news->setContent($item->getContent());
            $news->setSpot($item->getSummary());
            $news->setAgencySource($item->getAgencySlug());
            $news->setAgencyItemId($item->getExternalId());
            $news->setStatus('published');
            $news->setPublishedAt(new \DateTimeImmutable());

            // 1) Kategori eslestirme (cache ile)
            $catName = $item->getCategory() ?? '';
            if (!isset($categoryCache[$catName])) {
                $categoryCache[$catName] = $this->matchOrCreateCategory($catName ?: null, $config);
            }
            $category = $categoryCache[$catName];
            if ($category) {
                $news->setCategory($category);
                $category->setNewsCount(($category->getNewsCount() ?? 0) + 1);
            }

            // 2) Gorseli yerel dizine indir
            $images = $item->getImages();
            if (!empty($images) && isset($images[0])) {
                $localPath = $this->downloadImage($images[0], $slug);
                $news->setFeaturedImage($localPath);
            }

            // 3) SEO alanlarini otomatik doldur
            $this->fillSeoFields($news, $category);

            $this->entityManager->persist($news);
            $item->setStatus('imported');
            ++$importedCount;

            // Batch flush
            if ($importedCount % $batchSize === 0) {
                $this->entityManager->flush();
            }
        }

        // Kalan haberleri flush et
        $this->entityManager->flush();

        return $importedCount;
    }

    /**
     * Ajans haberini atlar (skip).
     */
    public function skipItem(NewsAgencyItem $item): void
    {
        $item->setStatus('skipped');
        $this->entityManager->flush();
    }
}
