<?php

/**
 * API Router - Main entry point for all API requests
 *
 * Routes:
 * - /api/categories
 * - /api/notifications
 * - /api/upload
 * - /api/history
 */

declare(strict_types=1);

// Error handling
error_reporting(E_ALL);
ini_set('display_errors', '0');

// Load dependencies
require_once __DIR__ . '/../src/database.php';
require_once __DIR__ . '/../src/auth.php';
require_once __DIR__ . '/../src/services/recurrence-calculator.php';
require_once __DIR__ . '/../src/services/category-service.php';
require_once __DIR__ . '/../src/services/notification-service.php';
require_once __DIR__ . '/../src/services/job-service.php';
require_once __DIR__ . '/../src/services/telegram-service.php';

use Notifier\Database;
use Notifier\Auth;
use Notifier\Services\CategoryService;
use Notifier\Services\NotificationService;
use Notifier\Services\JobService;
use Notifier\Services\TelegramService;

// Load config
$configPath = __DIR__ . '/../config.php';
if (!file_exists($configPath)) {
    jsonResponse(['success' => false, 'error' => 'Configuration not found'], 500);
}
$config = require $configPath;

// CORS headers (adjust for production)
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, X-API-Token');

// Handle preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// JSON response helper
function jsonResponse(array $data, int $status = 200): void
{
    http_response_code($status);
    header('Content-Type: application/json');
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

// Get JSON body
function getJsonBody(): array
{
    $body = file_get_contents('php://input');
    if (empty($body)) {
        return [];
    }
    $data = json_decode($body, true);
    return is_array($data) ? $data : [];
}

// Resolve relative upload paths against upload directory
function resolveUploadPath(string $baseDir, ?string $relativePath): ?string
{
    if (!$relativePath) {
        return null;
    }

    $baseReal = realpath($baseDir);
    if ($baseReal === false) {
        return null;
    }

    $relativePath = ltrim($relativePath, '/');
    if (strpos($relativePath, '..') !== false) {
        return null;
    }

    return $baseReal . '/' . $relativePath;
}

function requireUploadedFile(array $config, string $relativePath, string $label): void
{
    $uploadsDir = $config['upload_path'] ?? __DIR__ . '/../uploads';
    $resolvedPath = resolveUploadPath($uploadsDir, $relativePath);
    if (!$resolvedPath || !is_file($resolvedPath)) {
        jsonResponse(['success' => false, 'error' => $label . ' not found on disk'], 400);
    }
    clearstatcache(true, $resolvedPath);
    $size = filesize($resolvedPath);
    if ($size === false || $size === 0) {
        jsonResponse(['success' => false, 'error' => $label . ' is empty or unreadable'], 400);
    }
}

function validateNotificationImages(array $config, array $data): void
{
    if (array_key_exists('image_path', $data)) {
        $imagePath = $data['image_path'];
        if ($imagePath !== null && $imagePath !== '') {
            if (!is_string($imagePath)) {
                jsonResponse(['success' => false, 'error' => 'image_path must be a string'], 400);
            }
            $imagePath = trim($imagePath);
            if ($imagePath !== '') {
                requireUploadedFile($config, $imagePath, 'image_path');
            }
        }
    }

    if (!empty($data['subsequents']) && is_array($data['subsequents'])) {
        foreach ($data['subsequents'] as $i => $sub) {
            if (!is_array($sub) || !array_key_exists('image_path', $sub)) {
                continue;
            }
            $subImagePath = $sub['image_path'];
            if ($subImagePath === null || $subImagePath === '') {
                continue;
            }
            if (!is_string($subImagePath)) {
                jsonResponse(['success' => false, 'error' => 'subsequents[' . ($i + 1) . '].image_path must be a string'], 400);
            }
            $subImagePath = trim($subImagePath);
            if ($subImagePath !== '') {
                requireUploadedFile($config, $subImagePath, 'subsequents[' . ($i + 1) . '].image_path');
            }
        }
    }
}

// Initialize auth
$auth = new Auth($config);

// Require authentication for all API routes
$auth->requireAuth(true);

// Initialize database
try {
    $db = new Database($config['database']['path'] ?? __DIR__ . '/../database.sqlite');
    $pdo = $db->getPdo();
} catch (Exception $e) {
    jsonResponse(['success' => false, 'error' => 'Database connection failed'], 500);
}

// Parse request
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
$path = parse_url($uri, PHP_URL_PATH);

// Remove base path prefix (handles /api, /notifier/api, etc.)
$path = preg_replace('#^.*/api#', '', $path);
$path = '/' . trim($path, '/');

// Route matching
$segments = explode('/', trim($path, '/'));
$resource = $segments[0] ?? '';
$id = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null;
$action = $segments[2] ?? null;

// Initialize services
$categoryService = new CategoryService($pdo);
$notificationService = new NotificationService($pdo, $config['display_timezone'] ?? 'America/Mexico_City');
$jobService = new JobService($pdo);

// Route to appropriate handler
switch ($resource) {
    case 'categories':
        handleCategories($method, $id, $categoryService);
        break;

    case 'notifications':
        handleNotifications($method, $id, $action, $notificationService, $config);
        break;

    case 'upload':
        handleUpload($config);
        break;

    case 'history':
        handleHistory($pdo, $id);
        break;

    case 'preview':
        handlePreview($notificationService);
        break;

    case 'jobs':
        handleJobs($method, $jobService, $pdo, $id, $action);
        break;

    case 'test-message':
        handleTestMessage($config);
        break;

    default:
        jsonResponse(['success' => false, 'error' => 'Not found'], 404);
}

// ==================== CATEGORY HANDLERS ====================

function handleCategories(string $method, ?int $id, CategoryService $service): void
{
    switch ($method) {
        case 'GET':
            if ($id) {
                $category = $service->getById($id);
                if (!$category) {
                    jsonResponse(['success' => false, 'error' => 'Category not found'], 404);
                }
                jsonResponse(['success' => true, 'data' => $category]);
            }
            $categories = $service->getAll();
            $counts = $service->getNotificationCounts();
            foreach ($categories as &$cat) {
                $cat['notification_count'] = $counts[$cat['id']] ?? 0;
            }
            jsonResponse(['success' => true, 'data' => $categories]);
            break;

        case 'POST':
            $data = getJsonBody();
            $result = $service->create(
                $data['name'] ?? '',
                $data['color'] ?? '#6366f1',
                $data['icon'] ?? null
            );
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            jsonResponse(['success' => true, 'id' => $result['id']], 201);
            break;

        case 'PUT':
            if (!$id) {
                jsonResponse(['success' => false, 'error' => 'ID required'], 400);
            }
            $data = getJsonBody();
            $result = $service->update(
                $id,
                $data['name'] ?? '',
                $data['color'] ?? '#6366f1',
                $data['icon'] ?? null
            );
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            jsonResponse(['success' => true]);
            break;

        case 'DELETE':
            if (!$id) {
                jsonResponse(['success' => false, 'error' => 'ID required'], 400);
            }
            $result = $service->delete($id);
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            jsonResponse(['success' => true]);
            break;

        default:
            jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }
}

// ==================== NOTIFICATION HANDLERS ====================

function handleNotifications(string $method, ?int $id, ?string $action, NotificationService $service, array $config): void
{
    // Handle actions first
    if ($id && $action) {
        switch ($action) {
            case 'pause':
                if ($method !== 'POST') {
                    jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
                }
                $result = $service->pause($id);
                if (!$result['success']) {
                    jsonResponse(['success' => false, 'error' => $result['error']], 400);
                }
                jsonResponse(['success' => true]);
                break;

            case 'resume':
                if ($method !== 'POST') {
                    jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
                }
                $result = $service->resume($id);
                if (!$result['success']) {
                    jsonResponse(['success' => false, 'error' => $result['error']], 400);
                }
                jsonResponse(['success' => true]);
                break;

            case 'preview':
                if ($method !== 'GET') {
                    jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
                }
                $result = $service->getPreview($id, 4);
                if (!$result['success']) {
                    jsonResponse(['success' => false, 'error' => $result['error']], 400);
                }
                jsonResponse([
                    'success' => true,
                    'dates' => $result['dates'],
                    'dates_display' => $result['dates_display'],
                ]);
                break;

            case 'test':
                if ($method !== 'POST') {
                    jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
                }
                handleTestSend($id, $service, $config);
                break;

            default:
                jsonResponse(['success' => false, 'error' => 'Unknown action'], 404);
        }
        return;
    }

    // Standard CRUD
    switch ($method) {
        case 'GET':
            if ($id) {
                $notification = $service->getById($id);
                if (!$notification) {
                    jsonResponse(['success' => false, 'error' => 'Notification not found'], 404);
                }
                jsonResponse(['success' => true, 'data' => $notification]);
            }
            $filters = [
                'category_id' => $_GET['category_id'] ?? null,
                'status' => $_GET['status'] ?? null,
                'search' => $_GET['search'] ?? null,
            ];
            $notifications = $service->getAll(array_filter($filters));
            jsonResponse(['success' => true, 'data' => $notifications]);
            break;

        case 'POST':
            $data = getJsonBody();
            validateNotificationImages($config, $data);
            $result = $service->create($data);
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            jsonResponse(['success' => true, 'id' => $result['id']], 201);
            break;

        case 'PUT':
            if (!$id) {
                jsonResponse(['success' => false, 'error' => 'ID required'], 400);
            }
            $data = getJsonBody();
            validateNotificationImages($config, $data);
            $result = $service->update($id, $data);
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            jsonResponse(['success' => true]);
            break;

        case 'DELETE':
            if (!$id) {
                jsonResponse(['success' => false, 'error' => 'ID required'], 400);
            }
            $result = $service->delete($id);
            if (!$result['success']) {
                jsonResponse(['success' => false, 'error' => $result['error']], 400);
            }
            // Delete image file if exists
            if ($result['image_path']) {
                $uploadsDir = $config['upload_path'] ?? __DIR__ . '/../uploads';
                $fullPath = realpath($uploadsDir . '/' . $result['image_path']);
                if ($fullPath && str_starts_with($fullPath, realpath($uploadsDir))) {
                    @unlink($fullPath);
                }
            }
            jsonResponse(['success' => true]);
            break;

        default:
            jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }
}

function handleTestSend(int $id, NotificationService $service, array $config): void
{
    $notification = $service->getById($id);
    if (!$notification) {
        jsonResponse(['success' => false, 'error' => 'Notification not found'], 404);
    }

    // Use test chat config (fallback to production if values are missing)
    $testConfig = $config['telegram_test'] ?? $config['telegram'];
    if (empty($testConfig['bot_token'])) {
        $testConfig['bot_token'] = $config['telegram']['bot_token'] ?? null;
    }
    if (empty($testConfig['chat_id'])) {
        $testConfig['chat_id'] = $config['telegram']['chat_id'] ?? null;
    }

    $telegram = TelegramService::fromConfig($testConfig);

    $message = $notification['message'];
    $formattedMessage = TelegramService::formatMarkdownV2($message);
    $uploadsDir = $config['upload_path'] ?? __DIR__ . '/../uploads';
    $imagePath = resolveUploadPath($uploadsDir, $notification['image_path'] ?? null);

    if ($imagePath && file_exists($imagePath)) {
        $result = $telegram->sendPhoto($imagePath, $formattedMessage, 'MarkdownV2');
    } else {
        $result = $telegram->sendMessage($formattedMessage, 'MarkdownV2');
    }

    if (!$result['success']) {
        jsonResponse(['success' => false, 'error' => $result['error']], 500);
    }

    jsonResponse([
        'success' => true,
        'message_id' => $result['message_id'],
        'chat_id' => $telegram->getChatId(),
    ]);
}

// ==================== UPLOAD HANDLER ====================

function handleUpload(array $config): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }

    if (!isset($_FILES['image'])) {
        jsonResponse(['success' => false, 'error' => 'No file uploaded'], 400);
    }

    $file = $_FILES['image'];

    if ($file['error'] !== UPLOAD_ERR_OK) {
        $errors = [
            UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
            UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE',
            UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
            UPLOAD_ERR_NO_FILE => 'No file was uploaded',
        ];
        jsonResponse(['success' => false, 'error' => $errors[$file['error']] ?? 'Upload error'], 400);
    }

    // Check size
    $maxSize = $config['upload_max_size'] ?? (5 * 1024 * 1024);
    if ($file['size'] > $maxSize) {
        jsonResponse(['success' => false, 'error' => 'File too large. Max: ' . ($maxSize / 1024 / 1024) . 'MB'], 400);
    }

    // Check type
    $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mimeType = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);

    if (!in_array($mimeType, $allowedTypes)) {
        jsonResponse(['success' => false, 'error' => 'Invalid file type. Allowed: JPEG, PNG, GIF, WebP'], 400);
    }

    // Generate path
    $ext = match($mimeType) {
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/gif' => 'gif',
        'image/webp' => 'webp',
        default => 'jpg',
    };

    $uploadsDir = $config['upload_path'] ?? __DIR__ . '/../uploads';
    $yearMonth = date('Y/m');
    $targetDir = $uploadsDir . '/' . $yearMonth;

    if (!is_dir($targetDir)) {
        mkdir($targetDir, 0755, true);
    }

    $filename = bin2hex(random_bytes(16)) . '.' . $ext;
    $targetPath = $targetDir . '/' . $filename;
    $relativePath = $yearMonth . '/' . $filename;

    if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
        jsonResponse(['success' => false, 'error' => 'Failed to save file'], 500);
    }

    clearstatcache(true, $targetPath);
    if (!is_file($targetPath) || filesize($targetPath) === 0) {
        @unlink($targetPath);
        jsonResponse(['success' => false, 'error' => 'File save verification failed'], 500);
    }

    jsonResponse([
        'success' => true,
        'path' => $relativePath,
    ], 201);
}

// ==================== HISTORY HANDLER ====================

function handleHistory(PDO $pdo, ?int $notificationId): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
        jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }

    $sql = '
        SELECT h.*, n.title as notification_title
        FROM notification_history h
        JOIN notifications n ON h.notification_id = n.id
        WHERE 1=1
    ';
    $params = [];

    if ($notificationId) {
        $sql .= ' AND h.notification_id = ?';
        $params[] = $notificationId;
    }

    if (!empty($_GET['notification_id'])) {
        $sql .= ' AND h.notification_id = ?';
        $params[] = (int)$_GET['notification_id'];
    }

    $sql .= ' ORDER BY h.sent_at DESC';

    // Pagination
    $page = max(1, (int)($_GET['page'] ?? 1));
    $limit = min(100, max(1, (int)($_GET['limit'] ?? 50)));
    $offset = ($page - 1) * $limit;

    $sql .= " LIMIT {$limit} OFFSET {$offset}";

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    $history = $stmt->fetchAll(PDO::FETCH_ASSOC);

    jsonResponse([
        'success' => true,
        'data' => $history,
        'page' => $page,
        'limit' => $limit,
    ]);
}

// ==================== PREVIEW HANDLER (without saved notification) ====================

function handlePreview(NotificationService $service): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }

    $data = getJsonBody();
    $result = $service->calculatePreview($data, 4);

    if (!$result['success']) {
        jsonResponse(['success' => false, 'error' => $result['error']], 400);
    }

    jsonResponse([
        'success' => true,
        'dates' => $result['dates'],
        'dates_display' => $result['dates_display'],
    ]);
}

// ==================== TEST MESSAGE HANDLER ====================

function handleTestMessage(array $config): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }

    $data = getJsonBody();
    $message = $data['message'] ?? '';
    $imagePath = $data['image_path'] ?? null;

    if (empty($message)) {
        jsonResponse(['success' => false, 'error' => 'Message is required'], 400);
    }

    $uploadsDir = $config['upload_path'] ?? __DIR__ . '/../uploads';
    $resolvedImagePath = null;
    if ($imagePath !== null && $imagePath !== '') {
        if (!is_string($imagePath)) {
            jsonResponse(['success' => false, 'error' => 'image_path must be a string'], 400);
        }
        $imagePath = trim($imagePath);
        if ($imagePath !== '') {
            requireUploadedFile($config, $imagePath, 'image_path');
            $resolvedImagePath = resolveUploadPath($uploadsDir, $imagePath);
        }
    }

    // Use test chat config (fallback to production if values are missing)
    $testConfig = $config['telegram_test'] ?? $config['telegram'];
    if (empty($testConfig['bot_token'])) {
        $testConfig['bot_token'] = $config['telegram']['bot_token'] ?? null;
    }
    if (empty($testConfig['chat_id'])) {
        $testConfig['chat_id'] = $config['telegram']['chat_id'] ?? null;
    }

    $telegram = TelegramService::fromConfig($testConfig);

    // Format message for MarkdownV2 (escapes special chars while preserving formatting)
    $formattedMessage = TelegramService::formatMarkdownV2($message);

    if ($resolvedImagePath) {
        $result = $telegram->sendPhoto($resolvedImagePath, $formattedMessage, 'MarkdownV2');
    } else {
        $result = $telegram->sendMessage($formattedMessage, 'MarkdownV2');
    }

    if (!$result['success']) {
        jsonResponse(['success' => false, 'error' => $result['error']], 500);
    }

    jsonResponse([
        'success' => true,
        'message_id' => $result['message_id'],
    ]);
}

// ==================== JOBS HANDLER (for debugging/admin) ====================

function handleJobs(string $method, JobService $service, PDO $pdo, ?int $id, ?string $action): void
{
    if ($method === 'POST') {
        if (!$id || $action !== 'retry') {
            jsonResponse(['success' => false, 'error' => 'Invalid jobs action'], 400);
        }

        $result = $service->retryNow($id);
        if (!$result['success']) {
            $status = $result['error'] === 'Job not found' ? 404 : 400;
            jsonResponse(['success' => false, 'error' => $result['error']], $status);
        }

        jsonResponse(['success' => true]);
    }

    if ($method !== 'GET') {
        jsonResponse(['success' => false, 'error' => 'Method not allowed'], 405);
    }

    // Check for status filter
    $statusFilter = $_GET['status'] ?? null;

    if ($statusFilter) {
        // Return jobs filtered by status
        $statuses = array_map('trim', explode(',', $statusFilter));
        $validStatuses = ['pending', 'sending', 'sent', 'retrying', 'cancelled', 'failed'];
        $statuses = array_filter($statuses, fn($s) => in_array($s, $validStatuses));

        if (empty($statuses)) {
            jsonResponse(['success' => false, 'error' => 'Invalid status filter'], 400);
        }

        $placeholders = implode(',', array_fill(0, count($statuses), '?'));
        $sql = "
            SELECT j.*, n.title as notification_title
            FROM send_jobs j
            JOIN notifications n ON j.notification_id = n.id
            WHERE j.status IN ({$placeholders})
            ORDER BY j.next_attempt_at DESC
            LIMIT 100
        ";

        $stmt = $pdo->prepare($sql);
        $stmt->execute($statuses);
        $jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);

        jsonResponse([
            'success' => true,
            'data' => $jobs,
            'count' => count($jobs),
        ]);
    }

    // No filter - return stats
    $stats = $service->getStats();
    jsonResponse([
        'success' => true,
        'stats' => $stats,
    ]);
}
