<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;

/**
 * NewsletterController — Frontend newsletter subscription
 *
 * Handles: subscribe (AJAX + form), unsubscribe
 * Uses direct SQL as primary method for maximum reliability.
 */
class NewsletterController extends Controller
{
    /**
     * Ensure the newsletter_subscribers table exists with all required columns.
     * Uses CREATE TABLE IF NOT EXISTS for the base table, then ALTER TABLE
     * to add any missing columns (for tables created by older migrations).
     */
    private function ensureTableExists(): void
    {
        try {
            $this->db->exec("
                CREATE TABLE IF NOT EXISTS `newsletter_subscribers` (
                    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
                    `email` VARCHAR(255) NOT NULL,
                    `name` VARCHAR(150) DEFAULT NULL,
                    `status` ENUM('active','unsubscribed','bounced') NOT NULL DEFAULT 'active',
                    `verification_token` VARCHAR(64) DEFAULT NULL,
                    `is_verified` TINYINT(1) NOT NULL DEFAULT 1,
                    `source` VARCHAR(50) DEFAULT 'homepage',
                    `subscribed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    `unsubscribed_at` TIMESTAMP NULL DEFAULT NULL,
                    `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                    PRIMARY KEY (`id`),
                    UNIQUE KEY `uk_newsletter_email` (`email`),
                    INDEX `idx_newsletter_status` (`status`, `subscribed_at`)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
            ");
        } catch (\Throwable $e) {
            error_log('Newsletter: create table failed: ' . $e->getMessage());
        }

        // Add student_id column if missing (older tables won't have it)
        $this->addColumnIfMissing('newsletter_subscribers', 'student_id', 'INT UNSIGNED DEFAULT NULL');
        $this->addColumnIfMissing('newsletter_subscribers', 'student_id_idx', null); // just triggers index add

        // Add index on student_id if column exists but index doesn't
        try {
            // Check if index already exists
            $idxCheck = $this->db->query("SHOW INDEX FROM `newsletter_subscribers` WHERE Key_name = 'idx_student_id'");
            if ($idxCheck && $idxCheck->fetch() === false) {
                // Check if student_id column exists before adding index
                $colCheck = $this->db->query("SHOW COLUMNS FROM `newsletter_subscribers` LIKE 'student_id'");
                if ($colCheck && $colCheck->fetch() !== false) {
                    $this->db->exec("ALTER TABLE `newsletter_subscribers` ADD INDEX `idx_student_id` (`student_id`)");
                }
            }
        } catch (\Throwable $e) {
            // Index may already exist or column missing — ignore
        }
    }

    /**
     * Add a column to a table if it doesn't already exist.
     */
    private function addColumnIfMissing(string $table, string $column, ?string $definition): void
    {
        try {
            $check = $this->db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
            if ($check && $check->fetch() === false && $definition !== null) {
                $this->db->exec("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}");
            }
        } catch (\Throwable $e) {
            // Table might not exist yet or column already added — ignore
        }
    }

    /**
     * Check if a column exists in a table.
     */
    private function columnExists(string $table, string $column): bool
    {
        try {
            $check = $this->db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
            return $check && $check->fetch() !== false;
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * Subscribe — handles both AJAX and regular form POST.
     */
    public function subscribe(): void
    {
        // Validate CSRF
        $csrfToken = $_POST['csrf_token'] ?? '';
        if (!verifyCsrfToken($csrfToken)) {
            if ($this->isAjax()) {
                $this->jsonResponse(['success' => false, 'message' => 'Invalid request. Please refresh the page and try again.'], 403);
                return;
            }
            flash('error', 'Invalid request. Please refresh the page.');
            redirect(BASE_PATH . '/');
            return;
        }

        $email = trim($_POST['email'] ?? '');
        $name  = trim($_POST['name'] ?? '') ?: null;

        // Validate email
        if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            if ($this->isAjax()) {
                $this->jsonResponse(['success' => false, 'message' => 'Please enter a valid email address.'], 422);
                return;
            }
            flash('error', 'Please enter a valid email address.');
            redirect(BASE_PATH . '/');
            return;
        }

        // Check if newsletter is enabled
        $newsletterEnabled = $this->getSetting('newsletter_enabled', '1');
        if ($newsletterEnabled !== '1') {
            if ($this->isAjax()) {
                $this->jsonResponse(['success' => false, 'message' => 'Newsletter subscription is currently disabled.'], 403);
                return;
            }
            flash('error', 'Newsletter subscription is currently disabled.');
            redirect(BASE_PATH . '/');
            return;
        }

        // Ensure table exists with all columns before inserting
        $this->ensureTableExists();

        // Check if a logged-in student is subscribing
        $studentId = null;
        $hasStudentIdCol = $this->columnExists('newsletter_subscribers', 'student_id');
        try {
            $currentUser = getCurrentUser();
            if ($currentUser && isset($currentUser['id'])) {
                $studentStmt = $this->db->prepare('SELECT `id` FROM `students` WHERE `user_id` = :uid LIMIT 1');
                $studentStmt->execute([':uid' => (int) $currentUser['id']]);
                $studentRow = $studentStmt->fetch(\PDO::FETCH_ASSOC);
                if ($studentRow) {
                    $studentId = (int) $studentRow['id'];
                }
            }
        } catch (\Throwable $e) {
            // Not logged in or students table doesn't have user_id — proceed without student_id
        }

        // Subscribe using direct SQL (most reliable)
        try {
            $email = strtolower($email);

            // Check if email already exists — only select columns that exist
            $checkCols = '`id`, `status`';
            if ($hasStudentIdCol) {
                $checkCols .= ', `student_id`';
            }
            $checkStmt = $this->db->prepare("SELECT {$checkCols} FROM `newsletter_subscribers` WHERE `email` = :email LIMIT 1");
            $checkStmt->execute([':email' => $email]);
            $existing = $checkStmt->fetch(\PDO::FETCH_ASSOC);

            if ($existing) {
                if ($existing['status'] === 'active') {
                    // Already active subscriber — update student_id if column exists and value available
                    if ($hasStudentIdCol && $studentId && empty($existing['student_id'])) {
                        $updateStmt = $this->db->prepare('UPDATE `newsletter_subscribers` SET `student_id` = :sid WHERE `id` = :id');
                        $updateStmt->execute([':sid' => $studentId, ':id' => $existing['id']]);
                    }
                    $message = 'This email is already subscribed to our newsletter.';
                    if ($this->isAjax()) {
                        $this->jsonResponse(['success' => true, 'message' => $message]);
                        return;
                    }
                    flash('success', $message);
                    redirect(BASE_PATH . '/');
                    return;
                }

                // Re-subscribe: was unsubscribed/bounced, now active again
                $token = bin2hex(random_bytes(32));
                if ($hasStudentIdCol) {
                    $reSubStmt = $this->db->prepare(
                        "UPDATE `newsletter_subscribers`
                         SET `status` = 'active', `unsubscribed_at` = NULL,
                             `name` = :name, `source` = 'homepage',
                             `verification_token` = :token, `is_verified` = 1,
                             `student_id` = COALESCE(:student_id, `student_id`)
                         WHERE `id` = :id"
                    );
                    $reSubStmt->execute([
                        ':name'       => $name,
                        ':token'      => $token,
                        ':student_id' => $studentId,
                        ':id'         => $existing['id'],
                    ]);
                } else {
                    $reSubStmt = $this->db->prepare(
                        "UPDATE `newsletter_subscribers`
                         SET `status` = 'active', `unsubscribed_at` = NULL,
                             `name` = :name, `source` = 'homepage',
                             `verification_token` = :token, `is_verified` = 1
                         WHERE `id` = :id"
                    );
                    $reSubStmt->execute([
                        ':name'  => $name,
                        ':token' => $token,
                        ':id'    => $existing['id'],
                    ]);
                }
                $message = 'Welcome back! You have been re-subscribed successfully.';
            } else {
                // New subscriber
                $token = bin2hex(random_bytes(32));
                if ($hasStudentIdCol) {
                    $insertStmt = $this->db->prepare(
                        "INSERT INTO `newsletter_subscribers` (`email`, `name`, `status`, `verification_token`, `is_verified`, `source`, `student_id`)
                         VALUES (:email, :name, 'active', :token, 1, 'homepage', :student_id)"
                    );
                    $insertStmt->execute([
                        ':email'      => $email,
                        ':name'       => $name,
                        ':token'      => $token,
                        ':student_id' => $studentId,
                    ]);
                } else {
                    $insertStmt = $this->db->prepare(
                        "INSERT INTO `newsletter_subscribers` (`email`, `name`, `status`, `verification_token`, `is_verified`, `source`)
                         VALUES (:email, :name, 'active', :token, 1, 'homepage')"
                    );
                    $insertStmt->execute([
                        ':email' => $email,
                        ':name'  => $name,
                        ':token' => $token,
                    ]);
                }

                $successMessage = $this->getSetting('newsletter_success_message', 'Thank you for subscribing! You will receive updates from Gmind Institute.');
                $message = $successMessage;
            }

            if ($this->isAjax()) {
                $this->jsonResponse(['success' => true, 'message' => $message]);
                return;
            }

            flash('success', $message);
            redirect(BASE_PATH . '/');
        } catch (\Throwable $e) {
            error_log('Newsletter subscribe error: ' . $e->getMessage());

            if ($this->isAjax()) {
                $this->jsonResponse(['success' => false, 'message' => 'Something went wrong. Please try again later.'], 500);
                return;
            }

            flash('error', 'Something went wrong. Please try again later.');
            redirect(BASE_PATH . '/');
        }
    }

    /**
     * Unsubscribe — via email link with token.
     */
    public function unsubscribe(): void
    {
        $token = $_GET['token'] ?? '';

        if (empty($token)) {
            flash('error', 'Invalid unsubscribe link.');
            redirect(BASE_PATH . '/');
            return;
        }

        try {
            $field = filter_var($token, FILTER_VALIDATE_EMAIL) ? 'email' : 'verification_token';
            $stmt = $this->db->prepare(
                "UPDATE `newsletter_subscribers` SET `status` = 'unsubscribed', `unsubscribed_at` = NOW() WHERE `{$field}` = :value AND `status` = 'active'"
            );
            $stmt->execute([':value' => $token]);

            if ($stmt->rowCount() > 0) {
                flash('success', 'You have been unsubscribed successfully. We are sorry to see you go!');
            } else {
                flash('error', 'Invalid or expired unsubscribe link.');
            }
        } catch (\Throwable $e) {
            error_log('Newsletter unsubscribe error: ' . $e->getMessage());
            flash('error', 'Something went wrong. Please try again.');
        }

        redirect(BASE_PATH . '/');
    }

    /**
     * Check if the request is AJAX.
     */
    private function isAjax(): bool
    {
        return !empty($_SERVER['HTTP_X_REQUESTED_WITH'])
            && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
    }

    /**
     * Send JSON response.
     */
    private function jsonResponse(array $data, int $code = 200): void
    {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
        exit;
    }

    /**
     * Get a setting value from the database.
     * Checks both setting_key/setting_value and key_name/value columns.
     */
    private function getSetting(string $key, string $default = ''): string
    {
        try {
            $stmt = $this->db->prepare('SELECT `setting_value` FROM `settings` WHERE `setting_key` = :k LIMIT 1');
            $stmt->execute([':k' => $key]);
            $val = $stmt->fetchColumn();
            if ($val !== false && $val !== '') {
                return $val;
            }

            $stmt2 = $this->db->prepare('SELECT `value` FROM `settings` WHERE `key_name` = :k LIMIT 1');
            $stmt2->execute([':k' => $key]);
            $val2 = $stmt2->fetchColumn();
            if ($val2 !== false && $val2 !== '') {
                return $val2;
            }
        } catch (\Throwable $e) {}

        return $default;
    }
}
