<?php

require_once APP_ROOT . '/models/Article.php';
require_once APP_ROOT . '/models/ArticleCategory.php';
require_once APP_ROOT . '/models/ArticleTag.php';
require_once APP_ROOT . '/models/User.php';
require_once APP_ROOT . '/helpers/Slug.php';
require_once APP_ROOT . '/helpers/HtmlSanitizer.php';
require_once APP_ROOT . '/helpers/ArticleImagePipeline.php';
require_once APP_ROOT . '/helpers/ArticleCache.php';
require_once APP_ROOT . '/helpers/FileUpload.php';

class ArticleController
{
    private Article $model;
    private ArticleCategory $catModel;
    private ArticleTag $tagModel;

    public function __construct()
    {
        $this->model = new Article();
        $this->catModel = new ArticleCategory();
        $this->tagModel = new ArticleTag();
    }

    // ==================== PUBLIC ====================

    public function index(): void
    {
        $this->listView();
    }

    public function indexByCategory(string $slug): void
    {
        $category = $this->catModel->findBySlug($slug);
        if (!$category) {
            $this->notFound();
            return;
        }
        $this->listView(['category' => $category]);
    }

    public function indexByTag(string $slug): void
    {
        $tag = $this->tagModel->findBySlug($slug);
        if (!$tag) {
            $this->notFound();
            return;
        }
        $this->listView(['tag' => $tag]);
    }

    private function listView(array $context = []): void
    {
        $page = max(1, (int)($_GET['page'] ?? 1));
        $q = trim((string)($_GET['q'] ?? ''));
        $locale = Lang::getLocale();

        $filters = ['locale' => $locale, 'status' => 'published'];
        if (!empty($context['category'])) $filters['category_id'] = $context['category']['id'];
        if (!empty($context['tag'])) $filters['tag_id'] = $context['tag']['id'];
        if ($q !== '') $filters['q'] = $q;

        // Cache guests only (no CSRF/flash, no search)
        $cacheable = !Session::isLoggedIn() && $q === '' && !Session::hasFlash('success') && !Session::hasFlash('error');
        $cacheKey = [
            'locale' => $locale,
            'page' => $page,
            'cat' => $context['category']['slug'] ?? '',
            'tag' => $context['tag']['slug'] ?? '',
        ];

        $lastMod = $this->model->lastModifiedAll($locale) ?? date('Y-m-d H:i:s');
        $etag = md5('list|' . json_encode($cacheKey) . '|' . $lastMod . '|' . ArticleCache::version());
        if (ArticleCache::sendEtag($etag, $lastMod, 300)) return;

        if ($cacheable) {
            $cached = ArticleCache::get('list', $cacheKey);
            if ($cached !== null) {
                echo $cached;
                return;
            }
        }

        $total = $this->model->countPublished($filters);
        $articles = $this->model->listPublished($filters, $page);
        $perPage = Article::PER_PAGE;
        $totalPages = max(1, (int)ceil($total / $perPage));

        $categories = $this->catModel->withCounts($locale);
        $topTags = $this->tagModel->topTags(20, $locale);

        // SEO
        $baseTitle = Lang::get('articles');
        $pageTitle = $baseTitle;
        $pageDesc = Lang::get('articles_meta_description');
        $canonicalPath = '/articles';
        if (!empty($context['category'])) {
            $pageTitle = $context['category']['name'] . ' · ' . $baseTitle;
            $pageDesc = $context['category']['description'] ?: ($context['category']['name'] . ' — ' . $pageDesc);
            $canonicalPath = '/articles/category/' . $context['category']['slug'];
        } elseif (!empty($context['tag'])) {
            $pageTitle = '#' . $context['tag']['name'] . ' · ' . $baseTitle;
            $canonicalPath = '/articles/tag/' . $context['tag']['slug'];
        }
        if ($q !== '') {
            $pageTitle = $q . ' — ' . $baseTitle;
        }
        $pageTitle .= ' | ' . Lang::get('app_name');
        $pageUrl = APP_URL . $canonicalPath . ($page > 1 ? '?page=' . $page : '');
        $pageImage = DEFAULT_OG_IMAGE;
        $noindex = ($q !== '' || $page > 1);

        $extraJs = [];

        if ($cacheable) ob_start();
        require APP_ROOT . '/views/articles/index.php';
        if ($cacheable) {
            $html = ob_get_clean();
            ArticleCache::put('list', $cacheKey, $html);
            echo $html;
        }
    }

    public function show(string $slug): void
    {
        // Slug history redirect (301)
        $article = $this->model->findPublishedBySlug($slug);
        if (!$article) {
            $historicalId = $this->model->resolveSlugHistory($slug);
            if ($historicalId) {
                $current = $this->model->findById($historicalId);
                if ($current && $current['status'] === 'published') {
                    http_response_code(301);
                    header('Location: ' . APP_URL . '/articles/' . $current['slug']);
                    return;
                }
            }
            $this->notFound();
            return;
        }

        $etag = md5('art|' . $article['id'] . '|' . $article['updated_at'] . '|' . ArticleCache::version());
        if (ArticleCache::sendEtag($etag, $article['updated_at'], 600)) return;

        // Increment views (non-blocking; only for GET and not for bots ideally)
        if (!$this->isBot()) {
            $this->model->incrementViews((int)$article['id']);
        }

        $tags = $this->tagModel->forArticle((int)$article['id']);
        $related = $this->model->related((int)$article['id'], $article['category_id'] ? (int)$article['category_id'] : null, 3);
        $prev = $this->model->prevInCategory((int)$article['id'], $article['category_id'] ? (int)$article['category_id'] : null, $article['published_at']);
        $next = $this->model->nextInCategory((int)$article['id'], $article['category_id'] ? (int)$article['category_id'] : null, $article['published_at']);

        $pageTitle = ($article['meta_title'] ?: $article['title']) . ' | ' . Lang::get('app_name');
        $pageDescription = $article['meta_description'] ?: ($article['excerpt'] ?: HtmlSanitizer::excerpt($article['content'], 160));
        $pageUrl = APP_URL . '/articles/' . $article['slug'];
        $pageImage = $this->imageOgUrl($article) ?: DEFAULT_OG_IMAGE;

        require APP_ROOT . '/views/articles/show.php';
    }

    // ==================== FEEDS & SITEMAP ====================

    public function rss(): void
    {
        header('Content-Type: application/rss+xml; charset=utf-8');
        $locale = Lang::getLocale();
        $articles = $this->model->latestPublished(20, null); // mixed locales
        $lastMod = $this->model->lastModifiedAll() ?? date('Y-m-d H:i:s');

        echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        echo '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/">' . "\n";
        echo '<channel>' . "\n";
        echo '<title>' . htmlspecialchars(Lang::get('app_name') . ' — ' . Lang::get('articles')) . '</title>' . "\n";
        echo '<link>' . htmlspecialchars(APP_URL . '/articles') . '</link>' . "\n";
        echo '<atom:link href="' . htmlspecialchars(APP_URL . '/feed.xml') . '" rel="self" type="application/rss+xml" />' . "\n";
        echo '<description>' . htmlspecialchars(Lang::get('articles_meta_description')) . '</description>' . "\n";
        echo '<language>' . htmlspecialchars($locale) . '</language>' . "\n";
        echo '<lastBuildDate>' . gmdate(DATE_RSS, strtotime($lastMod)) . '</lastBuildDate>' . "\n";
        foreach ($articles as $a) {
            $url = APP_URL . '/articles/' . $a['slug'];
            $pub = gmdate(DATE_RSS, strtotime($a['published_at'] ?: $a['created_at']));
            $img = $this->imageOgUrl($a);
            echo '<item>' . "\n";
            echo '<title>' . htmlspecialchars($a['title']) . '</title>' . "\n";
            echo '<link>' . htmlspecialchars($url) . '</link>' . "\n";
            echo '<guid isPermaLink="true">' . htmlspecialchars($url) . '</guid>' . "\n";
            echo '<pubDate>' . $pub . '</pubDate>' . "\n";
            if (!empty($a['category_name'])) echo '<category>' . htmlspecialchars($a['category_name']) . '</category>' . "\n";
            echo '<description><![CDATA[' . ($a['excerpt'] ?: HtmlSanitizer::excerpt($a['content'], 280)) . ']]></description>' . "\n";
            echo '<content:encoded><![CDATA[' . $a['content'] . ']]></content:encoded>' . "\n";
            if ($img) echo '<media:content url="' . htmlspecialchars($img) . '" medium="image" />' . "\n";
            echo '</item>' . "\n";
        }
        echo '</channel></rss>';
    }

    public function atom(): void
    {
        header('Content-Type: application/atom+xml; charset=utf-8');
        $articles = $this->model->latestPublished(20, null);
        $lastMod = $this->model->lastModifiedAll() ?? date('Y-m-d H:i:s');
        echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        echo '<feed xmlns="http://www.w3.org/2005/Atom">' . "\n";
        echo '<title>' . htmlspecialchars(Lang::get('app_name') . ' — ' . Lang::get('articles')) . '</title>' . "\n";
        echo '<link href="' . htmlspecialchars(APP_URL . '/articles') . '" />' . "\n";
        echo '<link rel="self" href="' . htmlspecialchars(APP_URL . '/articles/feed.atom') . '" />' . "\n";
        echo '<id>' . htmlspecialchars(APP_URL . '/articles') . '</id>' . "\n";
        echo '<updated>' . gmdate(DATE_ATOM, strtotime($lastMod)) . '</updated>' . "\n";
        foreach ($articles as $a) {
            $url = APP_URL . '/articles/' . $a['slug'];
            echo '<entry>' . "\n";
            echo '<title>' . htmlspecialchars($a['title']) . '</title>' . "\n";
            echo '<link href="' . htmlspecialchars($url) . '" />' . "\n";
            echo '<id>' . htmlspecialchars($url) . '</id>' . "\n";
            echo '<updated>' . gmdate(DATE_ATOM, strtotime($a['updated_at'])) . '</updated>' . "\n";
            echo '<published>' . gmdate(DATE_ATOM, strtotime($a['published_at'] ?: $a['created_at'])) . '</published>' . "\n";
            echo '<summary><![CDATA[' . ($a['excerpt'] ?: HtmlSanitizer::excerpt($a['content'], 280)) . ']]></summary>' . "\n";
            echo '<content type="html"><![CDATA[' . $a['content'] . ']]></content>' . "\n";
            echo '</entry>' . "\n";
        }
        echo '</feed>';
    }

    public function sitemap(): void
    {
        header('Content-Type: application/xml; charset=utf-8');
        $articles = $this->model->latestPublished(5000, null);
        echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:xhtml="http://www.w3.org/1999/xhtml">' . "\n";
        // list page
        echo '<url><loc>' . htmlspecialchars(APP_URL . '/articles') . '</loc><changefreq>daily</changefreq><priority>0.9</priority></url>' . "\n";
        foreach ($articles as $a) {
            $url = APP_URL . '/articles/' . $a['slug'];
            echo '<url>' . "\n";
            echo '<loc>' . htmlspecialchars($url) . '</loc>' . "\n";
            echo '<lastmod>' . date('Y-m-d', strtotime($a['updated_at'])) . '</lastmod>' . "\n";
            echo '<changefreq>weekly</changefreq><priority>0.8</priority>' . "\n";
            $img = $this->imageOgUrl($a);
            if ($img) {
                echo '<image:image><image:loc>' . htmlspecialchars($img) . '</image:loc>';
                if (!empty($a['featured_image_alt'])) echo '<image:title>' . htmlspecialchars($a['featured_image_alt']) . '</image:title>';
                echo '</image:image>' . "\n";
            }
            echo '</url>' . "\n";
        }

        // Category index pages
        try {
            $categories = (new ArticleCategory())->allWithLastMod();
            foreach ($categories as $c) {
                if (empty($c['slug'])) continue;
                echo '<url>' . "\n";
                echo '<loc>' . htmlspecialchars(APP_URL . '/articles/category/' . $c['slug']) . '</loc>' . "\n";
                if (!empty($c['last_mod'])) {
                    echo '<lastmod>' . date('Y-m-d', strtotime($c['last_mod'])) . '</lastmod>' . "\n";
                }
                echo '<changefreq>weekly</changefreq><priority>0.6</priority>' . "\n";
                echo '</url>' . "\n";
            }
        } catch (Throwable $e) {
            // Skip categories on error
        }

        // Tag index pages
        try {
            $tags = (new ArticleTag())->allWithLastMod(1000);
            foreach ($tags as $t) {
                if (empty($t['slug'])) continue;
                echo '<url>' . "\n";
                echo '<loc>' . htmlspecialchars(APP_URL . '/articles/tag/' . $t['slug']) . '</loc>' . "\n";
                if (!empty($t['last_mod'])) {
                    echo '<lastmod>' . date('Y-m-d', strtotime($t['last_mod'])) . '</lastmod>' . "\n";
                }
                echo '<changefreq>weekly</changefreq><priority>0.5</priority>' . "\n";
                echo '</url>' . "\n";
            }
        } catch (Throwable $e) {
            // Skip tags on error
        }

        echo '</urlset>';
    }

    // ==================== ADMIN ====================

    public function adminIndex(): void
    {
        $this->requireAdmin();
        $q = trim((string)($_GET['q'] ?? ''));
        $articles = $this->model->listAllForAdmin($q !== '' ? $q : null);
        $pageTitle = Lang::get('admin_articles') . ' | ' . Lang::get('app_name');
        $fullWidth = false;
        require APP_ROOT . '/views/admin/articles/index.php';
    }

    public function adminCreate(): void
    {
        $this->requireAdmin();
        $article = null;
        $categories = $this->catModel->all();
        $tags = [];
        $pageTitle = Lang::get('article_new') . ' | ' . Lang::get('app_name');
        require APP_ROOT . '/views/admin/articles/form.php';
    }

    public function adminEdit(string $id): void
    {
        $this->requireAdmin();
        $article = $this->model->findById((int)$id);
        if (!$article) {
            header('Location: ' . APP_URL . '/admin/articles');
            exit;
        }
        
        // Debug: Log the article data retrieved from database
        Debug::log('Article data from database: ' . json_encode($article));
        
        $tags = $this->tagModel->forArticle((int)$article['id']);
        $categories = $this->catModel->all();
        $pageTitle = Lang::get('article_edit') . ' | ' . Lang::get('app_name');
        require APP_ROOT . '/views/admin/articles/form.php';
    }

    public function adminStore(): void
    {
        $this->requireAdmin();
        if (!Session::verifyCsrfToken($_POST['csrf_token'] ?? '')) {
            Session::flash('error', 'Invalid request.');
            header('Location: ' . APP_URL . '/admin/articles/create');
            exit;
        }
        $data = $this->parseFormData();
        if (!empty($data['__error'])) {
            Session::flash('error', $data['__error']);
            header('Location: ' . APP_URL . '/admin/articles/create');
            exit;
        }
        $data['slug'] = $this->model->uniqueSlug($data['title']);
        $data['user_id'] = Session::getUserId();

        // Featured image upload
        if (!empty($_FILES['featured_image']['name']) && $_FILES['featured_image']['error'] === UPLOAD_ERR_OK) {
            $res = $this->handleFeaturedImage($_FILES['featured_image'], $data['slug']);
            if ($res['success']) {
                $data['featured_image'] = $res['rel_dir'] . '|' . $res['basename'];
            } else {
                Session::flash('error', 'Image upload failed: ' . $res['error']);
            }
        }

        $id = $this->model->create($data);
        $this->tagModel->syncForArticle($id, $data['__tags']);
        ArticleCache::bumpVersion();
        Session::flash('success', Lang::get('article_saved'));
        header('Location: ' . APP_URL . '/admin/articles/' . $id . '/edit');
        exit;
    }

    public function adminUpdate(string $id): void
    {
        // Debug: Log that adminUpdate method is being called
        Debug::log('adminUpdate method called for article ID: ' . $id);
        
        $this->requireAdmin();
        $existing = $this->model->findById((int)$id);
        if (!$existing) {
            Debug::log('Article not found with ID: ' . $id);
            header('Location: ' . APP_URL . '/admin/articles');
            exit;
        }
        
        // Debug: Log POST data
        Debug::log('POST data received: ' . json_encode($_POST));
        
        if (!Session::verifyCsrfToken($_POST['csrf_token'] ?? '')) {
            Session::flash('error', 'Invalid request.');
            header('Location: ' . APP_URL . '/admin/articles/' . $id . '/edit');
            exit;
        }
        $data = $this->parseFormData();
        if (!empty($data['__error'])) {
            Session::flash('error', $data['__error']);
            header('Location: ' . APP_URL . '/admin/articles/' . $id . '/edit');
            exit;
        }

        // Slug: recompute only if the admin changed the title/slug explicitly
        $desiredSlug = trim((string)($_POST['slug'] ?? ''));
        if ($desiredSlug === '') {
            $desiredSlug = $existing['slug'];
        } else {
            $desiredSlug = Slug::make($desiredSlug);
        }
        if ($desiredSlug !== $existing['slug']) {
            $desiredSlug = $this->model->uniqueSlug($desiredSlug, (int)$id);
        }
        $data['slug'] = $desiredSlug;

        // Featured image
        $data['featured_image'] = $existing['featured_image'];
        if (!empty($_POST['remove_featured']) && $existing['featured_image']) {
            [$relDir, $basename] = array_pad(explode('|', $existing['featured_image'], 2), 2, '');
            if ($relDir && $basename) ArticleImagePipeline::deleteAll($relDir, $basename);
            $data['featured_image'] = null;
        }
        if (!empty($_FILES['featured_image']['name']) && $_FILES['featured_image']['error'] === UPLOAD_ERR_OK) {
            $res = $this->handleFeaturedImage($_FILES['featured_image'], $data['slug']);
            if ($res['success']) {
                if ($existing['featured_image']) {
                    [$relDirOld, $basenameOld] = array_pad(explode('|', $existing['featured_image'], 2), 2, '');
                    if ($relDirOld && $basenameOld) ArticleImagePipeline::deleteAll($relDirOld, $basenameOld);
                }
                $data['featured_image'] = $res['rel_dir'] . '|' . $res['basename'];
            } else {
                Session::flash('error', 'Image upload failed: ' . $res['error']);
            }
        }

        // Debug: Log the data being saved
        Debug::log('Data being saved to database: ' . json_encode($data));
        
        $this->model->update((int)$id, $data, $existing['slug']);
        $this->tagModel->syncForArticle((int)$id, $data['__tags']);
        ArticleCache::bumpVersion();
        Session::flash('success', Lang::get('article_saved'));
        header('Location: ' . APP_URL . '/admin/articles/' . $id . '/edit');
        exit;
    }

    public function adminDelete(string $id): void
    {
        $this->requireAdmin();
        $existing = $this->model->findById((int)$id);
        if ($existing) {
            if (!empty($existing['featured_image'])) {
                [$relDir, $basename] = array_pad(explode('|', $existing['featured_image'], 2), 2, '');
                if ($relDir && $basename) ArticleImagePipeline::deleteAll($relDir, $basename);
            }
            $this->model->delete((int)$id);
            ArticleCache::bumpVersion();
            Session::flash('success', Lang::get('article_deleted'));
        }
        header('Location: ' . APP_URL . '/admin/articles');
        exit;
    }

    public function uploadImage(): void
    {
        $this->requireAdmin();
        header('Content-Type: application/json');
        if (!Session::verifyCsrfToken($_POST['csrf_token'] ?? ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''))) {
            http_response_code(403);
            echo json_encode(['error' => 'csrf']);
            return;
        }
        if (empty($_FILES['image']['name']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
            http_response_code(400);
            echo json_encode(['error' => 'no file']);
            return;
        }
        $upload = FileUpload::upload($_FILES['image'], APP_ROOT . '/public/images/articles/inline/' . date('Y/m'));
        if (!$upload['success']) {
            http_response_code(400);
            echo json_encode(['error' => $upload['error']]);
            return;
        }
        $rel = str_replace(APP_ROOT . '/public', '', $upload['filepath']);
        echo json_encode(['url' => APP_URL . $rel]);
    }

    // ==================== HELPERS ====================

    private function parseFormData(): array
    {
        $title = trim((string)($_POST['title'] ?? ''));
        if ($title === '') return ['__error' => Lang::get('title_required')];

        $content = (string)($_POST['content'] ?? '');
        // Debug: Log raw content
        Debug::log('Raw content from POST: ' . $content);
        
        $contentClean = HtmlSanitizer::clean($content);
        // Debug: Log cleaned content
        Debug::log('Cleaned content: ' . $contentClean);
        
        if (trim(strip_tags($contentClean)) === '') return ['__error' => Lang::get('content_required')];

        $excerpt = trim((string)($_POST['excerpt'] ?? ''));
        if ($excerpt === '') {
            $excerpt = HtmlSanitizer::excerpt($contentClean, 200);
        }

        $locale = trim((string)($_POST['locale'] ?? 'en')) ?: 'en';
        $categoryId = (int)($_POST['category_id'] ?? 0);
        $status = in_array(($_POST['status'] ?? 'draft'), ['draft', 'published'], true) ? $_POST['status'] : 'draft';
        $publishedAt = trim((string)($_POST['published_at'] ?? ''));
        if ($status === 'published') {
            $publishedAt = $publishedAt !== '' ? date('Y-m-d H:i:s', strtotime($publishedAt)) : date('Y-m-d H:i:s');
        } else {
            $publishedAt = $publishedAt !== '' ? date('Y-m-d H:i:s', strtotime($publishedAt)) : null;
        }

        $wordCount = HtmlSanitizer::wordCount($contentClean);
        $reading = max(1, (int)ceil($wordCount / 220));

        $tagsRaw = (string)($_POST['tags'] ?? '');
        $tags = array_values(array_filter(array_map('trim', explode(',', $tagsRaw))));

        return [
            'title' => $title,
            'excerpt' => $excerpt,
            'content' => $contentClean,
            'locale' => $locale,
            'category_id' => $categoryId ?: null,
            'status' => $status,
            'published_at' => $publishedAt,
            'word_count' => $wordCount,
            'reading_minutes' => $reading,
            'featured_image_alt' => trim((string)($_POST['featured_image_alt'] ?? '')) ?: null,
            'meta_title' => trim((string)($_POST['meta_title'] ?? '')) ?: null,
            'meta_description' => trim((string)($_POST['meta_description'] ?? '')) ?: null,
            '__tags' => $tags,
        ];
    }

    private function handleFeaturedImage(array $file, string $slugHint): array
    {
        // Accept via FileUpload (validates extension/mime), then pipeline
        $tempDir = APP_ROOT . '/public/images/articles/_tmp';
        $res = FileUpload::upload($file, $tempDir);
        if (!$res['success']) return $res;
        $out = ArticleImagePipeline::process($res['filepath'], $slugHint);
        // cleanup temp original
        @unlink($res['filepath']);
        return $out;
    }

    private function imageOgUrl(array $article): string
    {
        if (empty($article['featured_image'])) return '';
        [$relDir, $basename] = array_pad(explode('|', $article['featured_image'], 2), 2, '');
        if (!$relDir || !$basename) return '';
        return ArticleImagePipeline::ogUrl($relDir, $basename);
    }

    private function requireAdmin(): void
    {
        if (!Session::isLoggedIn()) {
            header('Location: ' . APP_URL . '/login');
            exit;
        }
        $userModel = new User();
        $user = $userModel->findById(Session::getUserId());
        if (!$user || ($user['role'] ?? '') !== 'admin') {
            header('Location: ' . APP_URL . '/home');
            exit;
        }
    }

    private function notFound(): void
    {
        http_response_code(404);
        $pageTitle = '404 | ' . Lang::get('app_name');
        echo '<!doctype html><meta charset="utf-8"><title>404</title><h1>404 — Not Found</h1>';
    }

    private function isBot(): bool
    {
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
        return (bool)preg_match('#bot|crawl|spider|slurp|facebookexternalhit|whatsapp|telegram#i', $ua);
    }
}
